假设您有一个提供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(即i
和f
可以正常访问并且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/