2

我需要知道如何使用 FFI 解决 Unions 和 Type**(例如 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

另外,您如何代表int**FFI?当我得到一个像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 联合没有标记联合,请参阅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)。要将 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

哟可以很容易地获得指向指针的指针(我使用类似的东西将(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 回答