可以存储在内存中的东西是类型类的实例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 函数无法区分接收到的数据块中的项目边界,但如果需要,包含长度前缀或类型代码并不难。