带有C struct指针/数组字段的Haskell Data.Vector.Storable.unsafeFromForeignPtr

时间:2014-06-07 12:36:19

标签: arrays pointers haskell vector ffi

我正在使用带有C库的Haskell FFI,该C库定义了许多struct类型,其中包含指向double的指针的成员,旨在被视为double的数组S:

typedef struct Foo {
  int length;
  double* values;
} FooT;

在我对这个库的Haskell绑定中,我有一个等效的数据类型,我正在尝试将Data.Vector.Storable.Vector Double用于数组:

data Foo = Foo {
  length :: Int,
  values :: Data.Vector.Storable.Vector Double
} deriving (Show, Eq)

为了在C库和我的Haskell代码之间编组数据,当然,我必须为这些类型编写Storable个实例。我正在尝试使用Data.Vector.Storable.unsafeFromForeignPtr从C库分配并填充在堆上的Vector数组中创建Haskell double*的方法。我希望通过这样做,我可以避免复制double*数组的内容,只需将Vector作为数组的一种包装器。 (旁边的问题是:假设double*阵列可以达到10,000 double秒,是否值得进行这种非复制?)

这是我到目前为止所拥有的。我正在使用hsc2hs宏来帮助生成Storable peek实现:

instance Storable Foo where
  alignment _ = alignment (undefined :: CDouble)
  sizeOf _ = #{size FooT}

  peek ptr = do
    len <- (#peek FooT, length) ptr
    valuesField <- ((#peek FooT, values) ptr) :: IO (ForeignPtr Double)
    let values' = DV.unsafeFromForeignPtr0 valuesField len

    return Foo { length = len, values = values' }

  poke ptr (Foo len values') = do
    (#poke FooT, length) ptr len
    DV.unsafeWith values' (\ptrValues -> (#poke FooT, values) ptr ptrValues)

因此,在peek我正在尝试将#peek values成员ForeignPtr Double作为unsafeFromForeignPtr,然后我可以使用#peek。但是,valuesField <- (((\ hsc_ptr -> peekByteOff hsc_ptr 16)) ptr) :: IO (ForeignPtr Double) 会生成如下代码:

Storable

因为ForeignPtr Double没有ForeignPtr Double个实例而卡住了。我想如果我试图为struct实现一个实例,我只会将如何访问peek成员的地址值的问题转移到该实例的struct实现。< / p>

总而言之,如何以这样的方式访问地址值(即指针)unsafeFromForeignPtr成员,以便将其用作{{1}}的参数?

2 个答案:

答案 0 :(得分:1)

我不知道如何使用hsc2hs,但是你已经有一个指向peek中数据的指针,所以你只需要使用它,当然还有正确的偏移量。免责声明:此编译,但未经测试。

import Data.Vector.Storable (Vector, unsafeFromForeignPtr)
import Foreign.Storable (Storable (..))
import Foreign.C.Types 
import Foreign.ForeignPtr (newForeignPtr_)
import Foreign.Ptr 

instance Storable Foo where
  peek ptr = do 
    len <- peek (castPtr ptr)
    valsPtr <- newForeignPtr_  (castPtr ptr `plusPtr` (sizeOf (undefined :: CInt)))
    return $ Foo len $ unsafeFromForeignPtr valsPtr 0 len  

答案 1 :(得分:0)

使用user2407038建议的newForeignPtr_工作解决方案是:

instance Storable Foo where
  alignment _ = alignment (undefined :: CDouble)
  sizeOf _ = #{size FooT}

  peek ptr = do
    len'    <- fmap fromIntegral (((#peek FooT, len) d) :: IO CInt)

    valuesField <- ((#peek FooT, values) ptr) :: IO (Ptr Double)
    valuesPtr   <- newForeignPtr_ valuesField
    let values' = DV.unsafeFromForeignPtr0 valuesField len

    return Foo { length = len, values = values' }

我发现只有#peek int字段给了我垃圾,而明确声明它是CInt然后使用fromIntegral将其转换为{{} 1}}给了我正确的价值。然后我实际上有一个正确的长度用于Int。然后,我也使用unsafeFromForeignPtr指针做了类似的事情:明确声明它为double*

在我的实际应用中,某些Ptr Double也有struct类型字段,类似于char*字段,我发现它们也只是在{{{{}}后出现的垃圾1}}。同样,明确声明他们的类型int也解决了。