关于Haskell:FFI可以处理数组吗?如果是这样,怎么办?

Can the FFI deal with arrays? If so, how?

我很确定可以通过FFI发送数组,但是我找不到任何示例。例如,我有一个发送给int foo(int*)函数的Haskell数组,或者我有一个发送给Haskell的C数组int bar[64];

理想情况下,我想要最有效的方法-我不需要任何堆分配或不必要的复制。另外,如果我可以在Haskell和C中都使用Haskell的未装箱的数组,那将是很好的。那么这样做的方法是什么?


如果您使用Data.Vector库,则可以根据需要使用Data.Vector.Storable。然后,您可以使用unsafeToForeignPtr或unsafeWith之类的函数来访问基础外部指针。这使您可以调用C代码,而不会进行任何复制或封送处理。

如果要从C数组创建向量,可以使用unsafeFromForeignPtr。

您可以使用示例(假设c_foo不会修改其参数)

1
2
3
4
5
6
7
8
9
10
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)

可以打到:

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

请注意,如果您的C函数修改了数据,则不应该这样做,而应该
制作数据副本以不破坏参照透明性。

如果要使用标准的Array-type,可以以相同的方式使用Data.Array.Storable中的withStorableArray


FFI规范具有很高的可读性,因此您可能需要坐下来研究整个内容。但是,对于这个特定问题,您可以跳至"编组"部分,尤其是Ptr和Storable子部分,概述了可用于此目的的内容。


要将FFI Ptr转换为Haskell列表,可以使用:

1
peekArray0 :: (Storable a, Eq a) => a -> Ptr a -> IO [a]

http://hackage.haskell.org/packages/archive/base/4.2.0.1/doc/html/Foreign-Marshal-Array.html#v:peekArray0


与C语言一样,数组基本上是指向该数组的第一个成员的指针。您可以通过对指针进行算术来获取其他元素。 PtrNum的成员,因此您可以使用常规的算术运算。