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

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

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

作者
提交日期(年-月-日)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

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

实现策略

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

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

工效学

允许 FIDL 协议的发展是用户寻求的一项重要功能。目前,更改方法的实参是 ABI 和 API 的重大更改。缓解此问题的方法是为每次更改引入新的方法名称,并继续支持旧方法(只要仍有调用方),或者将表作为实参并向该表添加新实参。此提案允许以更符合工效学的方式表达后一种方法。它保留了在方法定义中定义的实参,以便可以在文档注释中轻松引用它们。

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

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

文档和示例

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

向后兼容性

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

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

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

性能

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

安全

无影响

测试

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

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

替代方案

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

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

我们可以使用版本化方法。我们没有深入探讨此选项。

开放性问题

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

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

在先技术和参考资料

Protobuf 会在协议方法之外声明消息。

Flatbuffers 和 cap'n proto 使用版本控制。