2013-03-04 78 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