I have a question about the Haskell C FFI, specifically about accessing static data structures exported by a C library.
The C library I’m wrapping has static data structures like FOO_GEORGE
below, exported in the following fashion:
static struct foo_struct foo_table[] = { /* list of foo structs */ }
typedef struct foo_struct *foo_t;
foo_t FOO_GEORGE = &foo_table[0];
foo_t FOO_HARRY = &foo_table[1];
foo_t FOO_SUSAN = &foo_table[2];
/* ... */
The value I need in my Haskell library is the address of the foo_struct
(&foo_table[n]
), that is, the contents of FOO_GEORGE
, which is put in an opaque newtype wrapper in the usual way (the constructors are not exported from the library, only the type):
newtype Foo = Foo { getFoo :: (Ptr Foo) }
This is what I’m doing now:
foreign import ccall "&FOO_GEORGE" fooGeorgeHandle :: Ptr (Ptr Foo)
FooGeorge = Foo . unsafeDupablePerformIO . peek $ fooGeorgeHandle
I think this is an appropriate use of unsafePerformIO
, since the C API and implementation say that this use of peek
is pure and has no side effects. Also, I believe that I don’t need to take any of the precautions outlined in bullet points in the documentation (starting with {-# NOINLINE foo #-}
).
My overall question is: am I doing this right? Are the bits of analysis above correct? Is there a better or preferable way to do this? If the foreign import
clause allowed me to do the pointer defererence, that would be nice, but it doesn’t seem to; am I missing anything? One could argue that this would be a bad feature since it could segfault if the pointer is bad — but then, the same is true of the peek
I have to use instead, so it comes to the same thing.
Thanks!
John L. suggested the CApiFFI
extension, which does exactly what I want: allows you to import values rather than locations. Now:
{-# LANGUAGE CApiFFI #-}
newtype {-# CTYPE "foo.h" "struct foo_struct" #-} Foo = Foo { getFoo :: (Ptr Foo) }
foreign import capi "foo.h value FOO_GEORGE" fooGeorgePtr :: Ptr a
fooGeorge = Foo fooGeorgePtr
Another advantage is that this works regardless of whether FOO_GEORGE
is a C variable or a preprocessor macro. The C API I’m working with uses both, and different implementations of the same API that I link to do so differently, so being independent of that is great.
However, there’s a stumbling block: CApiFFI
doesn’t work with ghci! The problem is known, and it isn’t slated to be fixed until GHC 8.0.1 (when I first wrote this, it was due for 7.10, and then was pushed forward). I have a very hacky workaround. CApiFFI
works by generating a C library on the fly, compiling and linking the Haskell program against it. It removes the library when done; the ghci problem seems to be that the .so file is gone by the time ghci needs to link against it. I just grab the .c file during compilation before it gets deleted, then compile it myself and tell ghci to load it. Since I don’t change that portion of the program very often, it works out for me well enough.
My method for catching the temporary .c file is to start ghci in Emacs compilation-mode
, with (setq compilation-auto-jump-to-first-error t)
. Emacs sees the error and loads the .c file into a buffer before GHC gets around to deleting it — by the time I see it the file is gone, but I’ve got the contents in a buffer.
Update: ghci -fobject-code Foo
works, but can only see the names exported from the module.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With