C構造体があるとします
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
この構築された例では、フィールドには目的がないことに注意してください。これは、 and を使用した簡単な解決策を妨げるだけCString
ですCStringLen
。)
問題は、この種の C API 用の Haskell FFI をどのように作成するかです。
私が思いついたのはこれです:
data MyStruct = MyStruct {
num :: Word32,
str :: String
} deriving Show
Storable インスタンスを作成します。私の考えは、文字列のバッファとして機能する最後に 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などのツールを使用する必要があります。