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

快速參考

以下說明如何辨識 C++ 程式碼中的特定型別/函式/ID 是否屬於新的 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++ 繫結,適用於自然網域物件和連線網域物件。「自然」是指新 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 新版 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
字串: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 具有設定器,每個事件宣告都會採用一個回呼。

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

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";

如需完整程式碼清單和說明,請參閱 canvas 範例。

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

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 {
    // ...
  }
};

如需完整程式碼清單和說明,請參閱 canvas 範例。

新的 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、通訊協定存根、複製/移動函式,以及轉換為/從就地緩衝區轉換。
  • 用戶端會透過子類別化提供的虛設常式,並為每個作業實作虛擬方法,執行通訊協定分派作業。
  • 系統支援非同步和同步用戶端。不過,非同步用戶端並非安全執行緒。
  • 需要 C++14 以上版本。

摘要

類別 使用線路型別的新 C++ 使用自然型別的新 C++ 高階 C++
目標對象 驅動程式和效能關鍵應用程式 高階服務 高階服務
抽象化額外負荷 RAII 關閉控制代碼 1 堆積分配量、建構、破壞 堆積分配量、建構、破壞
類型安全型別 列舉、結構體、聯集、控制代碼、通訊協定 列舉、結構體、聯集、控制代碼、通訊協定 列舉、結構體、聯集、控制代碼、通訊協定
儲存空間 堆疊、使用者提供的緩衝區或堆積 堆積 堆積
生命週期 手動或自動免費 自動免費 (RAII) 自動免費 (RAII)
接收行為 就地解碼 解碼為堆積 解碼,然後移至堆積
傳送行為 複製或向量化 複製 複製
呼叫通訊協定方法 免費函式或 Proxy 免費函式或 Proxy 透過 Proxy 呼叫、註冊回呼
實作通訊協定方法 手動調度或實作存根介面 實作 Stub 介面 實作虛設常式物件,叫用回呼
非同步用戶端
非同步伺服器 是 (無界限) 2 是 (無界) [^2] 是 (無界限)
平行伺服器調度 3 是 [^3] no
產生的程式碼足跡 large large large

  1. 產生的型別擁有所有內嵌儲存的控制代碼。當指標的包含物件消失時,系統不會關閉行外控制代碼,例如指標間接參照後方的控制代碼。在這些情況下,繫結會提供 fidl::DecodedValue 物件,用於管理與呼叫相關聯的所有控制代碼。 

  2. lib/fidl 中定義的繫結程式庫可透過 fidl::BindServer (定義於 lib/fidl/cpp/wire/channel.h) 傳送不限數量的進行中交易。

  3. 繫結程式庫 lib/fidl 可透過 lib/fidl/cpp/wire/async_transaction.h 中定義的 EnableNextDispatch() API 啟用平行分派。