0

我有一个用 C 语言编写的函数,我想从 Haskell 程序中调用。函数类型为:

foo :: Int -> Ptr a -> IO ()

它需要一个大小和一个指针,并将整个事物放在内存中的某个位置。它旨在与混合类型一起使用。您可以放置​​ n 个浮点数,然后放置 m 个布尔值,依此类推(在 C 中)。

在 Haskell 中表示这种情况的最方便的方法是——在我看来——([a],[b])例如。但是,我需要整个东西都适合Ptr a(它实际上是 C 中的 void*)。我可以尝试编写一个类似的函数([a],[b]) -> Ptr c,但我需要一些帮助。所需的最终功能是:

withArrayLen magicArray foo
4

1 回答 1

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

于 2013-10-15T13:25:04.230 回答