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 不支持元组或可变将来,这仍然有限。绑定接口可能如下所示:

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。危险标识符测试应测试是否将危险标识符用作可选参数。

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

替代选项

我们可以按方法或按协议从结构体切换到表。我们甚至可以将默认结构从结构体切换为表。这种方法不够灵活。通常只有请求或响应会扩展。通常情况下,一些参数(例如,在 Modular 中,就是模块 ID)预计会长期保持稳定,而另一些参数却不会。

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

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

开放性问题

我们应该决定如何在 C++、Rust 和 Go 中绑定它。

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

早期技术和参考资料

协议缓冲区声明消息与协议方法不符。

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