2013-03-04 43 views
1

有一个在“C” wiringPi库类型的功能,调用由C指针引用该函数与Haskell的FFI C函数

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

我试图从Haskell中使用FFI调用它with FunPtr。所以,我没有,

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”哈斯克尔功能的实现我曾经偷看两次,以获得底层函数的引用。但是,编译器告诉我,(CInt -> CInt -> IO())类型的函数不是“Storable”类型类型的实例,所以我不断收到编译错误。

所以我检查了可存储的类型类,使(CInt -> CInt -> IO())成为可存储类型类的一个实例。需要的最小实现是偷看,戳和其他一些函数。我意识到,它真的不应该那么困难调用一个指针参考的函数..

我觉得我缺少一些基本的东西。有人可以请指点我正确的方向吗?

感谢和问候

+1

'的extern无效(* pinMode)(INT引脚INT模式);''声明pinMode'作为一个_pointer_指向一个带两个“int”并返回“void”的函数。尝试'外国进口ccall不安全“wiringPi.h pinMode”c_pinMode',采取指针的地址给你一个级别的间接太多(但我不太熟悉FFI,所以我不是100%确定我解释它的权利)。 – 2013-03-04 15:06:27

+0

'pinMode'是函数还是函数指针?如果它是一个函数指针,我认为“外部导入ccall不安全”的wiringPi.h和pinMode“c_pinMode”应该是'Ptr(FunPtr(CInt - > CInt - > IO()))''。 – nymk 2013-03-04 16:25:26

+0

@nymk pinMode是一个指向以2个整数为参数的过程的指针。比如说,我将导入的类型改写为你提到的内容。是否仍有可能获得对haskell方面底层函数的引用?因为,如果c_pinMode类型是'Ptr(FunPtr(CInt - > CInt - > IO()))',我不能'c_pinMode(3 :: CInt)(4 :: CInt)'。我不认为这有效。不过,我会尝试你所说的。 – Jay 2013-03-05 02:59:50

回答

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 
+0

谢谢你提供一个非常全面的答案。在我几分钟后发现如何自己做这件事后,我真的回到这里来发布相同的解决方案。但通过你的回答,我更全面地了解了它。最重要的是,fooptr只是一个导入的内存地址。 – Jay 2013-03-05 09:46:12