FFI可以处理数组吗?如果是这样,怎么样?
我很确定可以通过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