比较新的 C++ 和高级 C++ 语言绑定

快速参考

下面介绍了如何识别 C++ 代码中的特定类型/函数/标识符 新 C++ 绑定或高级 C++ 绑定的一部分。

examples.keyvaluestore.baseline 库为例:

library examples.keyvaluestore.baseline;

type Item = struct {
    key string:128;
    value vector<byte>:64000;
};

type WriteError = flexible enum {
    UNKNOWN = 0;
    INVALID_KEY = 1;
    INVALID_VALUE = 2;
    ALREADY_EXISTS = 3;
};

@discoverable
open protocol Store {
    /// Writes an item to the store.
    flexible WriteItem(struct {
        attempt Item;
    }) -> () error WriteError;
};

下面介绍了各种 FIDL 元素在 C++ 绑定中的对应关系。注意事项 在“C++”表格中指的是新的 C++ 绑定, 自然域对象和有线域对象。“自然”是指 新的 C++ 绑定中的自然域对象。“Wire”指的是传输域 新 C++ 绑定中的对象。

FIDL 元素 C++ 自然类型 评论
标题包含 C++ #include <fidl/examples.keyvaluestore.baseline/cpp/fidl.h> 格式为 `fidl/library name/cpp/fidl.h`
HLCPP #include <examples/keyvaluestore/baseline/cpp/fidl.h> 格式为 `正斜线分隔的库名称/cpp/fidl.h`
图书馆 C++ ::examples_keyvaluestore_baseline 新的 C++ 使用单级命名空间。
HLCPP 使用嵌套命名空间。
HLCPP ::examples::keyvaluestore::baseline
商品结构 自然 ::examples_keyvaluestore_baseline::Item 除了命名空间差异之外,电线类型嵌套在“::wire”下。
金属丝 ::examples_keyvaluestore_baseline::wire::Item
HLCPP ::examples::keyvaluestore::baseline::Item
WriteError 枚举 自然 ::examples_keyvaluestore_baseline::WriteError 除了命名空间差异之外,导线类型嵌套在“::wire”下。
对于枚举和位,导线类型和自然类型是等效的。只有一个额外的类型别名。
金属丝 ::examples_keyvaluestore_baseline::wire::WriteError
HLCPP ::examples::keyvaluestore::baseline::WriteError
string:128 自然 std::string
金属丝 fidl::StringView
HLCPP std::string
vector<byte>:64000 自然 std::vector<uint8_t>
金属丝 fidl::VectorView&lt;uint8_t&gt;
HLCPP std::vector<uint8_t>
协议存储区 C++ ::examples_keyvaluestore_baseline::Store 一种标记类型,包含协议的一些相关信息
HLCPP ::examples::keyvaluestore::baseline::Store 包含协议中方法的抽象基类
client_end:Store C++ fidl::ClientEnd&lt;Store&gt;
HLCPP fidl::InterfaceHandle&lt;Store&gt;
server_end:Store C++ fidl::ServerEnd&lt;Store&gt;
HLCPP fidl::InterfaceRequest&lt;Store&gt;
商店协议对应的
客户端和服务器类型
自然 客户端:fidl::Client<Store>
同步客户端:fidl::SyncClient<Store>
服务器接口:fidl::Server<Store>
事件处理脚本接口:fidl::EventHandler<Store>
金属丝 客户端:fidl::WireClient<Store>
同步客户端:fidl::WireSyncClient<Store>
服务器接口:fidl::WireServer<Store>
事件处理脚本接口:fidl::WireEventHandler<Store>
HLCPP 客户端:fidl::InterfacePtr<Store>
同步客户端:fidl::SynchronousInterfacePtr<Store>
服务器接口:Store
事件处理脚本接口:不适用。InterfacePtr 的 setter 会针对每个事件声明接受一个回调。

以下是设置客户端的最常用方法:

C++(自然)

  // Connect to the protocol inside the component's namespace. This can fail so it's wrapped in a
  // |zx::result| and it must be checked for errors.
  zx::result client_end = component::Connect<examples_canvas_baseline::Instance>();
  if (!client_end.is_ok()) {
    FX_LOGS(ERROR) << "Synchronous error when connecting to the |Instance| protocol: "
                   << client_end.status_string();
    return -1;
  }

  // Create an instance of the event handler.
  EventHandler event_handler(loop);

  // Create an asynchronous client using the newly-established connection.
  fidl::Client client(std::move(*client_end), dispatcher, &event_handler);
  FX_LOGS(INFO) << "Outgoing connection enabled";

C++(有线)

  // Connect to the protocol inside the component's namespace. This can fail so it's wrapped in a
  // |zx::result| and it must be checked for errors.
  zx::result client_end = component::Connect<examples_canvas_baseline::Instance>();
  if (!client_end.is_ok()) {
    FX_LOGS(ERROR) << "Synchronous error when connecting to the |Instance| protocol: "
                   << client_end.status_string();
    return -1;
  }

  // Create an instance of the event handler.
  EventHandler event_handler(loop);

  // Create an asynchronous client using the newly-established connection.
  fidl::WireClient client(std::move(*client_end), dispatcher, &event_handler);
  FX_LOGS(INFO) << "Outgoing connection enabled";

HLCPP

  // Connect to the protocol inside the component's namespace, then create an asynchronous client
  // using the newly-established connection.
  examples::canvas::baseline::InstancePtr instance_proxy;
  auto context = sys::ComponentContext::Create();
  context->svc()->Connect(instance_proxy.NewRequest(dispatcher));
  FX_LOGS(INFO) << "Outgoing connection enabled";

  instance_proxy.set_error_handler([&loop](zx_status_t status) {
    FX_LOGS(ERROR) << "Shutdown unexpectedly";
    loop.Quit();
  });

如需查看完整的代码列表和说明,请参阅 canvas 示例。

以下是实现服务器的最常用方法:

C++(自然)

// An implementation of the |Instance| protocol.
class InstanceImpl final : public fidl::Server<examples_canvas_baseline::Instance> {
  void AddLine(AddLineRequest& request, AddLineCompleter::Sync& completer) override {
    // ...
  }
};

C++(有线)

// An implementation of the |Instance| protocol.
class InstanceImpl final : public fidl::WireServer<examples_canvas_baseline::Instance> {
  void AddLine(AddLineRequestView request, AddLineCompleter::Sync& completer) override {
    // ...
  }
};

HLCPP

// An implementation of the |Instance| protocol.
class InstanceImpl final : public examples::canvas::baseline::Instance {
  void AddLine(Line line) override {
    // ...
  }
};

如需查看完整的代码列表和说明,请参阅 canvas 示例。

新的 C++ 绑定

新的 C++ 绑定同时支持低层级和高层级用例,具体方法是 提供了两个生成的域对象系列,以及相应的客户端和 支持这些类型的服务器 API。

自然类型

  • 经过优化,可满足高级服务编排的需求。
  • 使用惯用的 C++ 类型(例如 std::vector)表示数据结构, std::optionalstd::string
  • 使用智能指针管理堆分配的对象。
  • 使用 zx::handle 管理所有权。
  • 可以在线缆(例如 fidl::StringView)和自然线之间转换数据 类型表示法(例如 std::string)。

电汇类型

  • 经过优化,可满足低级别系统编程的需求,同时提供 安全性和功能略高于 C 绑定。
  • 表示内存布局与电线重合的数据结构 格式,即满足 C++ 标准布局的要求。这将打开 就地编码和解码。
  • 生成的结构是底层缓冲区的视图;不归自己所有 内存。
  • 支持就地访问 FIDL 消息。
  • 提供对内存分配的精细控制。
  • 使用自有标识名类型,例如 zx::handle。请注意,由于生成了 结构是底层缓冲区的视图,因此父结构只会 自身的子级句柄,前提是它也拥有其底层缓冲区。例如, FIDL 结构体拥有内嵌存储的所有句柄,但 FIDL 结构体拥有结构体向量 包含的手柄将表示为矢量视图,该视图不会拥有 执行脱线句柄

客户端和服务器 API

  • 与 C 绑定相比,代码生成器生成的代码更多。这包括 构造函数、析构函数、复制/移动函数、域之间的转换 对象系列、协议客户端实现和纯虚拟服务器 界面。
  • 用户通过为提供的服务器接口创建子类来实现服务器,以及 为每项操作替换纯虚方法。
  • 支持同步和异步调用以及支持同步和异步事件处理的客户端。
  • 需要 C++17 或更高版本。

如需开始使用,请参阅新建 C++ 教程

高级 C++ 绑定

  • 经过优化,可满足高级服务编排的需求。
  • 使用惯用的 C++ 类型(例如 std::vector)表示数据结构, std::optionalstd::string
  • 使用智能指针管理堆分配的对象。
  • 使用 zx::handle (libzx) 管理处理所有权。
  • 可以将数据从就地 FIDL 缓冲区转换为惯用的分配堆 对象的操作。
  • 可以从惯用的堆分配对象转换数据 (例如 std::string)添加到就地缓冲区(例如,作为 fidl::StringView)。
  • 与 C 绑定相比,代码生成器生成的代码更多。这包括 构造函数、析构函数、协议代理、协议存根、复制/移动 函数,以及与就地缓冲区之间的转换。
  • 客户端通过对提供的存根进行子类化来执行协议调度,并 为每项操作实现虚拟方法。
  • 异步和同步客户端均受支持。不过,异步客户端 不是线程安全的。
  • 需要 C++14 或更高版本。

如需开始使用,请参阅 HLCPP 教程

摘要

类别 包含线型的新 C++ 使用自然类型的新 C++ 高阶 C++
受众群体 驱动程序和性能关键型应用 高级服务 高级服务
抽象开销 手柄的 RAII 关闭 1 堆分配、构建、销毁 堆分配、构建、销毁
类型安全类型 枚举、结构体、联合、句柄、协议 枚举、结构体、联合、句柄、协议 枚举、结构体、联合、句柄、协议
存储空间 堆栈、用户提供的缓冲区或堆
lifecycle 手动或自动释放 自动释放 (RAII) 自动释放 (RAII)
接收行为 就地解码 解码为堆 解码然后移至堆
发送行为 复制或矢量化 复制 复制
调用协议方法 免费函数或代理 免费函数或代理 通过代理调用、注册回调
实现协议方法 手动调度或实现桩接口 实现桩接口 实现桩对象、调用回调
异步客户端
异步服务器 yes(无界限)2 是(无限制)[^2] yes(无界限)
并行服务器调度 3 是 [^3]
生成的代码占用空间

  1. 生成的类型拥有所有内嵌存储的标识名。非在线标识名 例如包含 对象会消失。在这些情况下,这些绑定提供了一个 fidl::DecodedValue 对象来管理与通话相关联的所有句柄。

  2. lib/fidl 中定义的绑定库可以 通过 fidl::BindServer 分派无限数量的进行中事务 定义于 lib/fidl/cpp/wire/channel.h.

  3. 绑定库 lib/fidl 支持并行 EnableNextDispatch() API 进行调度, lib/fidl/cpp/wire/async_transaction.h.