FFI能处理数组吗?如果可以,如何处理?

22

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

最理想的方式是最高效的方式 - 我不希望有任何堆分配或不必要的复制。此外,如果我可以在 Haskell 和 C 中都使用未打包的数组将会很好。那么怎么做呢?


请参阅Foreign.Marshal.Array - MasterMastic
4个回答

17
如果您使用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)

这可以被压缩为:
haskellFoo sv = unsafePerformIO $
    SV.unsafeWith sv (return . c_foo)

请注意,如果您的C函数修改数据,则不应该这样做,而是应该复制数据以避免破坏引用透明性。
如果您想使用标准的Array类型,可以以相同的方式使用来自Data.Array.Storable的withStorableArray。

你的压缩版本没有使用 sv 参数,我猜想你只是不小心省略了它(我试图编辑你的回答,但 SO 坚持我的编辑必须≥ 6 个字符 :( ) - Ben Millwood
1
@benmachine:已经为您修复了 :) - ehird

11

FFI规范相当易读,因此您可能只需坐下来全文阅读即可。但是,对于这个特定的问题,您可以跳转到“打包(Marshalling)”部分,特别是PtrStorable子部分,其中概述了可用的内容。


当我尝试访问那些链接时,我会收到“403 Forbidden”错误。 - mherzl

4

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

6
没错,但 Haskell 指针算术是按 字节 进行的。因此,在 C 中,您会写 aPtr += 1 来移动到数组的下一个元素,在 Haskell 中,您需要写 next = aPtr + 1*sizeOf element。或者您可以使用 Foreign.Marshal.Array.advancePtr - John L
当我导入 ForeignForeign.Ptr 时,我没有看到 Ptr 的实例 Num ,你是从哪里获取的? - Ben Millwood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接