【发布时间】: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 以更好的方式完成吗?
【问题讨论】: