我需要知道如何用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);
答案 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