我正在为需要将向量列表传递给它的 ac 库制作一个简单的包装器。它需要一个指向数组的指针数组。为了制作一个漂亮的界面,我想要 Vectors 的 Vector(或列表),但我真的不知道如何在惯用的 haskell 中做到这一点。(或者除了记忆复制东西之外的任何其他方式)。
我正在寻找的是类似的东西
Vector (Vector Foo) -> (Ptr (Ptr Foo) -> IO a) -> IO a
由于您要传递给 C 函数,因此您应该使用Data.Vector.Storable
. 您不能只传递向量的向量,Storable
因为它不仅仅是指向数组的指针的向量,它还包括大小和偏移量信息。
如果 C 函数的参数是,int myCFunc(foo** arrays, int sz)
那么下面的代码应该可以工作:
import Data.Vector.Storable
import Foreign.Storable
import Foreign.ForeignPtr
withCFunction :: Storable a => -- ^ Storable so compatible with C
Vector (Vector a) -- ^ vector of vectors
-> (Ptr (Ptr a) -> IO b) -- ^ C function wrapped by FFI
-> IO b
withCFunction v f = do
vs <- mapVectorM (\x -> let (fp,_,_) = unsafeToForeignPtr x
in unsafeForeignPtrToPtr fp) v
mapVectorM_ (\x -> let (tfp,_,_) = unsafeToForeignPtr x
in touchForeignPtr tfp) vs
let (vfp,_,_) = unsafeToForeignPtr vs
withForeignPtr vfp $ \p -> f p
编辑: hCsound 不处理这种确切的情况,所以我在下面添加了一个完整的例子。
你可能想看看我的包hCsound ( darcs repo ),它必须处理一个非常相似的案例。
请注意,C 库不修改. Data.Vector.Storable.Vector
如果确实需要修改数据,则应先复制旧数据,通过ffi修改数组,最后将指针包装成新的Vector。
这是代码。正如评论中指出的那样,Data.Vector.Storable.Vector
它本身没有 Storable 实例,因此您需要外部向量为Data.Vector.Vector
.
import Foreign.Storable
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Marshal.Array
import qualified Data.Vector as V
import qualified Data.Vector.Storable as S
import Data.Vector.Storable.Internal
withPtrArray v f = do
let vs = V.map S.unsafeToForeignPtr v -- (ForeignPtr, Offset, Length)
ptrV = V.toList $ V.map (\(fp,off,_) -> offsetToPtr fp off) vs
res <- withArray ptrV f
V.mapM_ (\(fp,_,_) -> touchForeignPtr fp) vs
return res
请注意,数组是由 分配的withArray
,因此在函数返回后它会自动 gc'd。
这些数组不是以空值结尾的,因此您需要确保通过其他方法将长度传递给 C 函数。
withForeignPtr
不使用。相反,touchForeignPtr
调用它以确保在 C 函数完成之前不会释放 ForeignPtr。为了使用withForeignPtr
,您需要为每个内部向量嵌套调用。这就是nest
hCsound 代码中的函数的作用。它比调用更复杂touchForeignPtr
。