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