库
假设有如下库声明:
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:P 、server_end:<P, optional> |
fidl::InterfaceRequest |
client_end:P 、client_end:<P, optional> |
fidl::InterfaceHandle |
zx.Handle 、zx.Handle:optional |
zx::handle |
zx.Handle:S 、zx.Handle:<S, optional> |
使用相应的 zx 类型。例如,zx::vmo 或 zx::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
还包含以下关联的生成值:
ColorPtr
:unique_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() const
和bool is_string_value() const
:每个变体都有一个关联的方法,用于检查JsonValue
的实例是否属于该变体const int32_t& int_value() const
和const 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
还包含以下关联的生成值:
JsonValuePtr
:unique_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::kUnknown
,JsonValue::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() const
和bool has_name() const
:返回是否设置了字段。const uint8_t& age() const
和const 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
是 resource 类型,它将使用以下方法:
const std::map<uint64_t, fidl::UnknownData>>& UnknownData() const
:返回从序数到字节的映射和句柄。句柄保证按遍历顺序。void SetUnknownDataEntry(uint32_t ordinal, fidl::UnknownData&& data)
:如果未知字段尚不存在,则设置该字段的字节和句柄。此方法应仅用于测试,例如检查包含未知字段的表是否已正确处理。
如果 User
是 value 类型,则它将使用以下方法:
const std::map<uint64_t, vector<uint8_t>& UnknownData() const
:返回从序数到字节的映射。void SetUnknownDataEntry(uint32_t ordinal, vector<uint8_t>&& data)
:如果未知字段尚不存在,则设置该字段的字节。此方法应仅用于测试,例如检查包含未知字段的表是否已得到正确处理。
User
还包含以下关联的生成值:
UserPtr
:unique_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
包含以下成员类型:
MakeMoveCallback
和OnOpponentMoveCallback
:每个响应和事件都有一个生成的成员类型,该类型表示用于处理该响应或事件的回调的类型。在上面的示例中,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>
表示同步客户端)。
解引用后,TicTacToePtr
和 TicTacToeSyncPtr
会返回一个分别实现 TicTacToe
和 TicTacToe_Sync
的代理类,用于代理向服务器发送的请求。在此示例中,假设有一个名为 async_tictactoe
的 TicTacToePtr
,可以通过调用 async_tictactoe->StartGame(start_first)
或 async_tictactoe->MakeMove(row,
col, callback)
发出请求。
HLCPP 教程中介绍了如何设置 InterfacePtr
或 SynchronousInterfacePtr
并将其绑定到通道。
fidl::InterfacePtr
类型为线程恶意。对此类型的实例的所有调用都必须从同一线程进行。fidl::SynchronousInterfacePtr
类型是线程兼容的。绑定此类型的实例后,您便可以从多个线程同时使用它。fidl::InterfaceHandle
类型可用于在线程之间安全地传输通道句柄。如需了解详情,请参阅有关这些类型的类文档。
服务器
为 FIDL 协议实现服务器需要提供 TicTacToe
的具体实现。
HLCPP 教程中提供了有关如何设置和绑定服务器实现的示例。
事件
客户端
对于 TicTacToePtr
tictactoe
,tictactoe.events()
会返回包含以下公共成员的代理类:
OnOpponentMoveCallback OnOpponentMove
:OnOpponentMove
事件的回调处理程序。
客户端可以通过将此类的成员设置为所需的事件处理脚本来处理事件。
如需详细了解回调类型,请参阅生成的顶级协议代码。
服务器
对于 Binding<TicTacToe>
tictactoe
,tictactoe.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
是错误类型,在MakeMove
和DryRunMove
的示例中为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_Result
的 response
变体的类型。此类被视为 FIDL 结构体,其字段与成功响应的每个参数相对应。除了常规结构体可用的方法和成员之外,TicTacToe_MakeMove_Response
还提供了允许与 std::tuple
进行互操作的其他方法:
explicit TicTacToe_MakeMove_Response(std::tuple<GameState> _value_tuple)
:元组的构造函数。operator std::tuple<GameState>() &&
:元组的转换运算符。
未知互动处理
服务器端
将协议声明为 open
或 ajar
时,生成的接口类(例如 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;
实现 open
或 ajar
服务器时,您还必须实现此方法。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
错误,则表示服务器无法识别该方法。
对于 strict
和 flexible
单向方法和事件,发送单向方法和接收事件的 API 相同。
对于 open
和 ajar
协议,生成的 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
为虚拟协议方法 StartGame
和 MakeMove
提供了实现,这两种方法实现时分别只调用 NotImplemented_("StartGame")
和 NotImplemented_("MakeMove")
。
扩展程序
fostr
是一个独立的库,提供用于在 HLCPP 中设置(美观输出)FIDL 类型格式的实用程序。您可以在教程中找到使用情况信息。