Union and Type** with FFI in Haskell?

i need to know how can i resolve Unions and Type** (eg int**) with the FFI? I know that i need a Storable instance for structs, could i use it for unions too?

a union like so:

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

This would typically be represented in Haskell as:

data MyUnion = I CInt | C CChar

My question is how would you marshall (define an Storable instance for) myUnion into my_union? It's my understanding that an instance my_union would take up sizeof(int) bytes in memory, ie the size of it's largest member. So to store this we would write something along the lines of:

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

Also, how can you represent a int** with the FFI? when i got a function like int foo(int i1, int* i2); the signature would be: foo -> CInt -> Ptr CInt -> CInt

but what if there is : int foo(int i1, int** i2);


C unions are not tagged unions, see wikipedia on this. In haskell MyUnion would take up more memory than a single raw (unboxed) 64-bit int. In GHC it would be a special pointer to either a thunk or a value: the thunk is when a lazy MyUnion has not been evaluated yet, the value is for when it has been evaluated and the pointed-to memory size can vary (unlike unions in C). The "special" pointer would use the usually-zero low bits of the 64-bit pointer to indicate whether it is known to be a C or I value, to combine the tagging with the pointer.

A less-lazy declaration in Haskell can be made with

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

Where the "!" indicates the value is never stored as an unevaluated thunk. The UNPACK compiler pragma comment asks GHC to store the raw unboxed value alongside the tag instead of storing a pointer to the Int or Char. So MyUnion2 may take up less memory and will be strict instead of lazy.

Also, I should emphasize "char" from C is a single signed byte while "Char" in Haskell is a full unicode code point (value 0 to 1114111). To store a C "char in Haskell you would use a CChar.

You have unions in use in C and need to serialize and desearialize them? Do you already have a binary format in use by C ? If you need to invent a binary format then you do need to design a tag to make Haskell happy. Your C example has no way to tell whether the value was "constructed" with an int or a char, while MyUnion in Haskell can tell whether the value was constructed by an I or a C.

The C type you wrote is also quite dangerous, as if I write to the single byte "char" and read the multi-byte "int" the rest of the bytes in the "int" are likely undefined.


Even in C you wouldn't know which member to use (unless clear from the context) if you were handed a:

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

The C solution is to add an extra member that carries the type.

typedef struct {
     int type;
     union {
          int i;
          char c;
     } my_union;
} my_tagged_union;

Yo can get Pointers to Pointer easily (I use something similar to pass a (void*)&val parameter to a C library). On 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
链接地址: http://www.djcxy.com/p/61174.html

上一篇: 哈斯克尔FFI馆际依存关系

下一篇: 联盟和类型**与Haskell FFI?