HLCPP 绑定

假设有如下库声明:

library fuchsia.examples;

此库的所有代码都是在 fuchsia::examples 命名空间中生成的,而测试基架是在 fuchsia::examples::testing 中生成的。

常量

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

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

在头文件中生成如下:

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

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

字段

本部分介绍 FIDL 工具链如何将 FIDL 类型转换为 HLCPP 中的原生类型。这些类型可以在聚合类型中显示为成员,也可以显示为协议方法的参数。

内置类型

根据下表,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 表示。

类型定义

块数

根据 bits 定义:

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 值的成员完全一致,此外还添加了一个 kMask 成员来替换 FileModeMask

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

枚举

根据 enum 定义:

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:将枚举转换回其底层值

生成的类包含每个枚举成员的静态成员,这保证与等效 strict 枚举中的 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> 的别名。

如果结构体表示 result 的响应变体,则它们可能还有其他成员。

用法示例:

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: reserved;
    2: int_value int32;
    3: 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 定义中指定的序数相匹配的值。此外,还有一个 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> 的别名。

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

用法示例:

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(属于 resource 类型)具有以下 extra 方法:

  • 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 方法类似,此方法将并集设置为具有指定序数、字节和句柄的未知变体。此方法应仅用于测试,例如,确保代码可以正确处理未知数据。

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

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

对具有未知变体的并集进行编码,将未知数据和原始序数重新写入线上。

对未知变体进行解码时,严格并集会失败。 在解码具有句柄的未知变体时,属于类型的灵活并集会失败。

表格

根据表定义

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

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

  • User():默认构造函数,初始化时未设置所有字段。
  • User(User&&):Move 构造函数。
  • ~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 的未知数据方法,并且将无法对包含未知字段(包含句柄)的数据进行解码。

如果 Userresource 类型,它将使用以下方法:

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

如果 Uservalue 类型,则它将使用以下方法:

  • 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 预留的名称用于内嵌布局。

协议

根据 protocol

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:每个响应和事件都有一个生成的成员类型,该类型表示用于处理该响应或事件的回调的类型。在上面的示例中,MakeMoveCallback 别名 fit::function<void(bool, std::unique_ptr<GameState>)>OnOpponentMoveCallback 别名 fit::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()));
}

以异步方式在客户端使用此 API 的示例如下:

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,对于双向方法,该参数为 true。在 ajar 协议中,只能处理未知的单向方法。

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

客户端

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

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

对于 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 工具链会生成一个 TicTacToe_TestBase 类,该类是 TicTacToe 的子类(请参阅协议),提供以下方法:

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

TicTacToe_TestBase 为虚拟协议方法 StartGameMakeMove 提供了实现,这两种方法实现时分别只调用 NotImplemented_("StartGame")NotImplemented_("MakeMove")

扩展程序

fostr 是一个独立的库,提供用于在 HLCPP 中设置(美观输出)FIDL 类型格式的实用程序。您可以在教程中找到使用情况信息。