RFC-0044:可扩展的方法参数

RFC-0044:可扩展的方法实参
状态已拒绝
区域
  • FIDL
说明

我们建议 FIDL 库作者在需要可扩展性时使用表而不是结构体,但方法实参会编码为结构体。此提案提出了一种基于表格构建可扩展方法实参的方式。

作者
提交日期(年-月-日)2019-04-08

拒绝理由

已由 RFC-0050:语法改版取代。

摘要

我们建议 FIDL 库作者在需要可扩展性时使用表而不是结构,但方法实参会编码为结构。此 RFC 提出了一种基于表构建可扩展方法实参的方法。

设计初衷

缺少可扩展方法实参的语法会阻碍库作者使用表来使方法实参可扩展。通过在语法中包含此内容,可以在设计协议时将可扩展性考虑因素放在首位。

Modular 团队正在设计需要同时保持 ABI 兼容性并具有可扩展性的协议,因为他们会发现新的要求并不断改进设计。他们正在考虑以表的形式定义方法,这些表是他们声明的内嵌表。

设计

此提案扩展了 FIDL 源语言,并影响了语言绑定。

FIDL 语法

它提议扩展方法和事件请求与响应实参的语法,以向实参结构体添加表格。

例如,以下协议:

protocol Example {
    Foo(int32 arg1, { 1: string arg2, 2: bool arg3 }) -> ({});
};

声明了一个方法,该方法包含一个必需实参、两个可选实参、一个可扩展的请求和一个可扩展的响应。它相当于声明:

table ExampleFooRequestExtension {
    1: string arg2;
    2: bool arg3;
};
table ExampleFooResponseExtension {
};
protocol Example {
    Foo(int32 arg1, ExampleFooRequestExtension extension)
        -> (ExampleFooResponseExtension extension);
}

IR

当前版本的 IR 可以以向后兼容的方式进行扩展。可扩展的方法实参包含名为 {Protocol}{Method}RequestExtension{Protocol}{Method}ResponseExtension{Protocol}{Method}EventExtension 的表。系统将为这些生成的表设置 [ExtensionArgument] 属性,以便绑定生成器可以根据需要对它们进行特殊处理。

未来的 IR 可能会将此想法提升为更高级别的结构。未来的 IR 应该允许更灵活的声明命名方法,以便语言在命名扩展表时做出更好的选择,例如 C++ 可以将它们嵌套在协议定义类中。

绑定

绑定不需要对可扩展的方法实参提供特殊支持。现有绑定生成器只需将生成的表作为方法的最后一个实参包含在内即可。

C++

例如,C++ 绑定(无需对上述协议进行任何更改)大致如下所示:

class ExampleFooRequestExtension;
class ExampleFooResponseExtension;
class Example {
  using FooCallback = fit::function<void(ExampleFooResponseExtension)>;
  virtual void Foo(int32_t arg1,
                   ExampleFooRequestExtension extension,
                   FooCallback callback) = 0;
};

绑定此协议的另一种方法是:

class Example {
  using FooCallback = fit::function<void()>;
  virtual void Foo(int32_t arg1,
                   FooCallback callback,
                   std::optional<std::string> arg2 = std::optional<std::string>(),
                   std::optional<bool> arg3 = std::optional<bool>()) = 0;
};

这样会更符合方法的声明方式,但会将回调实参放在静态实参和可扩展实参之间。

Dart

Dart 对可选的命名实参的支持使得 FIDL 概念能够很好地映射到其语法。Dart 不支持元组或可变数量的 future,这仍然是一个限制。绑定接口可能如下所示:

abstract class Example {
  Future<ExampleFooResponseExtension> foo(int arg1, {String arg2, bool arg3});
}

使用此语法添加其他扩展实参可保持源代码兼容性。

Rust

待定

Go

待定

Simple C

简单的 C 绑定不支持表,因此此功能与它们不兼容。

实施策略

第一步是将对新语法的支持添加到 fidlc 中,并更新参考文档和教程文档。

接下来,我们将为 Dart 绑定添加支持,因为它们具有最明显的人体工程学优势。

工效学设计

允许 FIDL 协议发展演变是用户寻求的一项重要功能。目前,更改方法的实参是一项会破坏 ABI 和 API 的更改。缓解此问题的方法是,为每次更改引入新的方法名称,并在仍有调用者的情况下继续支持旧方法,或者将表格作为实参包含在内,并向该表格添加新的实参。此提案允许以更符合人体工程学的方式表达后一种方法。它会保留方法定义中定义的实参,以便在文档注释中轻松引用这些实参。

可选实参的概念在许多编程语言中都很常见。对于库作者来说,这并不是一个令人惊讶的概念。

表中的显式序号使得扩展实参与必需实参不一致,但最好让它们与表保持一致,而不是引入具有哈希序号的新类表结构。

文档和示例

作为 FIDL 语言的扩展,FIDL 参考文档和教程文档应进行更新。

向后兼容性

现有 FIDL 库不受此变更的影响。

此提案可显著提升库作者长期维护 ABI 兼容接口的能力。

有关源代码兼容性的限制仍待确定,将根据我们计划如何绑定到支持的语言来确定。

性能

与结构体相比,表的编码和解码成本更高,因此对性能要求较高的协议应谨慎使用此功能

安全

无影响

测试

应将测试添加到 fidlc。危险标识符测试应测试将危险标识符用作可选实参的情况。

缺点、替代方案和未知因素

替代方案

我们可以按方法或按协议从结构体切换到表。我们甚至可以将默认设置从结构体切换到表。这种方法不太灵活。通常,只需扩展请求或响应。通常,某些实参(例如,在 Modular 的情况下,模块 ID)预计会在长期内保持稳定,而其他实参则不会。

我们可以定义一个类似于表格的数据结构,该结构可对名称进行哈希处理以计算序号,而不是使用表格并为每个扩展实参指定序号。这会简化源语言的语法,但会增加编码器、解码器和语言绑定的复杂性。

我们可以使用版本化方法。本教程未深入探讨此选项。

开放性问题

我们应决定如何在 C++、Rust 和 Go 中绑定此功能。

是否应允许添加实参来破坏源代码兼容性?

在先技术和参考资料

Protobuf 声明的消息与协议方法无关。

Flatbuffers 和 Cap'n Proto 使用版本控制。