c - Haskell FFI:包装包含单独分配的字符串(char *)的C结构
问题描述
假设你有一个 C-struct
typedef struct {
uint32_t num;
char* str;
} MyStruct;
以及对其进行一些操作的函数f
,
void f(MyStruct* p);
C API 要求char*
在调用之前分配足够的缓冲区f
:
char buf[64]; //the C API docs say 64
MyStruct s = {1, buf};
f(s); // would go badly if MyStruct.str isn't alloc'ed
(请注意,该num
字段在此构造示例中没有任何用途。它只是防止使用CString
and的简单解决方案CStringLen
。)
问题是如何为这种 C API 编写 Haskell FFI。
我想出的是:从
data MyStruct = MyStruct {
num :: Word32,
str :: String
} deriving Show
并编写一个可存储的实例。我的想法是在最后分配 64 个字节作为字符串的缓冲区:
instance Storable MyStruct where
sizeOf _ = 8{-alignment!-} + sizeOf (nullPtr :: CString) + 64{-buffer-}
alignment _ = 8
poke
必须更改 str 中的指针以指向分配的缓冲区,然后必须将 Haskell 字符串复制到其中。我这样做withCStringLen
:
poke p x = do
pokeByteOff p 0 (num x)
poke strPtr bufPtr
withCStringLen (str x) $ \(p',l) -> copyBytes bufPtr p' (l+1) -- not sure about the +1
where strPtr = castPtr $ plusPtr p 8 :: Ptr CString
bufPtr = castPtr $ plusPtr p 16 :: CString
最后是peek
,这很简单:
peek p = MyStruct
<$> peek (castPtr p)
<*> peekCAString (castPtr $ plusPtr p 16)
所有这些都有效,但我觉得它相当难看。这是这样做的方法,还是有更好的方法?
如果有人想玩它,小玩具问题在github 上。
更新
chi
正如以下警告所指出的那样:使用硬编码的对齐和偏移是不好的做法。它们很脆弱并且依赖于平台/编译器。相反,应该使用c2hsc、c2hs 或bindings-dsl或greencard等工具。
解决方案
虽然您的解决方案对我来说似乎相当不错(您将内存摆弄隐藏在Storable
实例中,因此用户不应该自己寻找内存缓冲区),但您也可以使用allocaBytes
.
推荐阅读
- autoload - Rails 6+、zeitwerk 自动加载器和命名空间常量
- sql - 使用 group by 从重复记录中选择唯一记录
- swiftui - SwiftUI:我可以向 TabView 添加更多视图,然后是选项卡项吗?
- python - clean 检查 dtype 是否是一个类型的实例?
- c++ - 将正常功能的工作分配给多个线程是否安全?
- c# - 如何在二传手内部调用逻辑?
- c# - 使用脚本在画布中更改 TextMeshPro UI 文本时出现问题
- php - Laravel - 嵌套的 foreach 循环
- python - 为什么最后没有显示?一旦我调用主函数?需要如何改进代码以使其最后不显示?
- python - 二叉搜索树的删除函数不返回