代码之家  ›  专栏  ›  技术社区  ›  Edd Barrett

通过FFI返回C99“bool”生锈的正确类型是什么?

  •  15
  • Edd Barrett  · 技术社区  · 7 年前

    我和一位同事一直在琢磨如何归还 bool 从…起 <stdbool.h> (也称为。 _Bool )通过FFI返回Rust。

    我们有C99代码,我们想从Rust开始使用:

    bool
    myfunc(void) {
       ...
    }
    

    我们让Rust知道 myfunc 使用 extern C 块:

    extern "C" {
        fn myfunc() -> T;
    }
    

    应采用何种混凝土类型 T

    铁锈没有 c_bool libc 如果你在互联网上搜索,你会发现人们讨论这一问题的各种GitHub问题和RFC,但对于什么是正确的和可移植的,并没有达成任何共识:

    据我所知:

    • a的大小 布尔 在C99中,除了它必须至少足够大以存储 true (1) 和 false (0). 换句话说,至少有一位长。
    • 甚至可能是 one bit wide .
    • 它的大小可能是 ABI defined .

    This comment 建议如果C99 布尔 作为参数传入函数或作为返回值传出函数, 这个 布尔 小于C int 然后它被提升到与 内景 . 在这种情况下,我们可以告诉Rust T u32 .

    好吧,但如果(出于某种原因)C99 布尔 64位宽吗?是 u32 还安全吗?也许在这种情况下,我们会截断4个最高有效字节,这很好,因为4个最低有效字节足以表示 真的 错误 .

    我的推理正确吗?直到生锈 libc::c_bool ,你会用什么 T 为什么它对于所有可能尺寸的C99来说都是安全和便携的 布尔 (>=1位)?

    2 回复  |  直到 6 年前
        1
  •  10
  •   Shepmaster Lukas Kalbertodt    6 年前

    截至年月日 2018-02-01 ,锈斑的大小为 officially the same as C's _Bool .

    这意味着 bool 是在外国金融机构中使用的正确类型。


    这个答案的其余部分适用于官方决定作出之前的版本

    直到生锈 libc::c_bool ,你会用什么 T 为什么它对于所有可能尺寸的C99布尔值(>=1位)都是安全和便携的?

    正如您已经链接到的那样 公务的 答案仍然是“待定”。这意味着保证正确的唯一可能性是: 没有什么 .

    是的,尽管它可能很悲伤。唯一真正安全的事情是转换你的 布尔 到已知的固定大小的积分类型,例如 u8 ,用于外国金融机构。这意味着你需要在双方进行协调。


    实际上,我会继续使用 布尔 在我的外国金融机构代码中。正如人们所指出的,它神奇地排列在目前广泛使用的所有平台上。如果语言决定 布尔 外国金融机构兼容,你很好去。如果他们决定其他事情,我会 非常 如果他们没有引入lint来让我们快速捕捉错误,我们会感到惊讶。

    另请参见:

        2
  •  0
  •   Edd Barrett    7 年前

    经过深思熟虑,我将试着回答我自己的问题。如果你能在以下推理中找到漏洞,请发表评论。

    这不是正确答案——请参见下面的评论

    我想是生锈了 u8 始终安全 T .

    我们知道C99 bool 是一个足以存储0或1的整数,这意味着它可以是至少1位的无符号整数,或者(如果你觉得奇怪)至少2位的有符号整数。

    让我们按案例进行分析:

    1. 如果C99 布尔 是8位然后生锈 u8 是完美的。即使在有符号的情况下,顶部位也将是零,因为表示0和1永远不需要负二次方。

    2. 如果C99 布尔 比铁锈还大 u8 ,然后通过“向下转换”到8位大小,我们只会丢弃前导零。因此,这也是安全的。

    3. 现在考虑C99 布尔 比铁锈小 u8 . 当从C函数返回值时,由于底层调用约定,不可能返回小于一个字节的值。CC将要求将返回值加载到寄存器或堆栈上的某个位置。由于最小的寄存器或内存位置是一个字节,因此需要将返回值(带零)扩展到至少一个字节大小的值(我相信函数参数也是如此,它也必须遵守调用约定)。如果将该值扩展为一个字节的值,则与情况1相同。如果将该值扩展到更大的大小,则与情况2相同。