新 C++ 绑定教程

本部分将帮助您了解如何使用新的 C++ FIDL 绑定。请参阅 使用入门获取设置 从头开始构建和编写简单的客户端或服务器。请参阅主题 了解有关有效使用绑定的更复杂指南和建议。 如需了解出现的名称和概念,请参阅术语 并快速讲解如何做出正确的选择。

概括来讲,C++ 绑定由以下部分组成:

  • 数据网域对象 ( 生成的 FIDL 结构,例如结构体、表...)
  • 行为:通过 协议、接收事件...

自然和有线网域对象

绑定支持两种类型的网域对象:自然类型和 wire 类型:

  • 自然类型是针对人体工程学进行优化的高层级领域对象。
    • 这些类型通过智能指针拥有其子级。
    • 它们使用惯用的 C++ 类型,例如 std::vectorstd::optionalstd::string
    • 如果有一个名为 fuchsia.my.lib 的 FIDL 库,则会生成类型 位于 fuchsia_my_lib 命名空间中。
  • wire 类型针对性能和就地解码进行了优化。
    • 它们是专用 C++ 标准布局类型,其内存布局 符合 FIDL 线路格式。
    • 外联子项是指向单独缓冲区的无主指针。 请参阅有线网域对象的内存所有权
    • 如果有一个名为 fuchsia.my.lib 的 FIDL 库,则会生成类型 位于 fuchsia_my_lib::wire 命名空间中。

启动项目时,默认选择自然类型,因为它们更容易 使用起来比较方便,性能相对合理。优化时仅选择电线类型 关键路径中的逻辑,或者需要精确控制 分配。由于电线类型由不安全的视图组成,因此不当使用 可能会导致释放后使用和其他内存安全 bug。

使用入门

  1. 使用自然和有线网域对象
  2. 编写服务器
  3. 编写客户端(异步同步

主题

术语

客户端和服务器中的Wire与无Wire对比

如果客户端/服务器 API 名称中有 Wire 前缀,则表示该 API 只接受网线类型否则,该 API 通常可以同时接受有线和 自然类型,或默认为自然类型。例如:fidl::Client 支持用自然线和有线网络拨打电话, fidl::WireClient 公开了限制性更强的接口,该接口仅接受数据线 。

fidl::Server 会收到自然类型的请求,并可能会发送 天然或电线类型。另一方面,fidl::WireServer 会获得 请求转换为线路类型,并仅在线路类型中发送回复。

在没有功能的情况下在发送端同时支持电线和自然类型 重载模糊性,有线接口存储在 .wire() 访问器下。 例如,对于 fidl::Client<MyProtocol> client;,可以写入 client->SomeMethod(natural_type);:使用自然类型发出请求;以及 client.wire()->SomeMethod(wire_type);,以便使用线缆类型发出请求。

建议

使用不带 Wire 前缀的客户端/服务器 API。只有在需要 确保在编译时仅使用电线类型,可以定义函数 使用 Wire 副本的签名,例如fidl::WireClient。有可能 也仅依赖于绑定的电线部分 fuchsia.my.lib_cpp_wire 目标,而不是 fuchsia.my.lib_cpp GN 目标。

客户中Sync与无sync对比

同步简而言之,适用于带有响应(双向 调用),且表示调用处于阻塞状态:进行此类调用的线程不会 从调用返回,直到响应返回。例如: fidl::WireSyncClient 是一个客户端,在该客户端中,所有双向调用都是同步的。 同样,fidl::WireClient 有一个 .sync() 访问器,它会返回一个 用于进行同步调用的接口。

单向调用没有响应,因此,同步的概念 不适用于这些内容

建议

如果您的代码是一个独立的程序,只使用来自其他 API 的功能 确定其业务需求所需的并发级别:

  • 如果它不管理大量并发操作,则可使用 客户端,可以轻松阅读直线逻辑。例如, 短期运行的命令行工具可能会使用 fidl::SyncClient

  • 如果您的代码管理大量并发操作,它通常可以访问 异步调度程序 (async_dispatcher_t*)。在选择 同步和异步客户端和调用,首选使用 异步副本。例如,直接选择fidl::WireClient而不去 通过 .sync() 通过 fidl::WireSyncClient.sync() 付款。尤其要注意 如果调度程序是单一的,则不会在调度程序线程上进行同步调用 线程处理,以避免死锁

如果您的代码是一项服务,即为其他 Google Cloud 服务提供功能的组件, 则应使用异步调度程序和异步客户端 支持多个使用者所需的并发级别。

如果您的代码是被其他应用使用的库,它将需要 应该更仔细地考虑是否应该公开同步 具体取决于其用户的需求例如, 并公开同步接口, 高度并发的应用使用起来更困难,这些应用 支持异步调度程序

以上只是一般性建议,不同的异步运行时 获得更具体的建议。

客户中Shared与无shared对比

当某个客户端类型“共享”时它可能会被绑定和销毁 任意线程。请参阅线程处理中的 SharedClient 指南。它将具有一个不带“共享”的对等项,例如 Client, 必须在调度程序线程上绑定和销毁。类似的关系 介于 WireClientWireSharedClient 之间。

建议

ClientSharedClient之间进行选择时,请优先 Client,除非应用的线程模型或性能要求 需要使用多线程的客户端请参阅 线程处理指南,了解使用线程时的许多注意事项 SharedClientClient 中的额外限制旨在 减少内存争用和释放后使用。例如,您可以使用 Client 但前提是它们全都在同一单线程异步调度程序上。

ThenThenExactlyOnce 的双向通话

当异步调用有响应时,有两种方法可以指定 回调以接收该调用的结果:

  • 当您使用 .ThenExactlyOnce(...) 时,系统始终会准确调用该回调 一次交付,最终交付成果。
  • 当您使用 .Then(...) 时,当客户端触发回调时,系统会以静默方式舍弃该回调 对象被销毁,这适合面向对象的代码。

Then的动机

进行异步双向调用时,系统会传递该调用的结果 并在执行时间结束后将其返回给应用 进行调用的原始作用域。异步调度程序稍后会 调用您在拨打电话时指定的后续逻辑(称为 接续。也就是说,在对象被销毁后,可以轻松使用它们, 会导致内存损坏:

// The following snippet shows an example of use-after-free
// occurring in asynchronous two-way calls.
void Foo(fidl::WireClient<MyProtocol>& client) {
  bool call_ok;
  client->SomeMethod().Then(
      // The following lambda function represents the continuation.
      [&call_ok] (fidl::WireUnownedResult<SomeMethod>& result) {
        // `call_ok` has already gone out of scope.
        // This would lead to memory corruption.
        call_ok = result.ok();
      });
}

当接续捕获 this 指针,且所述引用的对象也拥有客户端。正在销毁 外部对象(进而销毁客户端)会导致所有待处理双向 调用失败。当它们的接续运行时,它捕获的 this 指针不再 有效期。

ThenThenExactlyOnce 都会为双向调用注册接续。 不过,Then 旨在减少上述损坏情况。 具体而言:

  • Then 确保提供的接续最多会被调用一次,直到 该客户端会被销毁如果仅用于接续,则应选择 Then 捕获与客户端具有相同生命周期的对象(例如,您的用户对象 拥有该客户)。销毁 User 对象会抵消所有未完成的 回调。无需释放后使用。

  • 另一方面,ThenExactlyOnce 可保证调用接续 正好一次。如果客户端对象被销毁,接续将收到一个 取消错误。您需要确保所有引用的对象仍处于活动状态 该时间过后,该时间可能为 被销毁。如果您的ThenExactlyOnce 接续必须正好调用一次,例如,在 fpromise 完成者或 FIDL 服务器完成者,或在单元测试期间。

建议

一般而言:

  • 如果您的回调类似 client_->Foo([this],请使用 Then(请注意, client_ 是一个成员变量)。
  • 如果您的回调类似于 <ph type="x-smartling-placeholder">
      </ph>
    • client->Foo([completer],或者
    • client->Foo([],或者
    • client->Foo([&](在单元测试中常见),
    • 回调会捕获弱指针或强指针,
    • 请使用 ThenExactlyOnce

请勿捕获具有不同生命周期的对象,以便只捕获 对象处于活动状态。

Zircon 通道传输与驱动程序传输

FIDL 协议与 FIDL 定义,用于确定可能流经的资源类型 并且可能会影响生成的 API,用于发送和接收 消息。C++ 绑定支持两种传输:

Zircon 通道传输由端点类型表示 fidl::ClientEnd<SomeProtocol>fidl::ServerEnd<SomeProtocol>

驱动程序传输使用端点类型 fdf::ClientEnd<SomeProtocol>fdf::ServerEnd<SomeProtocol>

竞技场

Arenas 对象管理内存缓冲区池并提供高效的分配。 它们广泛用于有线域对象,以及连接客户端和服务器 避免成本高昂的复制。

竞技场不用于自然域对象和关联的客户端, 服务器,其中封装了有关内存分配的详细信息。

您可以使用 fidl::Arena 创建位于该上的传输域对象 竞技场。请参阅内存管理

通过有线驱动程序传输使用协议时 网域对象,应使用 fdf::Arena 对象分配缓冲区 对消息进行编码所需的操作。