我使用Haskell和一个C库,它定义了许多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 Vector的方法。我希望通过这样做,我可以避免复制double*数组的内容,而只是将Vector作为数组的包装器。(附带的问题是:考虑到double*数组可以达到double的10,000次,是否值得进行这种非复制?)
这就是我到目前为止所拥有的。我使用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中,我试图将values成员作为一个ForeignPtr Double来使用,然后我可以将它与unsafeFromForeignPtr一起使用。但是,#peek生成如下代码:
valuesField <- (((\ hsc_ptr -> peekByteOff hsc_ptr 16)) ptr) :: IO (ForeignPtr Double)并且因为没有Storable实例ForeignPtr Double而陷入困境。我想,如果我试图为ForeignPtr Double实现一个实例,我就会把如何访问struct成员的地址值的问题转换为该实例的peek实现。
因此,总之,如何访问地址值(即指针) struct成员,以便我可以将其用作unsafeFromForeignPtr的参数。
发布于 2014-06-07 13:59:20
我不知道如何使用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 发布于 2014-06-07 17:52:49
根据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将其转换为Int会给出正确的值。然后,我实际上有一个正确的长度用于unsafeFromForeignPtr。然后,我也对double*指针做了一件类似的事情:显式地声明它为Ptr Double。
在我的实际应用程序中,有些struct也有char*类型字段,类似于int字段,我发现它也只是作为垃圾出现在#peek之后。同样,显式声明它们的类型为CString也解决了这个问题。
https://stackoverflow.com/questions/24097259
复制相似问题