我很確定可以通過FFI發送數組,但是我找不到任何示例。例如,我有一個Haskell數組,我發送到一個int foo(int*)
函數,或者我有一個C數組int bar[64];
,我發送給Haskell。FFI可以處理數組嗎?如果是這樣,怎麼樣?
理想情況下,我想要最有效的方式 - 我不想要任何堆分配或不必要的複製。另外,如果我可以在Haskell和C中使用Haskell的unboxed數組,那將會很好。那麼這樣做的方法是什麼?
我很確定可以通過FFI發送數組,但是我找不到任何示例。例如,我有一個Haskell數組,我發送到一個int foo(int*)
函數,或者我有一個C數組int bar[64];
,我發送給Haskell。FFI可以處理數組嗎?如果是這樣,怎麼樣?
理想情況下,我想要最有效的方式 - 我不想要任何堆分配或不必要的複製。另外,如果我可以在Haskell和C中使用Haskell的unboxed數組,那將會很好。那麼這樣做的方法是什麼?
如果您使用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
。
和C一樣,數組基本上是一個指向數組第一個成員的指針。您通過對指針進行算術得到其他元素。 Ptr
是Num
的成員,因此您可以使用通常的算術運算。
正確,但Haskell指針運算以*字節*爲單位。所以在C語言中,你會寫'aPtr + = 1'移動到數組的下一個元素,在Haskell中你需要寫'next = aPtr + 1 * sizeOf元素'。或者你可以使用'Foreign.Marshal.Array.advancePtr'。 – 2012-03-25 10:09:23
是的。這當然是事實。 – fuz 2012-03-25 11:32:43
當我導入'Foreign'或'Foreign.Ptr'時,我沒有看到'Num'作爲'Ptr'的實例,你從哪裏得到? – 2012-04-06 00:27:13
要將FFI PTR轉換成一個Haskell列表,你可以使用:
peekArray0 :: (Storable a, Eq a) => a -> Ptr a -> IO [a]
參見['Foreign.Marshal.Array'](http://hackage.haskell.org/包/基層 - 4.7.0.0 /文檔/外編組Array.html)。 – MasterMastic 2014-06-02 09:27:03