【问题标题】:Haskell Data.Vector.Storable.unsafeFromForeignPtr with C struct pointer/array fieldHaskell Data.Vector.Storable.unsafeFromForeignPtr 与 C 结构指针/数组字段
【发布时间】:2014-06-07 12:36:18
【问题描述】:

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

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 库已在堆上分配和填充的double* 数组创建Haskell Vectors 的方法。我希望通过这样做,我可以避免复制 double* 数组的内容,而只需将 Vector 作为数组的一种包装器。 (附带的问题是:鉴于double* 数组最多可以有10,000 个doubles,是否值得追求这种非复制?)

这是我目前所拥有的。我正在使用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)

因为没有ForeignPtr DoubleStorable 实例而被卡住。我想如果我尝试为ForeignPtr Double 实现一个实例,我只会将如何访问struct 成员的地址值的问题转换为该实例的peek 实现。

总之,我怎样才能访问地址值(即指针)struct 成员,以便我可以将其用作unsafeFromForeignPtr 的参数?

【问题讨论】:

    标签: arrays pointers haskell vector ffi


    【解决方案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  
    

    【讨论】:

    • 看起来newForeignPtr_ 是这里的重要部分。我无法手动计算 plusPtr 右手参数来工作,但是使用 newForeignPtr_hsc2hs#peek 生成的指针可以工作。
    • 我已经使用这个建议编写了一个答案,但我不能发布它,因为我太 n00b 并且互联网不信任我:-/ 希望我仍然会被打扰发布它...
    • @rjlewis 如果需要,请在底部使用您的解决方案编辑我的答案。我应该能够接受编辑。
    【解决方案2】:

    使用 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' }
    

    我发现只是 #peeking int 字段给了我垃圾,而明确声明它是 CInt 然后使用 fromIntegral 将其转换为 Int 给了我正确的值。然后我实际上有一个正确的长度可以与unsafeFromForeignPtr 一起使用。然后我对double* 指针也做了类似的事情:明确声明它为Ptr Double

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-09
      • 1970-01-01
      • 2014-11-03
      • 1970-01-01
      • 2018-04-21
      • 2013-04-18
      • 1970-01-01
      相关资源
      最近更新 更多