5

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次の警告で指摘されているように、ハードコーディングされたアライメントとオフセットを使用することは悪い習慣です。それらは壊れやすく、プラットフォーム/コンパイラに依存します。代わりに、c2hscc2hs またはbindings-dslgreencardなどのツールを使用する必要があります。

4

1 に答える 1