mcm*_*yer 5 c haskell ffi storable haskell-ffi
假设你有一个C结构
typedef struct {
uint32_t num;
char* str;
} MyStruct;
Run Code Online (Sandbox Code Playgroud)
以及对其f执行某些操作的函数,
void f(MyStruct* p);
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
(请注意,该num字段在此构造示例中没有任何用途.它只是防止使用CString和的简单解决方案CStringLen.)
问题是如何为这种C API编写Haskell FFI.
我想出的是:开始
data MyStruct = MyStruct {
num :: Word32,
str :: String
} deriving Show
Run Code Online (Sandbox Code Playgroud)
并写一个可存储的实例.我的想法是在末尾分配64个字节,它将作为字符串的缓冲区:
instance Storable MyStruct where
sizeOf _ = 8{-alignment!-} + sizeOf (nullPtr :: CString) + 64{-buffer-}
alignment _ = 8
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
最后这里peek是直截了当的:
peek p = MyStruct
<$> peek (castPtr p)
<*> peekCAString (castPtr $ plusPtr p 16)
Run Code Online (Sandbox Code Playgroud)
这一切都有效,但我觉得它很难看.这是做到这一点的方式,还是有更好的方法?
如果有人想玩它,小玩具问题就在github上.
更新
正如chi以下警告所指出的那样:使用硬编码对齐和偏移是不好的做法.它们是脆弱的并且依赖于平台/编译器.相反,像工具c2hsc,c2hs 或绑定DSL的,或绿卡等,应使用.
| 归档时间: |
|
| 查看次数: |
94 次 |
| 最近记录: |