使用Haskell FFI调用由指向该函数的C指针引用的C函数

时间:2013-03-04 14:29:00

标签: haskell ffi

“C”wiringPi库中有一个类型为

的函数
extern void (*pinMode) (int pin, int mode) ;

我尝试使用带有FunPtr的FFI从haskell调用它。所以我做到了,

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 ())

但由于某种原因,即使它编译,也似乎没有调用'pinMode'指向的函数。

所以我尝试使用普通的Foreign.Ptr,认为我可以窥探Ptr以获得对'pinMode'指向的底层'C'函数的引用。所以我试过了,

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

然后,在调用'pinMode'的haskell函数的实现中,我使用peek两次来获取对底层函数的引用。但是我一直在编译错误,编译器告诉我类型(CInt -> CInt -> IO ())的函数不是'Storable'类型类的实例。

所以我检查了可存储的类型类,使(CInt -> CInt -> IO ())成为可存储的类型类的实例。所需的最小实现是窥视,戳和其他一些函数..我意识到,它真的不应该如此难以调用由指针引用的函数..

我觉得我错过了一些基本的东西。有人可以指出我正确的方向吗?

谢谢和问候

1 个答案:

答案 0 :(得分:2)

假设我们在foo.c中定义了一个C函数指针。

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

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

为了调用fooptr指向的函数,我们不仅需要声明静态地址导入,还需要动态导入。动态存根可以帮助我们将FunPtr值转换为相应的Haskell函数。

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是导入的地址,指向外部函数。它的类型既不是Ptr (Ptr a)也不是FunPtr a

如果我们导入foo的地址,其类型将为FunPtr Foo。为了使用它,我们仍然需要mkFooFun的帮助。

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

在此示例中,由于我们可以访问foo,因此调用foo的最简单方法是

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