比较新的 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
字符串:128 自然色 std::string
金属丝 fidl::StringView
HLCPP std::string
矢量<字节>:64000 自然色 std::vector<uint8_t>
金属丝 fidl::VectorView<uint8_t>
HLCPP std::vector<uint8_t>
Protocol Store C++ ::examples_keyvaluestore_baseline::Store 一种标记类型,包含一些有关协议的信息
HLCPP ::examples::keyvaluestore::baseline::Store 包含协议中的方法的抽象基类
client_end:Store C++ fidl::ClientEnd<Store>
HLCPP fidl::InterfaceHandle<Store>
server_end:Store C++ fidl::ServerEnd<商店>
HLCPP fidl::InterfaceRequest<存储>
适用于存储协议的
客户端和服务器类型
自然色 客户端: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::vectorstd::optionalstd::string)表示数据结构。
  • 使用智能指针管理堆分配的对象。
  • 请使用 zx::handle 管理标识名所有权。
  • 可以在其传输线(例如 fidl::StringView)和自然类型表示形式(例如 std::string)之间转换数据。

电线类型

  • 经过优化,可以满足低级别系统编程的需求,同时提供比 C 绑定略多的安全性和功能。
  • 表示内存布局与传输格式一致的数据结构,即满足 C++ 标准布局的要求。这样便打开了就地编码和解码的大门。
  • 生成的结构是底层缓冲区的视图;它们没有内存。
  • 支持就地访问 FIDL 消息。
  • 对内存分配进行精细控制。
  • 使用自有句柄类型,例如 zx::handle。请注意,由于生成的结构是底层缓冲区的视图,因此父结构只有在也拥有底层缓冲区的情况下才会拥有子句柄。例如,FIDL 结构体拥有以内嵌方式存储的所有句柄,但包含句柄的结构体的 FIDL 矢量将表示为矢量视图,该视图不会拥有外行句柄。

客户端和服务器 API

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

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

高级 C++ 绑定

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

请参阅 HLCPP 教程开始使用。

摘要

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

  1. 生成的类型拥有以内嵌方式存储的所有句柄。当指针的包含对象消失时,外加的句柄(例如,指针间接后面的句柄)不会关闭。在这些情况下,绑定将提供一个 fidl::DecodedValue 对象来管理与调用关联的所有句柄。

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

  3. 绑定库 lib/fidl 支持使用 lib/fidl/cpp/wire/async_transaction.h 中定义的 EnableNextDispatch() API 进行并行调度。