【问题标题】:Passing list of different typed elements to a C function将不同类型元素的列表传递给 C 函数
【发布时间】:2013-10-16 00:14:43
【问题描述】:

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

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

它需要一个大小和一个指针,然后把整个东西放在内存中的某个地方。它旨在与混合类型一起使用。你可以放 n 个浮点数,然后是 m 个 bools 等等(在 C 中)。

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

withArrayLen magicArray foo

【问题讨论】:

  • 读取外部函数接口以及如何为数据类型编写可存储实例。

标签: haskell tuples ffi


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

【讨论】:

    猜你喜欢
    • 2018-01-02
    • 1970-01-01
    • 2020-02-06
    • 1970-01-01
    • 2016-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多