首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有C结构指针/数组字段的Haskell Data.Vector.Storable.unsafeFromForeignPtr

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

Stack Overflow用户
提问于 2014-06-07 12:36:18
回答 2查看 564关注 0票数 4

我使用Haskell和一个C库,它定义了许多struct类型,这些成员是指向doubles的指针,用于作为doubles的数组对待:

代码语言:javascript
复制
typedef struct Foo {
  int length;
  double* values;
} FooT;

在我对这个库的Haskell绑定中,我有一个等效的数据类型,在该类型中,我试图对数组使用Data.Vector.Storable.Vector Double

代码语言:javascript
复制
data Foo = Foo {
  length :: Int,
  values :: Data.Vector.Storable.Vector Double
} deriving (Show, Eq)

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

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

代码语言:javascript
复制
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中,我试图将values成员作为一个ForeignPtr Double来使用,然后我可以将它与unsafeFromForeignPtr一起使用。但是,#peek生成如下代码:

代码语言:javascript
复制
valuesField <- (((\ hsc_ptr -> peekByteOff hsc_ptr 16)) ptr) :: IO (ForeignPtr Double)

并且因为没有Storable实例ForeignPtr Double而陷入困境。我想,如果我试图为ForeignPtr Double实现一个实例,我就会把如何访问struct成员的地址值的问题转换为该实例的peek实现。

因此,总之,如何访问地址值(即指针) struct成员,以便我可以将其用作unsafeFromForeignPtr的参数。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-06-07 13:59:20

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

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2014-06-07 17:52:49

根据user2407038的建议,newForeignPtr_的工作解决方案是:

代码语言:javascript
复制
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' }

我发现,仅仅是#peekint字段中就会给我带来垃圾,而显式地声明它为CInt,然后使用fromIntegral将其转换为Int会给出正确的值。然后,我实际上有一个正确的长度用于unsafeFromForeignPtr。然后,我也对double*指针做了一件类似的事情:显式地声明它为Ptr Double

在我的实际应用程序中,有些struct也有char*类型字段,类似于int字段,我发现它也只是作为垃圾出现在#peek之后。同样,显式声明它们的类型为CString也解决了这个问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24097259

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档