我正在为一个c库做一个简单的包装器,它需要有一个传递给它的向量列表。它需要一组指向数组的指针。为了建立一个漂亮的界面,我想要Vector的Vector(或列表),但我无法真正找到如何在idiomatic haskell中做到这一点。 (或者除了记忆周围的任何其他方式)。
我正在寻找的是像
Vector (Vector Foo) -> (Ptr (Ptr Foo) -> IO a) -> IO a
答案 0 :(得分:4)
由于您要转到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
答案 1 :(得分:2)
编辑:hCsound没有处理这个确切的情况,所以我在下面添加了一个完整的例子。
您可能希望查看我的包hCsound(darcs repo),该包必须处理非常类似的情况。
请注意,C库不会修改 Data.Vector.Storable.Vector
使用的数组非常重要。如果确实需要修改数据,则应首先复制旧数据,通过ffi修改数组,最后将指针包装到新的Vector中。
这是代码。正如评论中指出的那样,Data.Vector.Storable.Vector
本身没有可存储的实例,因此您需要外部向量为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。
这些数组不是以null结尾的,因此您需要确保通过其他方法将长度传递给C函数。
withForeignPtr
未使用。相反,调用touchForeignPtr
以确保在C函数完成之前不释放ForeignPtr。为了使用withForeignPtr
,您需要为每个内部向量嵌套调用。这就是hCsound代码中nest
函数的作用。它比调用touchForeignPtr
要复杂得多。