RFC-0045:大小为零的空结构体 | |
---|---|
状态 | 已拒绝 |
领域 |
|
说明 | RFC-0056(“空结构体”)支持定义空结构体,从而改进了语言工效学设计。空结构体不携带任何内容,但它们目前在传输格式中占用一个字节,以便兼容所有 FIDL 语言实现。这会占用不必要的空间,由于 FIDL 在许多上下文中采用 8 字节对齐方式,空间通常会变得更糟。 |
作者 | |
提交日期(年-月-日) | 2018-12-26 |
审核日期(年-月-日) | 2019-05-29 |
遭拒原因
由于工作优先级较高,此 RFC 已被作者拒绝,并且 影响很小 如果本视频中的主意 未来产生的影响。
因此,某些部分不完整。
摘要
RFC-0056(“Empty Structs”)通过 定义空结构体。 空结构体不携带任何内容,但它们目前在 线上格式与所有 FIDL 语言实现兼容。 这会占用不必要的空间,由于 FIDL 8 字节对齐。
此 RFC 以 RFC-0056 为基础而构建,增强了空结构体以占据零字节 电线。
设计初衷
RFC-0056 用于标识空结构体的用例:
- 按计划未来使用
- 表示为联盟中的一种选项
- 作为命令 图案,
除了一个不含任何信息的对象普遍不公平之外 占用非零的电线空间, 空结构体对于未来潜在的 FIDL 工作非常重要,例如 代数数据类型 或泛型。
size-one 空结构体设计也会使其他所有 FIDL 目标语言 支付特定于 C++ 的费用(如需了解详情,请参阅下文的设计部分) 详情)。 其他语言通常可以表示包含零字节的空结构体。
设计
两种设计:任一种选择。我们得选择。
我更喜欢设计 1,使用零长度的数组来表示空的 结构体。 这种做法对于用户来说没有那么惊讶 应用场景。 但缺点是,我们需要使用 C 扩展名,这可能是不可接受的。
设计 2 可以完成,但当设计 空结构体会用作 FIDL 方法的参数。 我很想听一听这方面的一些想法和反馈。
如果编译器支持零长度数组,我们还可以假设设计 1, 如果不是,则设计 2我挺喜欢的。
设计 1:零长度数组
此 RFC 提议使用长度为零的数组来表示一个空结构体。 这是 FIDL 目标所支持且普遍支持的扩展 C 和C++ 编译器。
对于 C 和 C 的简化示例,C++:
// FIDL: struct Empty {};
// define a zero-length array type
typedef int FIDLEmptyStructInternal[0];
typedef struct {
FIDLEmptyStructInternal reserved;
} Empty;
上述代码段针对 C 和sizeof(Empty) == 0
C++。
实际上,为绑定生成的代码应该会关闭各种警告
适用于 C 和 C++1;GitHub Gist显示了
生成的 C 和C++ 绑定。
设计 2:完全省略发出空结构体
必须将 FIDL 结构体转换为 C 和 C++ 中的等效结构体。 空结构体是一种特殊情况,因为 C 和 C++ 处理它们的方式不同 空结构体:
为了解决此问题,RFC-0056 建议生成具有单个
uint8
成员来表示一个空结构体,这在 C 语言中是一致的
和 C++。
在三种不同的上下文中,空结构体会出现:
- 位于包含结构体内,
- 或位于包含性联合或表中的内部
- 作为“顶级”通过成为 FIDL 接口的参数来实现 struct 方法。
这三种上下文可以单独处理。
在容器结构体内部
父结构体内的空结构体可以包含 已省略空结构体。 例如,以下 FIDL 结构体:
// FIDL
struct FooOptions {
// There is currently no information here but
// we have preserved the plumbing for now.
};
struct Foo {
uint32 before;
FooOptions options;
uint32 after;
};
而不必生成“FooOptions”空结构成员 C/C++ 绑定:
// generated C or C++ code
struct Foo {
uint32_t before;
// "FooOptions options" is missing here
uint32_t after;
};
然后,序列化 FIDL 线格式将与 C/C++ 内存兼容 并且可以直接与这两种格式相互转换。
由于空结构体不包含任何信息,因此无法访问
.options
成员几乎不会带来任何后果。
如果结构体之后变为非空结构,则包含的结构体可以
在源代码中发出之前的空结构体成员 options
兼容 3。
人们可能希望采取的一种合理操作是,
空结构体(即 &(foo.options)
)的地址,系统将不再是
可能发生的变化。
我们认为这是可以接受的,但权衡的是一致的跨语言
零大小的空结构体。
TODO(apang):Go、Rust、Dart。
在包含表或联合体内
表或(静态或可扩展)联合具有序数(“标记”) 指明表/联合携带的信息。 在本例中,空结构体“携带了信息” 其存在表示信息,即使空结构体本身也是如此 不携带任何信息。
因此,表或联合仍将发出序数,以便客户端代码 可以进行检查,以确定表/联合中包含哪些信息。 不过,空结构体本身将无法访问。 例如,空结构体的并集:
// FIDL
struct Option1 {};
struct Option2 {};
union {
// an "empty" union!
Option1 o1;
Option2 o2;
};
我仍然会:
- 具有明确定义的内存布局,该布局将包含
uint32
枚举标记。 - 发出表示序数和相应访问器方法的枚举, 以便客户端代码可以创建和检查此类联合。
TODO(apang):包含示例 C/C++ 绑定。
表类似:表字段存在空结构体 表示信息。 对联合采用相同的方法,即发出序数和联合的枚举 客户端代码,但不访问空结构体 - 可用于 表格。
TODO(apang):Go、Rust、Dart。
作为 FIDL 方法参数
目前已有一些用例将空结构体用作 FIDL 方法参数(例如 fuchsia.ui.viewsv1 中):
interface ViewContainerListener {
// |ViewInfo| is an empty struct
OnChildAttached(uint32 child_key, ViewInfo child_view_info) -> ();
};
struct ViewInfo {};
任何为空结构体的方法参数可以是:
- 方法签名中省略此参数(推荐),
- 在 C 或 C++ 中规范化为单个空结构体单例类型(例如,
不直接映射到零字节线的
fidl::EmptyStruct
) 格式,或者 - 并采用不直接 编码/解码为线缆格式。
变更
- 无需更改 FIDL 源语言。
- FIDL 传输格式和文档将会更改, 结构体在线路上占用了 0 个字节,而不是 1 个字节。
- “
fidlc
”无需任何更改。 - 每种语言后端(C、C++、Rust、Go、Dart)都需要更新为 反映本节中讨论的绑定更改。 此操作应作为硬过渡完成,以便跨层 ABI 兼容性。
实施策略
实现类似于 RFC-0056, 需要拆分为多个 CL:
- 用于更新针对所有语言生成的绑定的 CL,而不更新 跨语言兼容性测试
- 硬转换集成 CL,确保滚轮成功。
- 更新跨语言。
- 更新。
无需为此 RFC 更改 FIDL 源语言。
缺点、替代方案和未知问题
请注意:
- 它在 C 和 C++ 中都是一致的,
- 这两种语言具有不同的大小实现方式
- 从概念上讲,C++ 要求
- 零长度数组
- C++ [[无唯一地址]]
先验技术和参考资料
https://herbsutter.com/2009/09/02/when-is-a-zero-length-array-okay/