我正在使用带有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}}的参数?
答案 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
也解决了。