比較新的 C++ 和高階 C++ 語言繫結

快速參考

以下說明如何判斷 C++ 程式碼中的特定類型/函式/識別符是否屬於新的 C++ 繫結或高階 C++ 繫結。

examples.keyvaluestore.baseline 程式庫為例:

library examples.keyvaluestore.baseline;

type Item = struct {
    key string:128;
    value vector<byte>:64000;
};

type WriteError = flexible enum {
    UNKNOWN = 0;
    INVALID_KEY = 1;
    INVALID_VALUE = 2;
    ALREADY_EXISTS = 3;
};

@discoverable
open protocol Store {
    /// Writes an item to the store.
    flexible WriteItem(struct {
        attempt Item;
    }) -> () error WriteError;
};

以下說明各種 FIDL 元素在 C++ 繫結中如何對應。請注意,表格中的「C++」是指新的 C++ 繫結,同樣適用於自然網域物件和線路網域物件。「Natural」是指新 C++ 繫結中的自然網域物件。「Wire」是指新 C++ 繫結中的線路網域物件。

FIDL 元素 C++ 自然類型 留言
標頭包含 C++ #include <fidl/examples.keyvaluestore.baseline/cpp/fidl.h> 格式為 `fidl/library name/cpp/fidl.h`
HLCPP #include <examples/keyvaluestore/baseline/cpp/fidl.h> 格式為「以斜線分隔的程式庫名稱/cpp/fidl.h」
程式庫 C++ ::examples_keyvaluestore_baseline New C++ 使用單一層級命名空間。
HLCPP 使用巢狀命名空間。
HLCPP ::examples::keyvaluestore::baseline
項目結構體 自然 ::examples_keyvaluestore_baseline::Item 除了命名空間差異之外,線路類型會在「::wire」下方巢狀排列。
電線 ::examples_keyvaluestore_baseline::wire::Item
HLCPP ::examples::keyvaluestore::baseline::Item
WriteError 列舉 自然 ::examples_keyvaluestore_baseline::WriteError 除了命名空間差異之外,線路類型會在「::wire」下方巢狀。
就列舉和位元而言,線路類型和自然類型是等同的。只是多了一個型別別名。
電線 ::examples_keyvaluestore_baseline::wire::WriteError
HLCPP ::examples::keyvaluestore::baseline::WriteError
string:128 自然 std::string
電線 fidl::StringView
HLCPP std::string
vector<byte>:64000 自然 std::vector<uint8_t>
電線 fidl::VectorView<uint8_t>
HLCPP std::vector<uint8_t>
通訊協定儲存庫 C++ ::examples_keyvaluestore_baseline::Store 標記類型,可攜帶一些關於通訊協定資訊
HLCPP ::examples::keyvaluestore::baseline::Store 包含通訊協定中方法的抽象基本類別
client_end:Store C++ fidl::ClientEnd<Store>
HLCPP fidl::InterfaceHandle<Store>
server_end:Store C++ fidl::ServerEnd<Store>
HLCPP fidl::InterfaceRequest<Store>

Store 通訊協定的用戶端和伺服器類型
自然 用戶端:fidl::Client<Store>
同步用戶端:fidl::SyncClient<Store>
伺服器介面:fidl::Server<Store>
事件處理常式介面:fidl::EventHandler<Store>
電線 用戶端:fidl::WireClient<Store>
同步用戶端:fidl::WireSyncClient<Store>
伺服器介面:fidl::WireServer<Store>
事件處理常式介面:fidl::WireEventHandler<Store>
HLCPP 用戶端:fidl::InterfacePtr<Store>
同步用戶端:fidl::SynchronousInterfacePtr<Store>
伺服器介面:Store
事件處理常式介面:不適用。InterfacePtr 具有 setter,可為每個事件宣告使用一個回呼。

以下是設定用戶端最常見的方式:

C++ (自然)

  // Connect to the protocol inside the component's namespace. This can fail so it's wrapped in a
  // |zx::result| and it must be checked for errors.
  zx::result client_end = component::Connect<examples_canvas_baseline::Instance>();
  if (!client_end.is_ok()) {
    FX_LOGS(ERROR) << "Synchronous error when connecting to the |Instance| protocol: "
                   << client_end.status_string();
    return -1;
  }

  // Create an instance of the event handler.
  EventHandler event_handler(loop);

  // Create an asynchronous client using the newly-established connection.
  fidl::Client client(std::move(*client_end), dispatcher, &event_handler);
  FX_LOGS(INFO) << "Outgoing connection enabled";

C++ (Wire)

  // Connect to the protocol inside the component's namespace. This can fail so it's wrapped in a
  // |zx::result| and it must be checked for errors.
  zx::result client_end = component::Connect<examples_canvas_baseline::Instance>();
  if (!client_end.is_ok()) {
    FX_LOGS(ERROR) << "Synchronous error when connecting to the |Instance| protocol: "
                   << client_end.status_string();
    return -1;
  }

  // Create an instance of the event handler.
  EventHandler event_handler(loop);

  // Create an asynchronous client using the newly-established connection.
  fidl::WireClient client(std::move(*client_end), dispatcher, &event_handler);
  FX_LOGS(INFO) << "Outgoing connection enabled";

請參閱畫布範例,查看完整程式碼清單和說明。

以下是實作伺服器最常見的方式:

C++ (自然)

// An implementation of the |Instance| protocol.
class InstanceImpl final : public fidl::Server<examples_canvas_baseline::Instance> {
  void AddLine(AddLineRequest& request, AddLineCompleter::Sync& completer) override {
    // ...
  }
};

C++ (Wire)

// An implementation of the |Instance| protocol.
class InstanceImpl final : public fidl::WireServer<examples_canvas_baseline::Instance> {
  void AddLine(AddLineRequestView request, AddLineCompleter::Sync& completer) override {
    // ...
  }
};

請參閱畫布範例,查看完整程式碼清單和說明。

新的 C++ 繫結

新的 C++ 繫結可提供兩種產生的網域物件系列,以及相應的用戶端和伺服器 API,因此可支援低階和高階用途。

自然類型

  • 經過最佳化,可滿足高階服務程式設計的需求。
  • 使用慣用 C++ 類型 (例如 std::vectorstd::optionalstd::string) 表示資料結構。
  • 使用智慧型指標管理堆積分配的物件。
  • 使用 zx::handle 管理句柄擁有權。
  • 可在線路 (例如 fidl::StringView) 和自然類型表示法 (例如 std::string) 之間轉換資料。

線路類型

  • 經過最佳化處理,可滿足低階系統程式設計的需求,同時提供比 C 繫結功能更安全的功能。
  • 代表記憶體配置與線路格式相符的資料結構,也就是符合 C++ 標準配置。這會開啟原地編碼和解碼的門戶。
  • 產生的結構體是底層緩衝區的檢視畫面,不具備記憶體。
  • 支援 FIDL 訊息的當地存取功能。
  • 提供精細的記憶體配置控管機制。
  • 使用自有帳號類型,例如 zx::handle。請注意,由於產生的結構體是底層緩衝區的檢視畫面,因此父項結構體只有在擁有底層緩衝區時,才會擁有子項句柄。舉例來說,FIDL 結構體會擁有內嵌儲存的所有句柄,但包含句柄的結構體 FIDL 向量會以向量檢視畫面表示,不會擁有離線句柄。

用戶端和伺服器 API

  • 與 C 繫結相比,程式碼產生器會產生更多程式碼。這包括建構函式、解構函式、複製/移動函式、網域物件系列之間的轉換、通訊協定用戶端實作項目,以及純虛擬伺服器介面。
  • 使用者可透過子類別化提供的伺服器介面,並覆寫每個作業的純虛擬方法,實作伺服器。
  • 支援同步和非同步呼叫,以及同步和非同步事件處理的用戶端。
  • 需要 C++17 以上版本。

請參閱新版 C++ 教學課程,瞭解如何開始使用。

高階 C++ 繫結

  • 經過最佳化,可滿足高階服務程式設計的需求。
  • 使用慣用 C++ 類型 (例如 std::vectorstd::optionalstd::string) 表示資料結構。
  • 使用智慧指標管理堆積分配的物件。
  • 使用 zx::handle (libzx) 管理句柄擁有權。
  • 可將資料從原地 FIDL 緩衝區轉換為慣用堆積分配的物件。
  • 可將資料從慣用堆積分配物件 (例如 std::string) 轉換為原地緩衝區 (例如 fidl::StringView)。
  • 與 C 繫結相比,程式碼產生器會產生更多程式碼。這包括建構函式、解構函式、通訊協定 Proxy、通訊協定 Stub、複製/移動函式,以及從/至原地緩衝區的轉換。
  • 用戶端會透過子類別化提供的存根目錄,並為每個作業實作虛擬方法,執行通訊協定調度作業。
  • 支援非同步和同步用戶端。不過,非同步用戶端並非安全執行緒。
  • 需要 C++14 以上版本。

摘要

類別 使用線路類型的新 C++ 含有自然型別的新 C++ 高階 C++
目標對象 驅動程式和效能至關重要的應用程式 高階服務 高階服務
抽象額外負擔 RAII 關閉句柄 1 堆積分配、建構、銷毀 堆積分配、建構、銷毀
類型安全類型 列舉、結構體、聯集、句柄、通訊協定 列舉、結構體、聯集、句柄、通訊協定 列舉、結構體、聯集、句柄、通訊協定
storage 堆疊、使用者提供的緩衝區或堆積 堆積 堆積
lifecycle 手動或自動免費 自動免費 (RAII) 自動免費 (RAII)
接收行為 原地解碼 將解碼結果解碼至堆積 解碼,然後移至堆積
傳送行為 複製或向量化 複製 複製
呼叫通訊協定方法 自由函式或 Proxy 自由函式或 Proxy 透過 Proxy 呼叫、註冊回呼
實作通訊協定方法 手動調度或實作 Stub 介面 實作 Stub 介面 實作 Stub 物件,叫用回呼
非同步用戶端
非同步伺服器 是 (無上限) 2 是 (無上限) [^2] 是 (無上限)
平行伺服器調度 3 是 [^3] no
產生的程式碼足跡 large large large

  1. 產生的類型會擁有內嵌儲存的所有句柄。離線處理常式 (例如指標指向方向後方的常式) 不會在指標的包含物件消失時關閉。在這種情況下,繫結會提供 fidl::DecodedValue 物件,用於管理與呼叫相關聯的所有處理常式。

  2. lib/fidl 中定義的繫結程式庫可透過 lib/fidl/cpp/wire/channel.h 中定義的 fidl::BindServer,調度無限數量的執行中交易。

  3. 繫結程式庫 lib/fidl 可使用 lib/fidl/cpp/wire/async_transaction.h 中定義的 EnableNextDispatch() API 啟用並行調度功能。