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.Storable
的withStorableArray
。
【讨论】:
你打高尔夫球的版本没有使用sv
参数,我猜你只是不小心忽略了它(我试图编辑你的答案,但坚持我的编辑是 ≥ 6 个字符 :( )
@benmachine: 为你解决了这个问题:)【参考方案3】:
在 C 中,数组基本上是指向数组第一个成员的指针。您可以通过对指针进行算术运算来获得其他元素。 Ptr
是 Num
的成员,因此您可以使用通常的算术运算。
【讨论】:
是的,但 Haskell 指针运算以 字节 工作。因此,在 C 语言中,您可以编写aPtr += 1
以移动到数组的下一个元素,而在 Haskell 中,您需要编写 next = aPtr + 1*sizeOf element
。或者你可以使用Foreign.Marshal.Array.advancePtr
。
当我导入Foreign
或Foreign.Ptr
时,我没有看到Ptr
的Num
实例,你从哪里得到的?【参考方案4】:
FFI 规范非常易读,因此您可能只想坐下来完成整个过程。但是,对于这个特定问题,您可以跳转到“编组”部分,尤其是 Ptr 和 Storable 小节,其中概述了可用的内容。
【讨论】:
以上是关于FFI 可以处理数组吗?如果是这样,怎么做?的主要内容,如果未能解决你的问题,请参考以下文章
我们可以在 Spark DataFrame 列中使用 Pandas 函数吗?如果是这样,怎么做?
十六进制格式可以与 JSON 文件一起使用吗?如果是这样,怎么做?