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 工具鍊如何將 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

Structs

假設有以下 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 定義中指定的序數相符。此外,還有 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::kUnknown,而 JsonValue::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:每個回應和事件都會產生一個成員類型,代表用於處理該回應或事件的回呼類型。在上述範例中,MakeMoveCallback 別名為 fit::function<void(bool, std::unique_ptr<GameState>)>OnOpponentMoveCallback 別名為 fit::function<void(GameState)>

TicTacToe 還具有下列純虛擬方法,對應於通訊協定定義中的各個方法:

  • virtual void StartGame(bool start_first):用於 fire and 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,這些代理程式會將要求轉送至伺服器。在本例中,假設有一個名為 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() 會傳回包含下列公開成員的 Stub 類別:

  • 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 伺服器實作的便利程式碼。這個檔案包含每個通訊協定的類別,為每個類別的方法提供 Stub 實作項目,讓您只實作測試期間使用的各個方法。這些類別會產生至產生程式庫命名空間內的 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 類型。如需使用資訊,請參閱教學課程