假设您有一个提供C结构的C API提供

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

和一个填充它的函数:
void getA(A* a);

例如,这可能是从C API内部获取一些信息的吸气剂。

在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 #}编译指示照常进行。

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更好地完成此操作?

最佳答案

您在使用{#pointer ...#}挂钩吗?由于您不会像对待不透明指针那样对待A(即if可以正常访问并且Storable实例是明确编写的),因此,您需要使用箭头形式:

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

在这一点上,您仍然必须使用alloca / peek函数来编组A,但是您的第一个理想的{#fun ...#}挂钩应按编写的方式工作。 (您最终忽略了APtr类型;它显示在生成的代码中,但在*.chs文件中不是必需的。)

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

关于c - 具有c2hs的Haskell FFi:更好的结构编码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53777758/

10-10 18:10