亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++?Protobuf的學(xué)習(xí)使用指南

 更新時間:2023年07月19日 14:08:51   作者:藍(lán)天巨人  
protocol?buffers是一種語言無關(guān)、平臺無關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)的方法,它可用于(數(shù)據(jù))通信協(xié)議、數(shù)據(jù)存儲等,下面就來跟隨小編一起簡單學(xué)習(xí)一下它的使用吧

簡介

protocol buffers 是一種語言無關(guān)、平臺無關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)的方法,它可用于(數(shù)據(jù))通信協(xié)議、數(shù)據(jù)存儲等。通信時所傳遞的信息是通過Protobuf定義的message數(shù)據(jù)結(jié)構(gòu)進(jìn)行打包,然后編譯成二進(jìn)制的碼流再進(jìn)行傳輸或者存儲。

Protocol buffers 是一種靈活,高效,自動化機(jī)制的結(jié)構(gòu)數(shù)據(jù)序列化方法-可類比 XML,但是比 XML 更?。? ~ 10倍)、更快(20 ~ 100倍)、更為簡單。

Protobuf 使用的時候必須寫一個 IDL(Interface description language)文件,在里面定義好數(shù)據(jù)結(jié)構(gòu),只有預(yù)先定義了的數(shù)據(jù)結(jié)構(gòu),才能被序列化和反序列化。其中,序列化是將對象轉(zhuǎn)換二進(jìn)制數(shù)據(jù),反序列化是將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成對象。

教程

本教程提供了一個基本的C++程序員介紹使用協(xié)議緩沖區(qū)。通過創(chuàng)建一個簡單的示例應(yīng)用程序,它向您展示了如何

  • 在.proto文件中定義消息格式。
  • 使用協(xié)議緩沖區(qū)編譯器。
  • 使用C++協(xié)議緩沖區(qū)API寫入和讀取消息。

這不是一個在C++中使用協(xié)議緩沖區(qū)的全面指南。有關(guān)更多詳細(xì)的參考信息,請參見Protocol Buffer Language Guide(proto2)、Protocol Buffer Language Guide(proto3)、C++ API Reference、C++ Generated Code Guide和Encoding Reference。

我們將要使用的示例是一個非常簡單的“地址簿”應(yīng)用程序,它 可以在文件中讀取和寫入人們的聯(lián)系方式。每個人在 地址簿具有姓名、ID、電子郵件地址和聯(lián)系電話 號碼。

如何序列化和檢索像這樣的結(jié)構(gòu)化數(shù)據(jù)?有幾個 解決這個問題的方法:

  • 原始存儲器中數(shù)據(jù)結(jié)構(gòu)可以以二進(jìn)制形式發(fā)送/保存。這是一種脆弱的方法,因為接收/閱讀代碼必須 使用完全相同的內(nèi)存布局、字節(jié)序等編譯。此外,作為 文件以原始格式積累數(shù)據(jù),而軟件的副本 這種格式的有線傳播,很難擴(kuò)展 格式。
  • 您可以發(fā)明一種特別的方法來將數(shù)據(jù)項編碼為單個 字符串-例如將4個int編碼為“12:3:-23:67”。這是一個簡單的和 靈活的方法,雖然它確實需要編寫一次性編碼和 解析代碼,并且解析施加小的運(yùn)行時成本。這樣最好 用于編碼非常簡單的數(shù)據(jù)。
  • 將數(shù)據(jù)序列化為XML。這種方法非常有吸引力,因為XML是 (sort的)人類可讀,并且有許多 語言。如果你想與其他人共享數(shù)據(jù),這是一個很好的選擇 應(yīng)用程序/項目。然而,XML是眾所周知的空間密集型,并且 編碼/解碼它可能對應(yīng)用施加巨大的性能損失。 此外,導(dǎo)航XMLDOM樹比 在類中導(dǎo)航簡單字段通常是這樣的。

您可以使用Protobuf來代替這些選項。協(xié)議緩沖區(qū)是 靈活、高效、自動化的解決方案,以準(zhǔn)確地解決這一問題。與 Protobuf,可以編寫數(shù)據(jù)結(jié)構(gòu)的.proto描述 希望儲存。Protobuf編譯器由此創(chuàng)建一個類,該類 實現(xiàn)Protobuf數(shù)據(jù)的自動編碼和解析 高效二進(jìn)制格式生成的類為 組成協(xié)議緩沖區(qū)的字段,并負(fù)責(zé) 作為一個單元閱讀協(xié)議緩沖器。重要的是,協(xié)議 緩沖區(qū)格式支持以這種方式隨時間擴(kuò)展格式的想法 代碼仍然可以讀取用舊格式編碼的數(shù)據(jù)。

示例代碼

包含在源代碼包中的 “examples”目錄

Defining Your Protocol Format

要創(chuàng)建地址簿應(yīng)用程序,您需要從.proto開始 文件。.proto文件中的定義很簡單:您為以下對象添加消息 要序列化的每個數(shù)據(jù)結(jié)構(gòu),然后為其指定名稱和類型 消息中的每個字段。下面是定義消息的.proto文件, addressbook.proto.

syntax = "proto2";
package tutorial;
message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
  message PhoneNumber {
    optional string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
  repeated PhoneNumber phones = 4;
}
message AddressBook {
  repeated Person people = 1;
}

正如你所看到的,它的語法類似于C++或Java。讓我們把每一部分都看一遍 看看它做了什么。

.proto文件以包聲明開始,這有助于防止不同項目之間的命名沖突。在C++中,生成的類將放置在與包名稱匹配的命名空間中。

接下來,您有了消息定義。消息包含一組類型化字段。有許多標(biāo)準(zhǔn)的簡單數(shù)據(jù)類型可用作為字段類型,包括bool、int32、float、doublestring。你呢 還可以通過使用其他消息類型為消息添加進(jìn)一步的結(jié)構(gòu) 字段類型-在上面的示例中,Person消息包含PhoneNumber AddressBook消息包含Person消息。你可以的 甚至定義嵌套在其他消息中的消息類型-正如您所看到的, PhoneNumber類型定義在Person內(nèi)。您還可以定義enum類型 如果您希望您的一個字段具有預(yù)定義值列表中的一個, 在這里,您希望指定電話號碼可以是下列電話之一 類型:MOBILE、HOMEWORK

每個元素上的“= 1”、“= 2”標(biāo)記標(biāo)識 字段在二進(jìn)制編碼中使用。字段號1-15需要少一個字節(jié) 編碼比更高的數(shù)字,所以作為一個優(yōu)化,你可以決定使用這些 通常使用的或重復(fù)的元件的編號,留下字段編號16和18。 對于不太常用的可選元素,則更高。中的每個元素 字段需要重新編碼字段編號,因此重復(fù)的字段尤其 這是優(yōu)化的好方法。

每個字段必須使用以下修飾符之一進(jìn)行注釋:

  • optional:字段可以設(shè)置,也可以不設(shè)置。如果是可選字段值 則使用默認(rèn)值。對于簡單類型,可以指定 自己的默認(rèn)值,就像我們在示例中對電話號碼type所做的那樣。 否則,將使用系統(tǒng)默認(rèn)值:數(shù)值類型為零,為空 字符串為字符串,布爾為假。對于嵌入的消息,默認(rèn)的 值始終是消息的“默認(rèn)實例”或“原型”,它 沒有設(shè)置任何字段。調(diào)用訪問器以獲取 未始終顯式設(shè)置的可選(或必需)字段 返回該字段的默認(rèn)值。
  • repeated:字段可以重復(fù)任何次數(shù)(包括零次)。 重復(fù)值的順序?qū)⒈A粼趨f(xié)議緩沖區(qū)中。 將重復(fù)字段視為動態(tài)大小的數(shù)組。
  • required:必須提供字段的值,否則消息 將被視為“未初始化”。如果libprotobuf在debug中編譯 模式下,序列化未初始化的消息將導(dǎo)致斷言失敗。 在優(yōu)化的生成中,將跳過檢查并寫入消息 不管怎樣。但是,解析未初始化的消息總是會失敗(通過 從parse方法返回false)。除此之外,必填字段 行為與可選字段完全相同。

Important

Required Is Forever 您應(yīng)該非常小心地將字段標(biāo)記為 required. 如果在某個時候 如果您希望停止編寫或發(fā)送必填字段,則 將字段更改為可選字段-老讀者將考慮消息 而該字段是不完整的,并且可能無意中拒絕或丟棄它們。 您應(yīng)該考慮為 你的緩沖器。在Google內(nèi)部 required fields are strongly disfavored; most messages defined in proto2 syntax use 領(lǐng)域受到強(qiáng)烈反對; proto 2語法中定義的大多數(shù)消息使用 optional and 和 repeated only. (Proto3 does not support 只有 (Proto3不支持 required fields at all.) 所有的領(lǐng)域)。

您將找到編寫.proto文件的完整指南-包括所有 可能的字段類型-在協(xié)議緩沖區(qū)語言指南。 不要去尋找類似于類繼承的工具- protocol 緩沖器不會這樣做。

Compiling Your Protocol Buffers編譯協(xié)議緩沖區(qū)

現(xiàn)在您已經(jīng)有了.proto,接下來您需要做的就是生成 你需要讀和寫的類AddressBook(因此Person和 PhoneNumber)消息。為此,您需要運(yùn)行協(xié)議緩沖區(qū) 編譯器protoc上的.proto

1.如果你還沒有安裝編譯器, 下載軟件包并按照在README中的說明。

2.現(xiàn)在運(yùn)行編譯器,指定源目錄(您的 應(yīng)用程序的源代碼仍然存在-如果您 不提供值),目標(biāo)目錄(您希望 生成代碼去;通常與$SRC_DIR相同),和路徑你的 .proto.在這種情況下,您……:

protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto

因為需要C++類,所以使用--cpp_out選項-類似 為其他支持的語言提供了選項。

這將在指定的目標(biāo)目錄中生成以下文件:

  • addressbook.pb.h, the header which declares your generated classes.
  • addressbook.pb.cc, which contains the implementation of your classes.

The Protocol Buffer API協(xié)議緩沖區(qū)API

讓我們看一下生成的代碼,看看編譯器為您創(chuàng)建了哪些類和函數(shù)。如果查看addressbook.pb.h,可以看到在addressbook.proto中指定的每個消息都有一個類。仔細(xì)觀察Person類,可以看到編譯器為每個字段生成了訪問器。例如,對于name、id、email和phones字段,可以使用以下方法:

// name
  inline bool has_name() const;
  inline void clear_name();
  inline const ::std::string& name() const;
  inline void set_name(const ::std::string& value);
  inline void set_name(const char* value);
  inline ::std::string* mutable_name();
  // id
  inline bool has_id() const;
  inline void clear_id();
  inline int32_t id() const;
  inline void set_id(int32_t value);
  // email
  inline bool has_email() const;
  inline void clear_email();
  inline const ::std::string& email() const;
  inline void set_email(const ::std::string& value);
  inline void set_email(const char* value);
  inline ::std::string* mutable_email();
  // phones
  inline int phones_size() const;
  inline void clear_phones();
  inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const;
  inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones();
  inline const ::tutorial::Person_PhoneNumber& phones(int index) const;
  inline ::tutorial::Person_PhoneNumber* mutable_phones(int index);
  inline ::tutorial::Person_PhoneNumber* add_phones();

正如您所看到的,getter的名稱與小寫的字段完全相同,而 setter方法以set_開始。也有has_方法 單數(shù)(必需或可選)字段,如果該字段已 準(zhǔn)備好了。最后,每個字段都有一個clear_方法,用于將字段恢復(fù)為 空的狀態(tài)。

雖然數(shù)字id字段僅具有上述基本訪問器集, nameemail字段有兩個額外的方法,因為它們 strings -一個mutable_ getter,讓你直接得到一個指向字符串的指針, 還有一只二傳手請注意,即使mutable_email()為 尚未設(shè)置;它將自動初始化為空字符串。如果你 如果在本例中有一個重復(fù)的消息字段,它也會有一個email 方法,而不是mutable_方法。

重復(fù)字段也有一些特殊的方法-如果您查看 重復(fù)的phones字段,您將看到

  • 檢查重復(fù)字段的_size(換句話說,有多少個電話號碼 Person2#2)。
  • 使用索引獲取指定的電話號碼。
  • 更新指定索引處的現(xiàn)有電話號碼。
  • 添加另一個電話號碼到消息,然后您可以編輯(重復(fù) 標(biāo)量類型有一個add_,它只是讓你傳入新值)。

有關(guān)協(xié)議編譯器為哪些成員生成的詳細(xì)信息 任何特定字段定義,請參見 C++生成代碼參考.

枚舉和嵌套類

生成的代碼包括一個與您的PhoneType對應(yīng)的.proto枚舉 枚舉。您可以將此類型稱為Person::PhoneType,其值為 Person::MOBILE、Person::HOMEPerson::WORK(實現(xiàn)細(xì)節(jié) 稍微復(fù)雜一點,但您不需要了解它們就可以使用 enum)。

編譯器還為您生成了一個名為 Person::PhoneNumber.如果你看一下代碼,你可以看到“真實的的” 類實際上被稱為Person_PhoneNumber,但內(nèi)部定義了一個typedef Person允許您將其視為嵌套類。唯一的案子 這會產(chǎn)生不同的地方是,如果你想在 另一個文件-你不能在C++中正向聲明嵌套類型,但你可以 forward-declare Person_PhoneNumber。

標(biāo)準(zhǔn)消息方法

每個消息類還包含許多其他方法,這些方法允許您檢查或 操縱整個消息,包括:

  • bool IsInitialized() const;:檢查是否已輸入所有必填字段 準(zhǔn)備好了。
  • string DebugString() const;:返回一個人類可讀的 消息,對調(diào)試特別有用。
  • void CopyFrom(const Person& from);:用給定的 消息的價值。
  • void Clear();:將所有元素清零為空狀態(tài)。

這些方法和下一節(jié)中描述的I/O方法實現(xiàn)了 Message所有C++協(xié)議緩沖區(qū)類共享的接口。有關(guān)更多信息, 見 Message的完整API文檔。

解析和序列化

最后,每個協(xié)議緩沖區(qū)類都有寫入和閱讀消息的方法 所選類型的二進(jìn)制格式這些措施 包括:

  • bool SerializeToString(string* output) const;:序列化消息并 存儲給定字符串中的字節(jié)。注意,字節(jié)是二進(jìn)制的,而不是 text;我們只使用string類作為方便的容器。
  • bool ParseFromString(const string& data);:從給定的 字符串
  • bool SerializeToOstream(ostream* output) const;:將消息寫入 C++ #2
  • bool ParseFromIstream(istream* input);:從給定的 C++ istream。

這些只是為解析和序列化提供的幾個選項。 再一次,請參閱 Message API參考 一份完整的名單。

寫消息

現(xiàn)在讓我們嘗試使用協(xié)議緩沖區(qū)類。你首先要做的 地址簿應(yīng)用程序能做的就是把個人詳細(xì)信息寫到你的 地址簿文件。為此,您需要創(chuàng)建并填充 協(xié)議緩沖器類,然后將它們寫入輸出流。

下面是一個程序,它從文件中讀取AddressBook,添加一個新的 Person基于用戶輸入將新的AddressBook寫回到它,并將新的undefined寫回 文件再次。直接調(diào)用或引用由 協(xié)議編譯器突出顯示。

#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
  cout << "Enter person ID number: ";
  int id;
  cin >> id;
  person->set_id(id);
  cin.ignore(256, '\n');
  cout << "Enter name: ";
  getline(cin, *person->mutable_name());
  cout << "Enter email address (blank for none): ";
  string email;
  getline(cin, email);
  if (!email.empty()) {
    person->set_email(email);
  }
  while (true) {
    cout << "Enter a phone number (or leave blank to finish): ";
    string number;
    getline(cin, number);
    if (number.empty()) {
      break;
    }
    tutorial::Person::PhoneNumber* phone_number = person->add_phones();
    phone_number->set_number(number);
    cout << "Is this a mobile, home, or work phone? ";
    string type;
    getline(cin, type);
    if (type == "mobile") {
      phone_number->set_type(tutorial::Person::MOBILE);
    } else if (type == "home") {
      phone_number->set_type(tutorial::Person::HOME);
    } else if (type == "work") {
      phone_number->set_type(tutorial::Person::WORK);
    } else {
      cout << "Unknown phone type.  Using default." << endl;
    }
  }
}
// Main function:  Reads the entire address book from a file,
//   adds one person based on user input, then writes it back out to the same
//   file.
int main(int argc, char* argv[]) {
  // Verify that the version of the library that we linked against is
  // compatible with the version of the headers we compiled against.
  GOOGLE_PROTOBUF_VERIFY_VERSION;
  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }
  tutorial::AddressBook address_book;
  {
    // Read the existing address book.
    fstream input(argv[1], ios::in | ios::binary);
    if (!input) {
      cout << argv[1] << ": File not found.  Creating a new file." << endl;
    } else if (!address_book.ParseFromIstream(&input)) {
      cerr << "Failed to parse address book." << endl;
      return -1;
    }
  }
  // Add an address.
  PromptForAddress(address_book.add_people());
  {
    // Write the new address book back to disk.
    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
    if (!address_book.SerializeToOstream(&output)) {
      cerr << "Failed to write address book." << endl;
      return -1;
    }
  }
  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();
  return 0;
}

注意GOOGLE_PROTOBUF_VERIFY_VERSION宏。這是一個很好的實踐-雖然 不是絕對必要的-在使用C++協(xié)議之前執(zhí)行此宏 緩沖庫。它驗證您沒有意外地鏈接到 版本的庫,這是不兼容的版本的標(biāo)題你 編譯。如果檢測到版本不匹配,程序?qū)⒅兄?。注意事?每個.pb.cc文件在啟動時自動調(diào)用此宏。

還請注意在程序結(jié)束時對ShutdownProtobufLibrary()的調(diào)用。 所有這一切都是刪除任何全局對象分配的協(xié)議 緩沖庫。這對于大多數(shù)程序來說是不必要的,因為這個過程是 無論如何都要退出,操作系統(tǒng)將負(fù)責(zé)回收其所有內(nèi)存。 但是,如果使用的內(nèi)存泄漏檢查器要求 或者如果你正在寫一個可以被加載和卸載的庫 多次,則您可能需要強(qiáng)制Protocol Buffers 清理一切

定義消息類型

首先讓我們看一個非常簡單的例子。假設(shè)您要定義一個 請求消息格式,其中每個搜索請求都有一個查詢字符串, 您感興趣的結(jié)果的特定頁面,以及每個頁面的結(jié)果數(shù)量。下面是您用來定義消息類型的.proto文件。

syntax = "proto3";
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 results_per_page = 3;
}

文件的第一行指定您使用的是proto3語法:如果 如果您不這樣做,協(xié)議緩沖區(qū)編譯器將假定您使用的是原型2.這一定是 文件的第一個非空、非注釋行。SearchRequest息定義指定了三個字段(名稱/值 對),一個用于要包含在此類型 留言每個字段都有一個名稱和類型。 指定字段類型

在前面的示例中,所有字段都是標(biāo)量類型:兩個整數(shù) (page_numberresults_per_page)和字符串(query)。也可以指定枚舉和復(fù)合類型,如其他消息類型的領(lǐng)域。

到此這篇關(guān)于C++ Protobuf的學(xué)習(xí)使用指南的文章就介紹到這了,更多相關(guān)C++ Protobuf內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論