1
votes

There is a functions in the 'C' wiringPi library with type,

extern void (*pinMode) (int pin, int mode) ;

I tried calling it from haskell using the FFI with FunPtr. So I did,

foreign import ccall unsafe "wiringPi.h &pinMode" c_pinMode 
        :: FunPtr (CInt -> CInt -> IO ())
foreign import ccall "dynamic" dc_pinMode 
        :: FunPtr (CInt -> CInt -> IO ()) -> (CInt -> CInt -> IO ())

but for some reason, even though it compiles, it doesn't seem to be calling the function that 'pinMode' points to.

So I tried using normal Foreign.Ptr, thinking I might be able to peek at the Ptr to get a reference to the underlying 'C' function pointed to by 'pinMode'. So I tried,

foreign import ccall "wiringPi.h &pinMode" c_pinMode
    :: Ptr (Ptr (CInt -> CInt -> IO ()))

And then, in the implementation of the haskell function that calls 'pinMode' I used peek twice to get a reference to the underlying function. But I keep getting compilation errors where the compiler tell me that the function of type (CInt -> CInt -> IO ()) is not an instance of the 'Storable' typeclass.

So I checked out the storable typeclass, to make (CInt -> CInt -> IO ()) an instance of the storable typeclass.. The minimum implementation required is for peek, poke and a few other functions.. I realized, it really shouldn't be so difficult to call a function that is reference by a pointer..

I feel like I am missing something fundamental. Can someone please point me in the right direction?

Thanks and Regards

1
extern void (*pinMode) (int pin, int mode); declares pinMode as a pointer to a function taking two ints and returning void. Try foreign import ccall unsafe "wiringPi.h pinMode" c_pinMode, taking the address of the pointer gives you one level of indirection too much (but I'm not too familiar with the FFI, so I'm not 100% sure I interpret it right).Daniel Fischer
Is pinMode a function or a function pointer? If it's a function pointer, I think the type of foreign import ccall unsafe "wiringPi.h &pinMode" c_pinMode should be Ptr (FunPtr (CInt -> CInt -> IO ())).nymk
@nymk pinMode is a pointer to a procedure that takes 2 integers as parameters. Say, I rewrite the type of the the import to what you mentioned. Is it still possible to get a reference to the underlying function on the haskell side? Because, if type of c_pinMode is Ptr (FunPtr (CInt -> CInt -> IO ())), I can't do c_pinMode (3 :: CInt) (4 :: CInt). I do not think this works. However I will try what you said.Jay

1 Answers

2
votes

Suppose we have a C function pointer defined in foo.c.

void foo(int x, int y)
{
    printf("foo: sum = %d\n", x+y);
}

typedef void (*FooPtr) (int, int);
FooPtr fooptr = foo;

In order to call the function that fooptr points to, we need to not only declare a static address import, but also a dynamic import. The dynamic stub can help us convert FunPtr values to corresponding Haskell functions.

type Foo = CInt -> CInt -> IO ()

foreign import ccall "foo.c &fooptr" fooptr :: Ptr (FunPtr Foo)
foreign import ccall "dynamic" mkFooFun :: FunPtr Foo -> Foo

main = do
    funcptr <- peek fooptr
    mkFooFun funcptr 1 2

fooptr is an imported address that points to a foreign function. Its type is neither Ptr (Ptr a) nor FunPtr a.

If we import foo's address, its type will be FunPtr Foo. In order to use it, we still need the help from mkFooFun.

foreign import ccall "foo.c &foo" fooptr2 :: FunPtr Foo
main = mkFooFun fooptr2 1 2

In this example, since we can access foo, the simplest way to call foo is

foreign import ccall "foo.c foo" foo :: Foo
main = foo 1 2