在使用 FFI 时,C99 的 bool 类型在 Haskell 中的等效类型是什么?

18

我有一个使用C99 bool数据类型的库,我想通过FFI调用它。

在Haskell中,C99 bool的对应类型是什么?在Foreign.C.types中有CInt、CShort等类型,但没有CBool。

如果没有“正确”的bool类型,有哪种安全的替代类型可以传递给期望bool的函数?

另一种方法是修改C库,但我想保持其完整性。


我最终修改了库,使用整数代替布尔值。这是一个内部库,所以修补起来非常简单。无论如何,这是一个有趣的问题。希望基础库将来可以扩展布尔支持。 - Zouppen
5个回答

12

由于C99中sizeof(_Bool)的大小是实现定义的(参见ISO / IEC 9899:1999 6.2.5、6.2.6和6.5.3.4/4), 明显的可移植解决方案是通过使用int而不是bool调用这些函数的包装器。或者,如果您不关心可移植性,可以检查您的编译器文档以找出您平台上的_Bool大小,并使用相应的FFI类型。

我猜CBoolForeign.C.Types中不存在的原因是底层的C实现不支持C99的所有特性。一个非常广泛使用的编译器(MSVC)根本不支持C99。


我不知道这是否作为一个理由有意义。显然,只有在与使用“bool”的C代码进行接口时才会使用CBool,在这种情况下,您假定存在符合标准的编译器。我认为更可能的原因是CBool更加晦涩和不常用,因此还没有人发现有必要编写支持。 - Ben Millwood
由于它是“实现定义的”,因此它可以是任何大小,甚至在符合C99标准的编译器上也可能不同于其他编译器。任何对C的FFI都很难自动和安全地解决这个问题,Haskell FFI也不例外。 - jpaugh
@jpaugh 嗯,sizeof(int) 在 C 中也是实现定义的。然而,我们可以从 Haskell 调用带有 int 参数的函数,因为目标平台的 C ABI 是由平台供应商指定的。问题在于,一些供应商选择忽略 C99 扩展。一个合理的解决方案是在这些情况下将 _Bool 类似于 unsigned char 处理。 - Mikhail Glushenkov

2
据我所知,C99没有定义_Bool类型的确切大小(而bool映射到_Bool)。你唯一知道的是它足够大以容纳值01。因此,它的二进制表示是与编译器相关的,这就是为什么我猜想它不在FFI支持库中的原因。
下面的程序是使用GCC 4.7.2编译的:
#include <stdio.h>
#include <stdbool.h>

int main() {
    printf("%d", sizeof(bool));
    return 0;
}

给出以下输出:
% gcc -std=c99 -o test test.c
% ./test 
1    

在GCC中,bool似乎被映射为char。你看到的结果取决于编译器。

因此,如果你想要完全可移植性,最好为你的C函数编写一个包装器(wrapper),它将采用普通的int并使用类型转换传递给函数。


4
按照同样的推理,CIntCLong也不存在,因为它们也没有精确的大小要求。我们知道关于 int 的只是它至少必须支持从 -32767 到 32767 的值。 - Luc Danton
1
@LucDanton,CIntCLong的基础类型被所有编译器支持,没有例外。但是对于bool来说就不是这样了,因为它是C99扩展,一些最常用的编译器根本不支持C99。显然,良好的FFI实现不应该依赖于存在支持所有最新标准的理想编译器。 - Vladimir Matveev
3
那么应该是这个答案,而不是你所提供的。我的评论只是一种修辞手法,用于展示论点中的缺陷。 - Luc Danton
我认为表示问题完全是一个误导。 _Bool 可能会改变大小并不是问题,我们只需要让 CBool(或其他)做同样的事情即可。 - Ben Millwood

1

将参数设为bool和将返回类型设为bool之间存在差异。


我非常确定C99布尔类型被引入(并且不同于所有其他基本值),是因为预C99的BOOL不是函数的足够好的返回类型:
必须存在这样一个变量的内存表示,至少为1个字节,但是...
1. 在汇编级别上处理布尔值是通过FLAG依赖的goto完成的。(参见&&和||) 2. 应该可以在布尔值上使用更快的按位&和|,而不是&&和||,这需要bool为1位。 3. 函数的返回值应该能够使用FLAG寄存器(在某些机器上),而不是被强制使用INT寄存器的1位或甚至在INT寄存器中使用任何零/非零int。
因为第3点,我认为,除非我们考虑每个平台上C99 bool用法的ABI(应用程序二进制接口),否则不会有直接的解决方案。可能更容易修补Haskell编译器,以包装具有/应该具有C99-bool结果类型的每个导入/导出的C函数。
如果我没记错的话,在C函数的参数列表中,int是大多数基本C类型的安全替代品。但是对于bool类型,CChar很可能会做得更好。您可能希望阅读有关可变参数特性,...)的文章,这是由printf使用的。

1
如果您使用hsc2hs,您可以这样做:
#include <stdbool.h>

type CBool = #{type bool}

cFalse, cTrue :: CBool
cFalse = 0
cTrue = 1

你还可以使用Foreign.Marshall.Utils中的toBoolfromBool函数在CBoolBool之间进行转换。

关键在于#{type bool},它告诉hsc2hs选择一个与C类型bool大小相同的Haskell数值类型。在我的机器上,这个类型是Word8


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接