RFC-0051:适用于 C++ 的更安全的结构体

RFC-0051:更安全的 C++ 结构体
状态已拒绝
领域
  • FIDL
说明

允许 C++ 开发者编写 FIDL 代码,以便在结构体未完全初始化时在编译时中断。

作者
提交日期(年-月-日)2018-07-19
审核日期(年-月-日)2019-03-14

遭拒原因

此 RFC 最初于 2019 年 3 月 14 日被接受。不过, implementation 从未合并,并且 C++ 绑定 自那时起发生了重大变化。认识到这一点后,我们将采取 拒绝此 RFC。

摘要

允许 C++ 开发者编写在编译时中断的 FIDL 代码 如果结构未完全初始化,则会发生此错误。

设计初衷

在 Peridot 中,我们拥有复杂的 FIDL 结构体,随着我们不断改进, 理解如何解决我们目前正面临的问题。 结构体通常深度嵌套,并在代码中远离 架构。 在对结构体进行迭代时,我们通常会对语义进行破坏性更改, 添加必填字段,或将之前的选填字段设为必填字段。 我们很难找到所有需要更新的代码。 这些错误不会显示为编译时错误,而是显示为运行时错误, 与错误初始化的代码相关联 结构体。

在更新之前,Dart 代码中 要求将所有必填字段传递到结构体中 构造函数。 此更改使 Dart 代码的开发效率和可靠性大大提高。

设计

这会修改 C++ 绑定库和代码生成器。 它不会移除任何现有接口,而只是在 构造 FIDL 结构体的实例。

这会为 FIDL 结构体添加了构建器模式。 使用它大致如下所示:

FooPtr foo = Foo::Builder()->set_bar("hello")->set_baz("world");

结构体类的 Builder() 静态方法会返回模板化构建器 对象。 构建器模板参数可捕获正在构建的结构体的类型, 类。 它存储着结构体的实例。

Field 类有两种方法:set_name(value) 方法,用于设置 字段值,并返回从中移除该字段的构建器 构建器的模板参数,以及对Check() 可选字段;如果必填字段为 static_assert,则会失败。

Builder 类扩展了其模板中的所有字段类型 参数,以便开发者能够访问 setter 方法。 当开发者调用 setter 并接收新的构建器类型时, 构建器模板参数 shrink 中的 field 类。 例如,省略一些模板操作:

Foo::Builder()的会员级别为Builder<Foo, Foo::Field_bar, Foo::Field_baz> 使用 set_bar()set_baz() 方法。

Foo::Builder()->set_bar(...)的会员级别为Builder<Foo, Foo::Field_baz> 使用 set_baz() 方法。

Foo::Builder()->set_bar(...)->set_baz(...)的会员级别为Builder<Foo> 没有任何 setter 方法。

构建器具有结构体类型的隐式转换运算符 struct 指针类型。 它们会对其余字段类型调用 Check() 方法,并返回 struct 实例。 Check() 方法要么为空操作(针对可选字段),要么是空操作 static_assert 次失败,指明未设置哪些必填字段。

文档和示例

FIDL 教程和示例将进行更新,以演示 创建结构体实例的传统方法和新方法。

向后兼容性

此方案纯粹是附加的。 它不会引入向后不兼容性。

性能

此更改不会产生运行时性能成本。 它是在 Compiler Explorer 中进行原型设计的 专门用于确保不会生成额外的代码 。

它会将一个新的头文件添加到绑定库 生成的 C++ 代码中每个结构体字段的行数。 C++ 编译器需要做一些额外的工作才能解析模板 但它没有向编译添加任何可能 产生重大影响。

安全

这一变更使我们能够将程序员错误消除运行时错误 构建时错误。 这样可以减少程序的状态空间并减少错误数量 必须正确处理和测试的用例。 减少意外行为有助于保证安全性。

测试

C++ 绑定单元测试应扩展,以测试构建器 正确设置了不同类型的字段

很难测试构建器(即 未能设置必填字段)的数据被编译器捕获。 目前尚不清楚应如何测试。

缺点、替代方案和未知问题

这会向 FIDL C++ 绑定库添加一些相当棘手的模板。 这会带来维护负担 构建时开销。

之前的模板方法使用位掩码,其模板更简单 但施加了限制,例如 64 个必填字段,增加了 FIDL 编译器。

我们还可以构建一个 linter,尝试跟踪 都已设置完毕。 这似乎是相当复杂的 Dataflow 分析。

先验技术和参考资料

Dart 绑定去年有所更改,因此结构体构造函数采用 命名参数。 必需项标记为必需项,以便 dartanalyzer 拒绝 使某些字段未初始化的更改。