HLCPP 绑定

假设有以下库声明:

library fuchsia.examples;

此库的所有代码均在 fuchsia::examples 命名空间中生成,测试框架则在 fuchsia::examples::testing 中生成。

常量

所有常量均生成为 constexpr。例如,以下常量:

const BOARD_SIZE uint8 = 9;
const NAME string = "Tic-Tac-Toe";

在头文件中生成为:

constexpr uint8_t BOARD_SIZE = 9u;
extern const char[] NAME;

内置类型中概述了 FIDL 基元类型与 C++ 类型之间的对应关系。字符串在头文件中声明为 extern const char[],而不是 constexpr,并在 .cc 文件中进行定义。

字段

本部分介绍了 FIDL 工具链如何在 HLCPP 中将 FIDL 类型转换为原生类型。这些类型可以作为汇总类型中的成员或作为协议方法的参数出现。

内置类型

FIDL 类型会根据下表转换为 C++ 类型:

FIDL 类型 HLCPP 类型
bool bool
int8 int8_t
int16 int16_t
int32 int32_t
int64 int64_t
uint8 uint8_t
uint16 uint16_t
uint32 uint32_t
uint64 uint64_t
float32 float
float64 double
array<T, N> std::array
vector<T>:N std::vector
vector<T>:<N, optional> fidl::VectorPtr
string std::string
string:optional fidl::StringPtr
server_end:Pserver_end:<P, optional> fidl::InterfaceRequest
client_end:Pclient_end:<P, optional> fidl::InterfaceHandle
zx.Handlezx.Handle:optional zx::handle
zx.Handle:Szx.Handle:<S, optional> 系统会使用相应的 zx 类型。例如,zx::vmozx::channel

用户定义的类型

在 HLCPP 中,绑定中使用生成的类或变量引用用户定义的类型(位、枚举、常量、结构体、联合体或表)(请参阅类型定义)。对于可为 null 的用户定义类型 T,系统会使用等效生成类型的 unique_ptr

请求、响应和事件参数

每当 FIDL 需要生成一个表示请求、响应或事件参数的单一类型时(例如,生成 fpromise::result 兼容的结果类型时),它都会使用以下规则:

  • 多个实参会生成为参数类型的 std::tuple
  • 只需使用参数类型本身即可引用单个参数。
  • 空参数集使用 void 表示。

类型定义

给定定义:

type FileMode = strict bits : uint16 {
    READ = 0b001;
    WRITE = 0b010;
    EXECUTE = 0b100;
};

FIDL 工具链使用指定的底层类型生成 C++ enum class,如果未指定任何类型,则生成 uint32_t

enum class FileMode : uint16_t {
    READ = 1u;
    WRITE = 2u;
    EXECUTE = 4u;
};

此外,FIDL 会为 FileMode 生成以下方法:

  • 按位运算符:生成 ||=&&=^^=~ 运算符的实现,以允许对 mode |= FileMode::EXECUTE 等位进行按位运算。

FIDL 还会生成一个 const static FileMode FileModeMask 变量。这是一个位掩码,包含枚举类中的所有位,可用于从原始底层 uint16_t(或 bits 基于的任何类型)中移除所有未使用的位值。在上面的示例中,FileModeMask 的值为 0b111

用法示例:

auto flags = fuchsia::examples::FileMode::READ | fuchsia::examples::FileMode::WRITE;
ASSERT_EQ(static_cast<uint16_t>(flags), 0b11);
flags |= fuchsia::examples::FileMode::EXECUTE;
ASSERT_EQ(flags, fuchsia::examples::FileModeMask);

灵活位

灵活位是作为 class(而非 enum class)实现的,并提供了以下额外的方法:

  • constexpr FileMode():默认构造函数,用于将值初始化为未设置任何位。
  • constexpr FileMode(uint16_t):根据底层基元值构造值,保留所有未知的位成员。
  • constexpr cpp17::optional<FileMode> TryFrom(uint16_t value):如果值不包含任何未知成员,则从底层基元值构造位的实例;否则,返回 cpp17::nullopt
  • constexpr FileMode TruncatingUnknown(uint16_t value):从底层基元值构造位实例,清除所有未知成员。
  • constexpr FileMode unknown_bits() const:返回仅包含此位值中的未知成员的位值。
  • constexpr bool has_unknown_bits() const:返回此值是否包含任何未知位。
  • explicit constexpr operator uint16_t() const:将位值转换回其底层基元值。
  • explicit constexpr operator bool() const:返回是否设置了任何位。

生成的类包含每个位成员以及位掩码的静态数字。这些成员与 enum class 值的成员完全对应,此外还添加了一个替换 FileModeMaskkMask 成员。

  • const static FileMode READ
  • const static FileMode WRITE
  • const static FileMode EXECUTE
  • const static FileMode kMask

枚举

给定以下枚举定义:

type LocationType = strict enum {
    MUSEUM = 1;
    AIRPORT = 2;
    RESTAURANT = 3;
};

FIDL 工具链使用指定的底层类型生成 C++ enum class,如果未指定任何类型,则生成 uint32_t

enum class LocationType : uint32_t {
    MUSEUM = 1u;
    AIRPORT = 2u;
    RESTAURANT = 3u;
};

用法示例:

ASSERT_EQ(static_cast<uint32_t>(fuchsia::examples::LocationType::MUSEUM), 1u);

灵活枚举

灵活枚举通过以下方法实现为 class,而不是 enum class

  • constexpr LocationType():默认构造函数,用于将枚举初始化为未指定的未知值。
  • constexpr LocationType(uint32_t value):接受枚举基础类型值的显式构造函数。
  • constexpr bool IsUnknown():返回枚举值是否未知。
  • constexpr static LocationType Unknown():返回一个枚举值,保证会被视为未知。如果枚举中有带有 [Unknown] 注解的成员,则会返回该成员的值。如果没有这样的成员,则返回的枚举成员的底层值未指定。
  • explicit constexpr operator int32_t() const:将枚举转换回其底层值

生成的类包含每个枚举成员的静态成员,这些成员保证与等效严格枚举中的 enum class 成员匹配:

  • const static LocationType MUSEUM
  • const static LocationType AIRPORT
  • const static LocationType RESTAURANT

结构体

假设有以下struct声明:

type Color = struct {
    id uint32;
    @allow_deprecated_struct_defaults
    name string:MAX_STRING_LENGTH = "red";
};

FIDL 工具链会生成具有公共成员和方法的 Color 类型。

  • public 位成员:

    • uint32_t id{}:由于未提供默认值,因此此字段会被初始化为零。
    • std::string name = "red"name 对应的字段。
  • 方法:

    • static inline std::unique_ptr<Color> New():将 unique_ptr 返回到新的 Color

Color 的 6 个特殊成员(默认、复制和移动构造函数、析构函数、复制和移动赋值)是隐式定义的。

Color 还有以下关联的生成值:

  • ColorPtrunique_ptr<Color> 的别名。

如果结构体代表结果的响应变体,则可能具有其他成员。

用法示例:

fuchsia::examples::Color default_color;
ASSERT_EQ(default_color.id, 0u);
ASSERT_EQ(default_color.name, "red");

fuchsia::examples::Color blue = {1, "blue"};
ASSERT_EQ(blue.id, 1u);

联合体

给定联合体定义:

type JsonValue = strict union {
    1: int_value int32;
    2: string_value string:MAX_STRING_LENGTH;
};

FIDL 会生成一个 JsonValue 类。JsonValue 包含一个公共标记枚举,表示可能的变体

enum Tag : fidl_xunion_tag_t {
  kIntValue = 2,
  kStringValue = 3,
  Invalid = std::numeric_limits<fidl_xunion_tag_t>::max(),
};

Tag 的每个成员都有一个值,该值与 union 定义中指定的其 ordinal 相匹配。此外,还有一个 Invalid 字段,它是尚未设置变体的 JsonValue 使用的初始值。

JsonValue 提供了以下方法:

  • JsonValue():默认构造函数。该标记最初为 Tag::Invalid,直到 JsonValue 设置为特定变体。应尽可能使用 WithFoo 构造函数。
  • ~JsonValue():默认析构函数
  • static JsonValue WithIntValue(int32&&)static JsonValue WithStringValue(std::string&&):直接构建联合体的特定变体的静态构造函数。
  • static inline std::unique_ptr<JsonValue> New():将 unique_ptr 返回到新的 JsonValue
  • bool has_invalid_tag():如果 JsonValue 的实例尚未设置变体,则返回 true。在设置变体之前,用户不应访问联合体,否则应被视为未定义的行为。
  • bool is_int_value() constbool is_string_value() const:每个变体都有一个关联的方法,用于检查 JsonValue 的实例是否属于该变体
  • const int32_t& int_value() constconst std::string& string_value() const:每个款式/规格的只读访问器方法。如果 JsonValue 未设置指定的变体,这些方法将失败
  • int32_t& int_value()std::string& string_value():每个变体的可变访问器方法。如果 JsonValue 的变体与调用的访问器方法不同,则会销毁其当前数据,并将其重新初始化为指定的变体。
  • JsonValue& set_int_value(int32_t)JsonValue& set_string_value(std::string):每个变体的 setter 方法。
  • Tag Which() const:返回 JsonValue 的当前标记
  • fidl_xunion_tag_t Ordinal() const:返回原始 fidl_xunion_tag_t 标记。除非需要原始序数,否则最好使用 Which()

JsonValue 还有以下关联的生成值:

  • JsonValuePtrunique_ptr<Foo> 的别名。

如果联合体表示结果的响应变体,则可能具有其他方法。

用法示例:

auto int_val = fuchsia::examples::JsonValue::WithIntValue(1);
ASSERT_EQ(int_val.Which(), fuchsia::examples::JsonValue::Tag::kIntValue);
ASSERT_TRUE(int_val.is_int_value());

auto str_val = fuchsia::examples::JsonValue::WithStringValue("1");
ASSERT_EQ(str_val.Which(), fuchsia::examples::JsonValue::Tag::kStringValue);
ASSERT_TRUE(str_val.is_string_value());

fuchsia::examples::JsonValuePtr other_int_val = std::make_unique<fuchsia::examples::JsonValue>();
other_int_val->set_int_value(5);
ASSERT_EQ(other_int_val->int_value(), 5);

灵活的联合体和未知变体

灵活联合体在生成的 Tag 类中具有额外的变体:

enum Tag : fidl_xunion_tag_t {
    kUnknown = 0,
    ... // other fields omitted
};

当包含具有未知变体的联合体的 FIDL 消息解码为 JsonValue 时,JsonValue::Which() 会返回 JsonValue::Tag::kUnknownJsonValue::Ordinal() 会返回未知序数。

灵活的 JsonValue 类型将具有用于与未知数据交互的额外方法,具体取决于该类型是值类型还是资源类型。值类型不会有引用 zx::handle 的未知数据方法。

作为资源类型的灵活 JsonValue 具有以下额外方法:

  • const vector<uint8_t>* UnknownBytes() const:如果未知联合体变体,则返回其原始字节;否则,返回 nullptr
  • const vector<zx::handle>* UnknownHandles() const:如果未知,则返回遍历顺序中的联合变体的句柄;否则返回 nullptr
  • JsonValue& SetUnknownData(fidl_xunion_tag_t ordinal, vector<uint8_t> bytes, vector<zx::handle> handles):与已知成员的 setter 方法类似,此方法会将联合设置为具有指定序数、字节和句柄的未知变体。此方法仅应用于测试,例如确保代码可以正确处理未知数据。

作为类型的灵活 JsonValue 具有以下额外方法:

  • const vector<uint8_t>* UnknownBytes() const:如果未知联合体变体,则返回其原始字节;否则返回 nullptr
  • JsonValue& SetUnknownData(fidl_xunion_tag_t ordinal, vector<uint8_t> bytes):与已知成员的 setter 方法类似,此方法会将联合设置为具有指定序数和字节的未知变体。此方法应仅用于测试,例如确保代码可以正确处理未知数据。

对包含未知变体的联合编码会将未知数据和原始序数写回线路上。

在解码未知变体时,严格联合会失败。灵活类型联合体在使用句柄解码未知变体时会失败。

表格

给定定义:

type User = table {
    1: age uint8;
    2: name string:MAX_STRING_LENGTH;
};

FIDL 工具链会使用以下方法生成 User 类:

  • User():默认构造函数,使用所有字段未设置进行初始化。
  • User(User&&):移动构造函数。
  • ~User():析构函数。
  • User& User::operator=(User&&):移动作业。
  • bool IsEmpty() const:如果未设置任何字段,则返回 true。
  • bool has_age() constbool has_name() const:返回字段是否已设置。
  • const uint8_t& age() constconst std::string& name() const:只读字段访问器方法。如果未设置该字段,这些操作将会失败。
  • uint8_t* mutable_age()std::string* mutable_name():可变字段访问器方法。如果未设置该字段,系统将构建、设置并返回默认字段。
  • User& set_age(uint8_t)User& set_name(std::string):字段 setter。
  • void clear_age()void clear_name():通过调用字段的析构函数来清除字段的值

User 类还将提供用于与未知字段交互的方法,具体取决于类型是值还是资源类型。值类型的表不会包含引用 zx::handle 的未知数据方法,并且无法解码包含包含句柄的未知字段的数据。

如果 User资源类型,则它将具有以下方法:

  • const std::map<uint64_t, fidl::UnknownData>>& UnknownData() const:返回从序数到字节和句柄的映射。这些句柄保证是按遍历顺序排列的。
  • void SetUnknownDataEntry(uint32_t ordinal, fidl::UnknownData&& data):如果未知字段尚不存在,则设置其字节和句柄。此方法应仅用于测试,例如检查是否正确处理了包含未知字段的表。

如果 User类型,则它将具有以下方法:

  • const std::map<uint64_t, vector<uint8_t>& UnknownData() const:返回从序数到字节的映射。
  • void SetUnknownDataEntry(uint32_t ordinal, vector<uint8_t>&& data):如果未知字段尚不存在,则设置该字段的字节。此方法应仅用于测试,例如检查是否正确处理了包含未知字段的表。

User 还有以下关联的生成值:

  • UserPtrunique_ptr<User> 的别名。

用法示例:

fuchsia::examples::User user;
ASSERT_FALSE(user.has_age());
user.set_age(100);
*user.mutable_age() += 100;
ASSERT_EQ(user.age(), 200);
user.clear_age();
ASSERT_TRUE(user.IsEmpty());

内嵌布局

生成的 C++ 代码使用 fidlc 为内嵌布局预留的名称

协议

给定协议

closed protocol TicTacToe {
    strict StartGame(struct {
        start_first bool;
    });
    strict MakeMove(struct {
        row uint8;
        col uint8;
    }) -> (struct {
        success bool;
        new_state box<GameState>;
    });
    strict -> OnOpponentMove(struct {
        new_state GameState;
    });
};

FIDL 会生成一个 TicTacToe 类,该类充当与协议交互的入口点,并定义客户端用于代理对服务器的调用以及服务器用于实现协议的服务接口。同步客户端使用不同的虚拟接口 TicTacToe_Sync

TicTacToe 包含以下成员类型:

  • MakeMoveCallbackOnOpponentMoveCallback:每个响应和事件都会生成一个成员类型,该类型表示用于处理相应响应或事件的回调的类型。在上面的示例中,MakeMoveCallbackfit::function<void(bool, std::unique_ptr<GameState>)> 的别名,OnOpponentMoveCallbackfit::function<void(GameState)> 的别名。

TicTacToe 还具有以下纯虚方法,对应于协议定义中的方法:

  • virtual void StartGame(bool start_first):适用于“火花式”协议方法的纯虚方法。它将请求参数作为参数。
  • virtual void MakeMove(uint8_t row, uint8_t col, MakeMoveCallback callback):双向协议方法的纯虚方法。它接受请求参数和响应处理程序回调作为参数。

TicTacToe_Sync 具有以下纯虚方法,对应于协议定义中的方法:

  • virtual zx_status_t StartGame(bool start_first):火花协议方法的纯虚方法。它接受请求参数作为参数,并返回一个 zx_status_t,表示请求是否已成功发送。
  • virtual zx_status_t MakeMove(uint8_t row, uint8_t col, bool* out_success, std::unique_ptr<GameState>* out_new_state):双向方法协议的纯虚方法。它将请求参数作为参数,后跟每个响应参数的输出指针。它会返回一个 zx_status_t,表示方法调用是否成功。

系统可能会生成其他代码,具体取决于应用于该协议或其方法的属性

客户端

FIDL 工具链会为用于调用 TicTacToe 服务器的类生成两个别名:TicTacToePtr(别名为表示异步客户端的 fidl::InterfacePtr<TicTacToe>)和 TicTacToeSyncPtr(别名为表示同步客户端的 fidl::SynchronousInterfacePtr<TicTacToe>)。

解引用后,TicTacToePtrTicTacToeSyncPtr 会返回分别实现 TicTacToeTicTacToe_Sync 的代理类,这些类会将请求代理到服务器。在此示例中,假设有一个名为 async_tictactoeTicTacToePtr,则可以通过调用 async_tictactoe->StartGame(start_first)async_tictactoe->MakeMove(row, col, callback) 发出请求。

HLCPP 教程介绍了如何设置 InterfacePtrSynchronousInterfacePtr 并将其绑定到通道。

fidl::InterfacePtr 类型不支持线程。对此类实例的所有调用都必须从同一线程中进行。fidl::SynchronousInterfacePtr 类型与线程兼容。此类的实例绑定后,可同时从多个线程中使用。fidl::InterfaceHandle 类型可用于在线程之间安全地传输通道句柄。如需了解详情,请参阅介绍这些类型的类文档。

服务器

为 FIDL 协议实现服务器需要提供 TicTacToe 的具体实现。

HLCPP 教程中介绍了如何设置和绑定服务器实现的示例。

事件

客户端

对于 TicTacToePtr tictactoetictactoe.events() 会返回一个包含以下公共成员的代理类:

  • OnOpponentMoveCallback OnOpponentMoveOnOpponentMove 事件的回调处理脚本。

客户端可以通过将此类的成员设置为所需的事件处理脚本来处理事件。

如需详细了解回调类型,请参阅生成的顶级协议代码

服务器

对于 Binding<TicTacToe> tictactoetictactoe.events() 会返回一个桩类,其中包含以下公共成员:

  • void OnOpponentMove(GameState new_state):发送 OnOpponentMove

教程中提供了获取 Binding 的示例。

结果

假设有一个错误类型的方法、一个灵活方法或一个具有错误类型的灵活方法:

open protocol TicTacToe {
    strict MakeMove(struct {
      row uint8;
      col uint8;
    }) -> (struct {
      new_state GameState;
    }) error MoveError;

    flexible GetState() -> (struct {
      current_state GameState;
    });

    flexible DryRunMove(struct {
      row uint8;
      col uint8;
    }) -> (struct {
      new_state GameState;
    }) error MoveError;
};

FIDL 会生成代码,以便客户端和服务器可以使用 fpromise::result 来替代生成的响应类型。为此,需要生成一个结果类来表示可与 fpromise::result 互换的响应。

  • MakeMove
    • 生成类 TicTacToe_MakeMove_Result
    • 可与 fpromise::result<GameState, MoveError> 互换
  • GetState
    • 生成类 TicTacToe_GetState_Result
    • 可与 fpromise::result<GameState, fidl::FrameworkErr> 互换
  • DryRunMove

    • 生成类 TicTacToe_DryRunMove_Result
    • 可与 fpromise::result<GameState, std::variant<MoveError, fidl::FrameworkErr>> 互换

    使用此功能,服务器端这些方法的示例实现可能如下所示:

void MakeMove(uint8_t row, uint8_t col, MakeMoveCallback callback) override {
  std::optional<MoveError> error = ApplyMove(row, col);
  if (!error.has_value()) {
    callback(fpromise::ok(game_state_.state()));
  }
  callback(fpromise::error(error.value()));
}

void GetState(MakeMoveCallback callback) override {
  callback(fpromise::ok(game_state_.state()));
  // The server application code *must not* attempt to send a
  // fidl::FrameworkErr. If it does, the server binding will panic.
  }

void DryRynMove(uint8_t row, uint8_t col, MakeMoveCallback callback) override {
  std::optional<MoveError> error = TestMove(row, col);
  if (!error.has_value()) {
    callback(fpromise::ok(game_state_.state()));
  }
  // The server application code *must not* attempt to send a
  // fidl::FrameworkErr. If it does, the server binding will panic.
  callback(fpromise::error(error.value()));
}

在异步情况下,在客户端使用此方法的示例如下:

async_game->MakeMove([&](fpromise::result<GameState, MoveError> response) { ... });
async_game->GetState(
    [&](fpromise::result<GameState, fidl::FrameworkErr> response) { ... });
async_game->DryRunMove(
    [&](fpromise::result<GameState, std::variant<MoveError, fidl::FrameworkErr>> response) { ... });

在客户端,fidl::FrameworkErr 表示服务器不知道灵活的双向互动。

在生成代码时,FIDL 工具链会将 TicTacToe_*_Result 视为最多具有三个变体的 union

  • response 是一种生成的类型,遵循参数类型转换规则
    • 如果 MakeMove 在其结构体返回类型中返回单个参数,或者返回类型是元组或联合,则返回类型为 fpromise::result<T, ...>,其中 T 是结构体的单个参数,或元组或联合返回类型。
    • 如果 MakeMove 在成功时返回多个值,则结果类型将是响应参数 fpromise::result<std::tuple<...>, ...> 的元组
    • 如果 MakeMove 返回空响应,则结果类型为 fpromise::result<void, ...>
  • err 是错误类型,在 MakeMoveDryRunMove 示例中为 MoveError
    • 只有当方法使用错误语法时,才存在此变体。
  • framework_err 始终为 fidl::FrameworkErr 类型。
    • 只有当方法为 flexible 时,此变体才存在。

TicTacToe_*_Result 类型提供适用于常规联合体的所有方法。此外,TicTacToe_*_Result 类型还提供了允许与 fpromise::result 互操作的方法,例如对于 TicTacToe_MakeMove_Result

  • TicTacToe_MakeMove_Result(fpromise::result<GameState, MoveError>&& result):从 fpromise::result 移出构造函数。
  • TicTacToe_MakeMove_Result(fpromise::ok_result<GameState>&& result):从 fpromise::ok_result 移出构造函数。
  • TicTacToe_MakeMove_Result(fpromise::error_result<MoveError>&& result):从 fpromise::error_result 移出构造函数。
  • operator fpromise::result<GameState, MoveError>() &&:转换为 fpromise::result

其他 TicTacToe_*_Result 类型对应的 fpromise::result 类型也会有类似的转换。

FIDL 工具链还会生成一个 TicTacToe_MakeMove_Response 类,这是 TicTacToe_MakeMove_Resultresponse 变体的类型。此类被视为一个 FIDL 结构体,其中的字段与成功响应的每个参数相对应。除了常规结构体可用的那些方法和成员之外,TicTacToe_MakeMove_Response 还提供了其他方法,可与 std::tuple 进行互操作:

  • explicit TicTacToe_MakeMove_Response(std::tuple<GameState> _value_tuple):基于元组的构造函数。
  • operator std::tuple<GameState>() &&:元组的转换运算符。

未知互动处理

服务器端

将协议声明为 openajar 后,生成的接口类(例如 TicTacToe)将包含一个名为 handle_unknown_method 的额外虚拟方法,其签名如下:

// If the protocol is open:
virtual void handle_unknown_method(uint64_t ordinal, bool method_has_response) = 0;
// If the protocol is ajar:
virtual void handle_unknown_method(uint64_t ordinal) = 0;

实现 openajar 服务器时,您还必须实现此方法。ordinal 参数是所调用方法的 FIDL 方法序数。如果协议为 open,则 method_has_response 参数表示方法是单向还是双向;对于单向方法,method_has_response 为 false,对于双向方法,method_has_response 为 true。在 ajar 协议中,只能处理未知的单向方法。

每当服务器收到它可以处理的未知灵活方法时,都会调用 handle_unknown_method 方法。

客户端

客户端无法确定服务器是否知道 flexible 单向方法。对于 flexible 双向方法,可以使用结果联合来确定服务器是否知道该方法。如果设置了 TicTacToe_<Method>_Result 类的 framework_err 变体,或者转换后的 fpromise::result 设置了 fidl::FrameworkErr 错误,则表示服务器无法识别该方法。

用于发送单向方法和接收事件的 API 对 strictflexible 单向方法和事件相同。

对于 openajar 协议,生成的 TicTacToe_Proxy 类将有一个额外的公共字段,名为 handle_unknown_event,类型如下所示:

fit::function<void(uint64_t)> handle_unknown_event;

就像协议中声明的事件(例如 OnOpponentMove)的事件处理脚本一样,您可以在此处分配一个函数回调,以便在每次收到未知 flexible 事件时调用该回调。回调的单个 uint64_t 参数是事件的 FIDL 方法序数。

协议组成

FIDL 没有继承的概念,并会为所有复合协议生成上述完整代码。换句话说,为

protocol A {
    Foo();
};

protocol B {
    compose A;
    Bar();
};

提供与以下代码生成的代码相同的 API:

protocol A {
    Foo();
};

protocol B {
    Foo();
    Bar();
};

生成的代码除了方法序数外,完全相同。

协议和方法属性

过渡

对于带有 @transitional 属性注解的协议方法,协议类上的 virtual 方法不是纯方法。这样,缺少方法替换项的协议类实现便可成功编译。

可检测到

使用 @discoverable 属性注解的协议会导致 FIDL 工具链在协议类上生成一个额外的 static const char Name_[] 字段,其中包含完整的协议名称。对于库 foo.bar 中的协议 Baz,生成的名称为 "foo.bar.Baz"

测试脚手架

FIDL 工具链还会生成一个后缀为 _test_base.h 的文件,其中包含用于测试 FIDL 服务器实现的便捷代码。此文件包含每个协议的类,这些类为每个类的方法提供桩实现,从而实现仅实现测试期间使用的那些方法。这些类会生成到生成的库命名空间内的 testing 命名空间中(例如,对于库 games.tictactoe,这些类会生成到 games::tictactoe::testing 中)。

对于上面列出的同一 TicTacToe 协议,FIDL 工具链会生成一个子类为 TicTacToeTicTacToe_TestBase 类(请参阅协议),提供以下方法:

  • virtual ~TicTacToe_TestBase() {}:析构函数。
  • virtual void NotImplemented_(const std::string& name) = 0:纯虚方法,会被替换以定义未实现方法的行为。

TicTacToe_TestBase 为虚拟协议方法 StartGameMakeMove 提供了实现,这些方法的实现仅会分别调用 NotImplemented_("StartGame")NotImplemented_("MakeMove")

扩展程序

fostr 是一个单独的库,用于提供用于在 HLCPP 中设置格式(美化输出)FIDL 类型的实用程序。如需了解使用信息,请参阅教程