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++ 類型之間的對應列於內建類型中。系統會在標頭檔案中宣告字串為 extern const char[],並在 .cc 檔案內定義字串,而非 constexpr

欄位

本節說明 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 中,使用產生的類別或變數會在繫結中參照使用者定義類型 (位元、列舉、常數、結構、聯集或資料表 (請參閱類型定義)。針對可為空值的使用者定義類型 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 值的成員,並加上取代 FileModeMaskkMask 成員。

  • 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:將列舉值轉換回基礎值

產生的類別包含每個列舉成員的靜態成員,這些成員保證會與對等嚴格列舉中的 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 定義中指定的 ordinal 相符的值。此外,還有 Invalid 欄位,這是用於尚未設定變化版本的 JsonValue 的初始值。

JsonValue 提供以下方法:

  • JsonValue():預設建構函式。直到 JsonValue 設為特定變數為止,否則標記一開始會處於 Tag::Invalid。請盡可能使用 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 訊息解碼時,JsonValueJsonValue::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 方法類似,這會將聯集設為具有指定序數和位元組的不明變數。這個方法只應用於測試,例如確保程式碼可正確處理未知資料。

為含有不明變體的聯集編碼會將未知的資料,將原始序數寫回線路。

解碼不明變化版本時,Strict 聯集會失敗。使用帳號代碼解碼不明變數時,類型的彈性聯集會失敗。

桌子

根據資料表定義:

type User = table {
    1: reserved;
    2: age uint8;
    3: 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):欄位設定器。
  • 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):設定未知欄位的位元組和控點 (如果不存在)。此方法僅應用於測試,例如檢查有不明欄位的資料表是否正確處理。

如果 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 保留的名稱做為內嵌版面配置。

通訊協定

指定「通訊協定」

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 類別,該類別可做為與通訊協定互動的進入點,並定義用戶端用於 Proxy 呼叫伺服器的服務介面,以及用於實作通訊協定的伺服器。同步用戶端會使用不同的虛擬介面: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):適用於火災與 forget 通訊協定方法的純虛擬方法。並將要求參數視為引數。
  • 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 的 Proxy 類別,該類別會分別透過 Proxy 將要求傳送至伺服器。在本例中,如果有名為 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() 會傳回 Proxy 類別,其中包含下列公開成員:

  • 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 方法序數。如果通訊協定是 openmethod_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 設為子類別的 TicTacToe_TestBase 類別 (請參閱「通訊協定」一節),並提供下列方法:

  • virtual ~TicTacToe_TestBase() {}:解構器。
  • virtual void NotImplemented_(const std::string& name) = 0:純虛擬方法遭到覆寫,用於定義未實作方法的行為。

TicTacToe_TestBase 提供虛擬通訊協定方法 StartGameMakeMove 的實作,但實作方式分別僅呼叫 NotImplemented_("StartGame")NotImplemented_("MakeMove")

擴充功能

fostr 是獨立的程式庫,提供在 HLCPP 中格式化 (美化) FIDL 類型的公用程式。您可以在教學課程中找到使用資訊。