我有一个用C编写的函数我想从Haskell程序调用。功能类型是:
foo :: Int -> Ptr a -> IO ()
它需要一个大小和一个指针,并将整个东西放在内存中。它旨在与混合类型一起使用。你可以把n个浮点数加上m bools等等(在C中)。
在Haskell中表示这种情况的最方便的方法是 - 在我看来 - 例如([a],[b])
。但是,我需要整个事情适合Ptr a
(它实际上是C中的空白*)。我可以尝试编写像([a],[b]) -> Ptr c
这样的函数,但我需要一些帮助。期望的最终功能是:
withArrayLen magicArray foo
答案 0 :(得分:1)
可以存储在内存中的东西是类Storable
类型的实例(在Foreign.Storable中)。所以,考虑到原始的FFI原型
foreign import "foo" c_foo :: CInt -> Ptr a -> IO ()
你可以为同源列表写这样的东西:
homfoo :: Storable a => [a] -> IO ()
homfoo items = withArray items $ \ptr -> c_foo (fromIntegral len) ptr
where len = length items * sizeOf (head items)
但是你已经说过这个函数适用于混合类型,所以我们需要某种类型约束的异构列表来处理好的Haskell包装器。这是一种方法:
{-# LANGUAGE GADTs #-}
data DynStorable where
MkStorable :: Storable a => a -> DynStorable
foo :: [DynStorable] -> IO ()
foo items =
let (requiredSize, offsets) = mapAccumL sizeFold 0 items in
allocaBytes requiredSize $ \ptr -> do
zipWithM
(\offset (MkStorable x) -> pokeByteOff ptr offset x)
offsets items
c_foo (fromIntegral requiredSize) ptr
where
sizeFold offset (MkStorable x) =
let unalignment = offset `mod` alignment x
offset' = if unalignment /= 0
then offset + alignment x - unalignment
else offset
in (offset' + sizeOf x, offset')
main :: IO ()
main = do
foo [MkStorable (2 :: Int), MkStorable (3.0 :: Double), MkStorable True]
C函数无法区分收到的数据块中的项边界,但如果需要,不可能包含长度前缀或类型代码。