本部分将帮助您了解如何使用新的 C++ FIDL 绑定。请参阅 使用入门获取设置 从头开始构建和编写简单的客户端或服务器。请参阅主题 了解有关有效使用绑定的更复杂指南和建议。 如需了解出现的名称和概念,请参阅术语 并快速讲解如何做出正确的选择。
概括来讲,C++ 绑定由以下部分组成:
- 数据: 网域对象 ( 生成的 FIDL 结构,例如结构体、表...)
- 行为:通过 协议、接收事件...
自然和有线网域对象
绑定支持两种类型的网域对象:自然类型和 wire 类型:
- 自然类型是针对人体工程学进行优化的高层级领域对象。
- 这些类型通过智能指针拥有其子级。
- 它们使用惯用的 C++ 类型,例如
std::vector
、std::optional
和std::string
。 - 如果有一个名为
fuchsia.my.lib
的 FIDL 库,则会生成类型 位于fuchsia_my_lib
命名空间中。
- wire 类型针对性能和就地解码进行了优化。
- 它们是专用 C++ 标准布局类型,其内存布局 符合 FIDL 线路格式。
- 外联子项是指向单独缓冲区的无主指针。 请参阅有线网域对象的内存所有权。
- 如果有一个名为
fuchsia.my.lib
的 FIDL 库,则会生成类型 位于fuchsia_my_lib::wire
命名空间中。
启动项目时,默认选择自然类型,因为它们更容易 使用起来比较方便,性能相对合理。优化时仅选择电线类型 关键路径中的逻辑,或者需要精确控制 分配。由于电线类型由不安全的视图组成,因此不当使用 可能会导致释放后使用和其他内存安全 bug。
使用入门
- 使用自然和有线网域对象
- 编写服务器
- 编写客户端(异步或同步)
主题
术语
客户端和服务器中的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
,
必须在调度程序线程上绑定和销毁。类似的关系
介于 WireClient
和 WireSharedClient
之间。
建议
在Client
或SharedClient
之间进行选择时,请优先
Client
,除非应用的线程模型或性能要求
需要使用多线程的客户端请参阅
线程处理指南,了解使用线程时的许多注意事项
SharedClient
。Client
中的额外限制旨在
减少内存争用和释放后使用。例如,您可以使用 Client
但前提是它们全都在同一单线程异步调度程序上。
Then
与 ThenExactlyOnce
的双向通话
当异步调用有响应时,有两种方法可以指定 回调以接收该调用的结果:
- 当您使用
.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
指针不再
有效期。
Then
和 ThenExactlyOnce
都会为双向调用注册接续。
不过,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
对象分配缓冲区
对消息进行编码所需的操作。