guaraqe
guaraqe

Reputation: 249

Haskell - FFI and Pointers

I'm using the FFI in order to use a function in C that takes a struct and returns the same struct. The references I saw say I have to use pointers to these structures in order to be able to import it into Haskell. So, for example.

data Bar = Bar { a :: Int, b :: Int }
type BarPtr = Ptr (Bar)

foreign import ccall "static foo.h foo"
    f_foo :: BarPtr -> BarPtr

Now I have the problem that I have to be able to use the function. The references I saw had functions of type BarPtr -> IO () and used with, which has signature Storable a => a -> (Ptr a -> IO b) -> IO b, which was ok, because they where calling the function inside main.

However, I would like to wrap this function in a library, getting a function of type Bar -> Bar without IO, is it possible to do without unsafePerformIO? What's the procedure?

Upvotes: 5

Views: 1005

Answers (1)

John L
John L

Reputation: 28097

It's not possible to remove IO from the type without using unsafePerformIO. However, it is possible to get a function with the type you want in this case, with some caveats. Specifically the C function "foo" cannot depend upon any global variables, thread-local state, or anything besides the single argument. Also, calling foo(bar) should always provide the same result when bar is unchanged.

I expect that trying to import the C function

bar foo(bar input);

with this call

f_foo :: BarPtr -> BarPtr

will result in a compiler error due to the result type. I think you may need to write a wrapper function (in C):

void wrap_foo(bar *barPtr) {
    bar outp = foo(*barPtr);
    *barPtr = outp;
}

and import it as

f_wrap_foo :: BarPtr -> IO ()

Finally, you would call this imported function with:

fooBar :: Bar -> Bar
fooBar bar = unsafePerformIO $ with bar $ \barPtr -> do
    f_wrap_foo barPtr
    peek barPtr

Upvotes: 7

Related Questions