FFI 可以处理数组吗?如果是这样,怎么做?

Posted

技术标签:

【中文标题】FFI 可以处理数组吗?如果是这样,怎么做?【英文标题】:Can the FFI deal with arrays? If so, how? 【发布时间】:2012-04-08 22:23:16 【问题描述】:

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

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

【问题讨论】:

Foreign.Marshal.Array 【参考方案1】:

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

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%3ApeekArray0

【讨论】:

【参考方案2】:

如果您使用 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 函数修改了数据,那么您不应该这样做,而是应该 制作数据副本以不破坏引用透明度。

如果你想使用标准的数组类型,你可以以同样的方式使用来自Data.Array.StorablewithStorableArray

【讨论】:

你打高尔夫球的版本没有使用sv 参数,我猜你只是不小心忽略了它(我试图编辑你的答案,但坚持我的编辑是 ≥ 6 个字符 :( ) @benmachine: 为你解决了这个问题:)【参考方案3】:

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

【讨论】:

是的,但 Haskell 指针运算以 字节 工作。因此,在 C 语言中,您可以编写 aPtr += 1 以移动到数组的下一个元素,而在 Haskell 中,您需要编写 next = aPtr + 1*sizeOf element。或者你可以使用Foreign.Marshal.Array.advancePtr 当我导入ForeignForeign.Ptr 时,我没有看到PtrNum 实例,你从哪里得到的?【参考方案4】:

FFI 规范非常易读,因此您可能只想坐下来完成整个过程。但是,对于这个特定问题,您可以跳转到“编组”部分,尤其是 Ptr 和 Storable 小节,其中概述了可用的内容。

【讨论】:

以上是关于FFI 可以处理数组吗?如果是这样,怎么做?的主要内容,如果未能解决你的问题,请参考以下文章

WinForm 可以执行控制台程序吗?如果是这样,怎么做?

您可以对图形数据库进行分区吗?如果是这样,怎么做?

我们可以在 Spark DataFrame 列中使用 Pandas 函数吗?如果是这样,怎么做?

十六进制格式可以与 JSON 文件一起使用吗?如果是这样,怎么做?

Git 真的可以跟踪单个函数从一个文件到另一个文件的移动吗?如果是这样,怎么做?

在 Django 数据迁移中可以打印到 stdout 或 stderr 吗?如果是这样,怎么做?