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

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

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

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

拒绝理由

已被 RFC-0050:语法改进弃用。

摘要

我们建议 FIDL 库作者在需要可扩展性但方法参数编码为结构体时使用表格而非结构体。这提出了一种在表上构建可扩展的方法参数的方法。

设计初衷

由于缺少可扩展方法参数的语法,因此库作者不太可能使用表来使方法参数可扩展。通过在语法中添加此项,在设计协议时,可将可扩展性注意事项放在首位。

模块化团队在设计协议时,需要同时考虑如何保持 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

待定

简单 C

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

实施策略

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

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

工效学设计

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

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

由于需要在表中使用显式序数,因此扩展实参与必需实参不一致,但最好使它们与表保持一致,而不是引入使用经过哈希处理的序数的新表格式结构。

文档和示例

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

向后兼容性

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

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

关于源代码兼容性的限制仍待确定,具体取决于我们计划如何绑定到受支持的语言。

性能

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

安全

无影响

测试

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

缺点、替代方案和未知情况

替代方案

我们可以按方法或协议从结构体切换到表。我们甚至可以将默认值从结构体切换为表。这种方法的灵活性较低。通常,只有请求或响应需要延长。通常,一些参数(例如在模块化的情况下,模块 ID)预计会长期保持稳定,而其他参数则不会。

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

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

开放性问题

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

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

在先技术和参考文档

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

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