Extension to the FFI to allow macro expansions to be avoided
Ian Lynagh
igloo at earth.li
Wed Apr 7 21:11:40 EDT 2004
[This has come from the cvs-ghc list. All the info should be in this
mail, though].
The problem
===========
Some C libraries (curses is the one I have to hand) provide macros in
addition to functions in some cases for efficiency reasons. If you use
the FFI to import such a function thus:
-----
module Q where
import Foreign.Ptr (Ptr)
import Foreign.C.Types (CInt)
foreign import ccall unsafe "static curses.h wstandend" wstandend
:: Ptr a -> IO CInt
-----
Then GHC fails if not using the NCG (which AFAIK is available on only
handful of platforms; the generated code efficiency may also be an issue
if you don't have enough foreign imports to warrant splitting them off
into their own module):
$ ghc -fffi -c Q.hs -O
/tmp/ghc1164.hc: In function `Q_zdwccall_entry':
/tmp/ghc1164.hc:29: warning: dereferencing `void *' pointer
/tmp/ghc1164.hc:29: error: request for member `_attrs' in something not a structure or union
$
as it is creating code like this (line 29 is the one containing
"wstandend"):
{
I_ _ccall_result;
StgAddr _ccall_arg1=_B0_;
CALLER_SAVE_SYSTEM
_ccall_result = (wstandend((_ccall_arg1)));
CALLER_RESTORE_SYSTEM
_B3_=_ccall_result;
}
which then gets expanded to this by cpp:
{
I_ _ccall_result;
StgAddr _ccall_arg1=_B0_;
_ccall_result = (((((_ccall_arg1))->_attrs = (0L))));
_B3_=_ccall_result;
}
(there was some disagreement over whether such a macro was poorly
written for not casting its argument; either way, the fact remains that
this sort of thing is out there, and convincing the rest of the world to
change to make things a bit easier for us is not going to happen.).
Possible solutions
==================
1
=
One solution is to write a C wrapper around each potentially affected
function. I don't think this is very satisfactory as it significantly
increases the amount of effort needed to create a binding (rather than
just a single foreign import statement you now also need a line in a C
file and to make sure the object file is built and linked in correctly).
2
=
Surrounding the function with parentheses so the macro doesn't match.
The offending line above would be
_ccall_result = ((wstandend)((_ccall_arg1)));
3
=
I haven't tested this, but I assume you could just generate
#undef wstandend
before the function call.
When to fix the problem
=======================
(I'm assuming solution 1 is rejected from here on - if it's the one
chosen then the rest is irrelevant)
The above gives ways to fix the problem, but there were worries that it
could cause a significant performance penalty to always do this,
including in some of the bits of GHC implementation.
Two possible alternatives come to mind.
A
=
Where a cid is permitted also permit '(' cid ')'
B
=
Before the optional '&' in a C impent, allow an optional "nomacro". The
implementation would then make sure you didn't get a macro using
solution 2, 3 or some other means.
=====
Both A and B allow the programmer to highlight where they cannot trust
macros to behave as needed (by ghc -fvia-C at least), so the performance
hit is taken only when we can't be sure it is safe not to.
=====
In case it isn't clear, I don't like solution 1 (which is what we have
at the minute), but would be happy with any combination of {1,2} and
{A,B}.
Thanks
Ian
More information about the FFI
mailing list