2012-03-24 128 views
21

我很確定可以通過FFI發送數組,但是我找不到任何示例。例如,我有一個Haskell數組,我發送到一個int foo(int*)函數,或者我有一個C數組int bar[64];,我發送給Haskell。FFI可以處理數組嗎?如果是這樣,怎麼樣?

理想情況下,我想要最有效的方式 - 我不想要任何堆分配或不必要的複製。另外,如果我可以在Haskell和C中使用Haskell的unboxed數組,那將會很好。那麼這樣做的方法是什麼?

+0

參見['Foreign.Marshal.Array'](http://hackage.haskell.org/包/基層 - 4.7.0.0 /文檔/外編組Array.html)。 – MasterMastic 2014-06-02 09:27:03

回答

18

如果您使用Data.Vector庫,則可以根據需要使用Data.Vector.Storable。然後你可以使用諸如unsafeToForeignPtr或unsafeWith之類的函數來訪問底層的外部指針。這允許您在不進行任何複製或編組的情況下調用C代碼。

如果你想從C數組創建一個向量,你可以使用unsafeFromForeignPtr。

爲了您的例子,你可以使用(假設c_foo不會修改它的參數)

import Foreign.Ptr 
import Foreign.C.Types 
import System.IO.Unsafe (unsafePerformIO) 
import qualified Data.Vector.Storable as SV 

foreign import ccall unsafe "foo" c_foo :: Ptr CInt -> CInt 

haskellFoo :: SV.Vector CInt -> CInt 
haskellFoo sv = unsafePerformIO $ 
    SV.unsafeWith sv $ \ptr -> return (c_foo ptr) 

這可以被golfed到:

haskellFoo sv = unsafePerformIO $ 
    SV.unsafeWith sv (return . c_foo) 

需要注意的是,如果你的C函數修改數據,那麼你不應該這樣做,而是你應該 製作一份數據的副本,以不破壞參考透明度。

如果要使用標準陣列類型,可以使用Data.Array.Storable中的withStorableArray

+0

你的高爾夫版本沒有使用'sv'參數,我猜你只是不小心省略了它(我試圖編輯你的答案,但是因此堅持我的編輯≥6個字符:() – 2012-03-29 10:18:44

+1

@benmachine:修正了對你:) – ehird 2012-03-29 13:45:34

+0

@ehird,benmachine:謝謝編輯。 – dnaq 2012-03-30 05:51:29

10

FFI規範非常易讀,所以你可能只想坐下來完成整個事情。但是,對於此特定問題,您可以跳轉到「編組」部分,特別是PtrStorable小節,其中概述了可用於此的部分。

3

和C一樣,數組基本上是一個指向數組第一個成員的指針。您通過對指針進行算術得到其他元素。 PtrNum的成員,因此您可以使用通常的算術運算。

+5

正確,但Haskell指針運算以*字節*爲單位。所以在C語言中,你會寫'aPtr + = 1'移動到數組的下一個元素,在Haskell中你需要寫'next = aPtr + 1 * sizeOf元素'。或者你可以使用'Foreign.Marshal.Array.advancePtr'。 – 2012-03-25 10:09:23

+0

是的。這當然是事實。 – fuz 2012-03-25 11:32:43

+0

當我導入'Foreign'或'Foreign.Ptr'時,我沒有看到'Num'作爲'Ptr'的實例,你從哪裏得到? – 2012-04-06 00:27:13

相關問題