2

FFI を使用して Union と Type** (int** など) を解決する方法を知りたいですか? 構造体には Storable インスタンスが必要ですが、共用体にも使用できますか?

そのようなユニオン:

typedef union {
     int i;
     char c;
} my_union;

これは通常、Haskell では次のように表されます。

data MyUnion = I CInt | C CChar

私の質問は、どのように myUnion を my_union にマーシャリング (Storable インスタンスを定義) しますか? インスタンス my_union がメモリ内の sizeof(int) バイト、つまり最大のメンバーのサイズを占めることは私の理解です。したがって、これを保存するには、次の行に沿って何かを記述します。

instance Storable myUnion where
     size _ = #{size my_union} -- <-- hsc2hs shortcut
     alignment _ = alignment undefined::CInt -- <-- What should this really be?
     peek ptr = do -- <-- How are you supposed to know which element to extract?
     poke ptr (I i) =  poke ptr i -- <-- Or should this be #{poke my_union, i} ptr i ?
     poke ptr (C c) = poke ptr c

int**また、どのように FFI でa を表すことができますか? int foo(int i1, int* i2); 署名のような関数を取得すると、次のようになります。foo -> CInt -> Ptr CInt -> CInt

しかし、もしあればint foo(int i1, int** i2);

4

3 に答える 3

6

C でさえ、次のように渡された場合、(文脈から明らかでない限り) どのメンバーを使用すればよいかわかりません。

typedef union {
     int i;
     char c;
} my_union;

C の解決策は、型を保持する追加のメンバーを追加することです。

typedef struct {
     int type;
     union {
          int i;
          char c;
     } my_union;
} my_tagged_union;
于 2011-06-30T11:32:34.567 に答える
2

C 共用体はタグ付けされた共用体ではありません。これについてはウィキペディアを参照してください。Haskell では、MyUnion は単一の生の (ボックス化されていない) 64 ビット int よりも多くのメモリを消費します。GHC では、サンクまたは値のいずれかへの特別なポインターになります。サンクは、遅延 MyUnion がまだ評価されていないときのものであり、値は評価されたときのものであり、ポイント先のメモリ サイズはさまざまです (共用体とは異なります)。 Cで)。「特別な」ポインタは、64 ビット ポインタの通常ゼロの下位ビットを使用して、それが C 値であるか I 値であるかを示し、タグ付けとポインタを組み合わせます。

Haskell での遅延の少ない宣言は、次のようにして行うことができます。

data MyUnion1 = I !Int | C !Char
data MyUnion2 = I {-# UNPACK #-} !Int | C {-# UNPACK #-} !Char

どこ "!" 値が未評価のサンクとして保存されないことを示します。UNPACK コンパイラ プラグマ コメントは、GHC に、Int または Char へのポインターを格納する代わりに、生のボックス化されていない値をタグと一緒に格納するように要求します。そのため、MyUnion2 はメモリ使用量が少なくなり、遅延ではなく厳密になります。

また、C の「char」は単一の符号付きバイトであるのに対し、Haskell の「Char」は完全な Unicode コード ポイント (値 0 ~ 1114111) であることを強調しておく必要があります。C "char を Haskell に格納するには、 CCharを使用します。

C で共用体を使用していて、それらをシリアライズおよびデシリアライズする必要がありますか? C で使用されているバイナリ形式は既にありますか? バイナリ形式を発明する必要がある場合は、Haskell を満足させるタグを設計する必要があります。C の例では、値が int または char で「構築」されたかどうかを判断する方法はありませんが、Haskell の MyUnion では、値が I または C で構築されたかどうかを判断できます。

あなたが書いたC型も非常に危険です。シングルバイトの「char」に書き込み、マルチバイトの「int」を読み取ると、「int」の残りのバイトは未定義になる可能性があります。

于 2011-06-30T12:36:52.140 に答える
0

YoはPointers to Pointerを簡単に取得できます((void*)&valパラメータをCライブラリに渡すために似たようなものを使用しています)。ghci で:

> a <- malloc :: (IO (Ptr Int))
> dir_a <- malloc :: (IO (Ptr (Ptr Int)))
> poke dir_a a
> poke a 5

> b <- peek dir_a
> peek b
5
于 2011-07-01T17:56:32.043 に答える