【问题标题】:Haskell FFi with c2hs: Better out-marshalling of structs带有 c2hs 的 Haskell FFi:更好的结构外编组
【发布时间】:2018-12-14 10:21:28
【问题描述】:

假设你有一个 C API 提供了一个 C 结构

typedef struct A {
    int i;
    float f;
} A;

以及填充它的函数:

void getA(A* a);

例如,这可能是从 C API 内部获取某些信息的 getter。

在 Haskell 中,C 结构将被镜像

data A = A {
    i :: Int,
    f :: Float
}

Storable 实例是

instance Storable A where
    sizeOf    _ = {#sizeof  A #}
    alignment _ = {#alignof A #}
    peek p = ...
    poke p x = ...

使用 c2hs 处理的 {#get...#}{#set #} 编译指示,Peek 和 poke 照常进行。

Haskell 函数getA :: IO A 应该类似于

{#fun unsafe getA as getA {alloca- `A' peek*} -> `()'#}

除了这不起作用,因为 c2hs 创建了这个绑定:

foreign import ccall unsafe "include/A.h getA"
    _getA'_ :: Ptr () -> IO ()

它有Ptr () 作为第一个参数。这可以通过

{#fun unsafe getA as getA {allocaA- `A' peekA*} -> `()'#}

peekA :: Ptr () -> IO A
peekA = peek . castPtr

allocaA :: (Ptr () -> IO a) -> IO a
allocaA f = alloca $ \(p :: Ptr A) -> f (castPtr p)

allocaA 很重要,因为它确保为A 分配内存,而不是为() 分配内存(如果使用了alloca)。

虽然这可行,但它有点乏味,而且如果你忘记写allocaXYZ 而不是只写alloca,你肯定会出现段错误。 (我刚刚看到花了很多时间来追踪一个这样的错误。)

我希望能找到一个 {#fun...#} 产生的咒语

foreign import ccall unsafe "include/A.h getA"
    _getA'_ :: Ptr A -> IO ()

所有其他内容都会自然而然地遵循(注意Ptr A 而不是Ptr ())。但据我所知,只有{allocXYZ- 'XYZ' peekXYZ*} 路线。

所以问题是:可以使用c2hs 以更好的方式完成吗?

【问题讨论】:

    标签: c haskell struct ffi c2hs


    【解决方案1】:

    你在使用{#pointer ...#} 钩子吗?由于您没有将 A 视为不透明的指针(即 if 可以正常访问,并且 Storable 实例是显式编写的),因此您需要使用箭头形式:

    {#pointer *A as APtr -> A#}
    

    此时,您仍然必须使用alloca/peek 函数来封送A,但是您的第一个理想的{#fun ...#} 钩子应该按照所写的方式工作。 (您最终会忽略 APtr 类型;它会显示在生成的代码中,但在 *.chs 文件中不是必需的。)

    还请注意,您需要将该指针定义添加到使用A 的每个文件中;即使你从一个主文件中导出APtr,你仍然需要在任何你使用它的地方添加{#pointer *A as APtr -> A nocode#}

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-16
      • 1970-01-01
      相关资源
      最近更新 更多