Vector(Vector Foo) - > (Ptr(Ptr Foo) - > IO a) - > IO a?

时间:2011-06-29 07:53:25

标签: haskell vector ffi

我正在为一个c库做一个简单的包装器,它需要有一个传递给它的向量列表。它需要一组指向数组的指针。为了建立一个漂亮的界面,我想要Vector的Vector(或列表),但我无法真正找到如何在idiomatic haskell中做到这一点。 (或者除了记忆周围的任何其他方式)。

我正在寻找的是像

Vector (Vector Foo) -> (Ptr (Ptr Foo) -> IO a) -> IO a  

2 个答案:

答案 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没有处理这个确切的情况,所以我在下面添加了一个完整的例子。

您可能希望查看我的包hCsounddarcs 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要复杂得多。