2012-03-24 127 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

相关问题