在Haskell中使用FFI进行联盟和类型**?

时间:2011-06-30 11:14:37

标签: haskell ffi discriminated-union

我需要知道如何用FFI解析联盟和类型**(例如int **)? 我知道我需要一个可存储的结构实例,我也可以将它用于工会吗?

像这样的联盟:

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

这通常在Haskell中表示为:

data MyUnion = I CInt | C CChar

我的问题是你将如何编组(定义一个可存储的实例) myUnion进入my_union?这是我的理解,一个实例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

另外,如何用FFI代表int**? 当我得到像int foo(int i1, int* i2);这样的函数时 签名将是:foo -> CInt -> Ptr CInt -> CInt

但如果有int foo(int i1, int** i2);

会怎样?

3 个答案:

答案 0 :(得分:5)

即使在C中你也不知道使用哪个成员(除非从上下文中清楚),如果你被交给了:

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

C解决方案是添加一个带有该类型的额外成员。

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

答案 1 :(得分:2)

C联盟不是标记的联盟,请参阅wikipedia on this。在haskell中,MyUnion将占用比单个原始(未装箱的)64位int更多的内存。在GHC中,它将是一个指向thunk或值的特殊指针:thunk是在尚未评估惰性MyUnion的时候,该值是针对何时进行评估并且指向的内存大小可以变化(与工会不同)在C)。 “特殊”指针将使用64位指针的通常为零的低位来指示它是否已知为C或I值,以将标记与指针组合。

可以使用

在Haskell中进行一个不太懒惰的声明
data MyUnion1 = I !Int | C !Char
data MyUnion2 = I {-# UNPACK #-} !Int | C {-# UNPACK #-} !Char

“!”表示该值永远不会存储为未评估的thunk。 UNPACK编译器编译指示注释要求GHC将原始未装箱值存储在标签旁边,而不是存储指向Int或Char的指针。所以MyUnion2可能占用更少的内存,而且会严格而不是懒惰。

另外,我应该强调来自C的“char”是单个有符号字节,而Haskell中的“Char”是完整的unicode代码点(值0到1114111)。要在Haskell中存储C“char,您将使用CChar

您是否在C中使用了联合,需要序列化和去除它们?你是否已经使用C的二进制格式?如果您需要发明二进制格式,那么您需要设计一个标签以使Haskell满意。您的C示例无法判断该值是使用int还是char“构造”,而Haskell中的MyUnion可以判断该值是由I还是C构造的。

您编写的C类型也非常危险,就像我写入单字节“char”并读取多字节“int”一样,“int”中的其余字节可能未定义。

答案 2 :(得分:0)

哟可以轻松地将指针指向指针(我使用类似的东西将(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