欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++ProtoBuf使用小結(jié)

 更新時(shí)間:2024年01月16日 11:38:18   作者:函數(shù)指針  
ProtoBuf全稱:protocol buffers,直譯過來是:“協(xié)議緩沖區(qū)”,是一種與語(yǔ)言無(wú)關(guān)、與平臺(tái)無(wú)關(guān)的可擴(kuò)展機(jī)制,用于序列化結(jié)構(gòu)化數(shù)據(jù),這篇文章主要介紹了C/C++ProtoBuf使用,需要的朋友可以參考下

一.什么是ProtoBuf

1.序列化和反序列化概念

序列化:把對(duì)象轉(zhuǎn)變?yōu)樽止?jié)序列的過程,稱為系列化。

反序列化:把字節(jié)序列的內(nèi)容恢復(fù)為對(duì)象的過程,稱為反序列化。

2.什么情況下需要序列化和反序列化

存儲(chǔ)數(shù)據(jù):將內(nèi)存中的對(duì)象狀態(tài)保存在文件中或存儲(chǔ)在數(shù)據(jù)庫(kù)中時(shí)。

網(wǎng)絡(luò)傳輸:網(wǎng)絡(luò)傳輸數(shù)據(jù)時(shí),無(wú)法直接傳輸對(duì)象,需要在傳輸前序列化,傳輸完成后反 序列化成對(duì)象,就像學(xué)習(xí)Socket編程中發(fā)送與接收時(shí)。

3.如何實(shí)現(xiàn)序列化

xml,json,protoBuf這三種工具都可以實(shí)現(xiàn)序列化和反序列化

4.ProtoBuf是什么

ProtoBuf是讓數(shù)據(jù)結(jié)構(gòu)序列化的方法,具有以下特點(diǎn):

  • 語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān):即 ProtoBuf 支持 Java、C++、Python 等多種語(yǔ)言,支持多個(gè)平臺(tái)。
  • 高效:即比 XML 更小、更快、更為簡(jiǎn)單。
  • 擴(kuò)展性、兼容性好:你可以更新數(shù)據(jù)結(jié)構(gòu),而不影響和破壞原有的舊程序。

二.ProtoBuf簡(jiǎn)單語(yǔ)法知識(shí)

1.文件規(guī)范

創(chuàng)建一個(gè)ProtoBuf文件,后綴一定以.proto結(jié)尾,文件命名全小寫字母,多個(gè)字母之間以_為分隔符,添加注釋的方法和C/C++的一毛一樣。

2.ProtoBuf語(yǔ)法分類

ProtoBuf有對(duì)個(gè)版本,在這里我們使用最新的版本,protobuf3的語(yǔ)法,簡(jiǎn)稱proto3,它是最新的ProtoBuf語(yǔ)法版本。proto3 簡(jiǎn)化了 ProtoBuf 語(yǔ)言,既易于使用,又可以在更廣泛的編程語(yǔ)言中使用。它允許你使用 Java,C++,Python等多種語(yǔ)言生成 protocol buffer 代碼。

3.ProtoBuf3語(yǔ)法

syntax

我們?cè)趧?chuàng)建一個(gè)文件時(shí),一定要在文件開頭致命你所使用的語(yǔ)法,如果沒有指定,默認(rèn)使用Protobuf2的語(yǔ)法來使用,

比如:

syntax = "proto3";

syntax為指定語(yǔ)法,一定要有,就像函數(shù)中一定要指明函數(shù)返回類型一樣。

package(聲明符)

package 是一個(gè)可選的聲明符,能表示 .proto 文件的命名空間,在項(xiàng)目中要有唯一性。它的作用是為了避免我們定義的消息出現(xiàn)沖突,就像c++中的namespace一樣,指定一個(gè)域,防止沖突。

比如:

package person;

message(定義消息)

消息(message):要定義的結(jié)構(gòu)化對(duì)象,可以給這個(gè)結(jié)構(gòu)化對(duì)象中定義對(duì)用的屬性內(nèi)容,就像C//C++中的結(jié)構(gòu)體一樣,只能夠定義對(duì)象。

這里強(qiáng)調(diào)下為什么要定義對(duì)象:

網(wǎng)絡(luò)傳輸中,需要傳輸雙方定制協(xié)議,定制協(xié)議就是定制結(jié)構(gòu)體或者結(jié)構(gòu)化數(shù)據(jù),比如:Tcp,Udp。而且將數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)時(shí),需將數(shù)據(jù)統(tǒng)一為對(duì)象組織起來,在進(jìn)行存儲(chǔ)。

所以ProtoBuf就是以message的方式支持我們定義協(xié)議字段的。比如:

syntax="proto3";
package=package;
message people
{
        ;      
}

定義字段消息

在 message 中我們可以定義其屬性字段,字段定義格式為:字段類型 字段名 = 字段唯一編號(hào),比如:

syntax="proto3";
package=package;
message people
{
       int32 age=1;     
}

int32為字段類型 age為字段名 1為字段唯一編號(hào)

下面是protobuf創(chuàng)建類型及所對(duì)應(yīng)的C/C++類型:

注:[1] 變長(zhǎng)編碼是指:經(jīng)過protobuf 編碼后,原本4字節(jié)或8字節(jié)的數(shù)可能會(huì)被變?yōu)槠渌止?jié)數(shù)。

比如:

syntax="proto3"
package sss
message person
{
    string name=1;
    int32 age=2;
    string sex=3;
}

注意:字段唯一編號(hào)范圍:

2^0~2^29-1,其中,19000~19999不可用,在 Protobuf 協(xié)議的實(shí)現(xiàn)中,對(duì)這些數(shù)進(jìn)行了預(yù)留。如果非要在.proto文件中使用這些預(yù)留標(biāo)識(shí)號(hào),例如將 name 字段的編號(hào)設(shè)置為19000,編譯時(shí)就會(huì)報(bào)警。值得一提的是,范圍為 1 ~ 15 的字段編號(hào)需要一個(gè)字節(jié)進(jìn)行編碼, 16 ~ 2047 內(nèi)的數(shù)字需要兩個(gè)字節(jié)進(jìn)行編碼。編碼后的字節(jié)不僅只包含了編號(hào),還包含了字段類型。所以 1 ~ 15 要用來標(biāo)記出現(xiàn)非常頻繁的字段,要為將來有可能添加的、頻繁出現(xiàn)的字段預(yù)留一些出來。

通過上面的學(xué)習(xí),我們來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的test.proto文件,

syntax="proto3";
package sss;
message person
{
    string name=1;
    int32 age=2;
    string sex=3;
}

編輯protobuf命令如下:proto --cpp_out. [文件名]

我們使用的是C++語(yǔ)言,所以編譯之后生成兩個(gè)文件:test.pb.h和test.pb.cc。

對(duì)于編譯生成的 C++ 代碼,包含了以下內(nèi)容 :

  • 對(duì)于每個(gè) message ,都會(huì)生成一個(gè)對(duì)應(yīng)的消息類。
  • 在消息類中,編譯器為每個(gè)字段提供了獲取和設(shè)置方法,以及一下其他能夠操作字段的方法。
  • 編輯器會(huì)針對(duì)于每個(gè) .proto 文件生成.h 和 .cc 文件,分別用來存放類的聲明與類的實(shí)現(xiàn)。

代碼過長(zhǎng),就不展示了,感興趣的自己做下?。。?/p>

編譯命令行格式

protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto

protoc:是protobuf提供的命令行編輯工具。

--proto_path: 指定被編輯的所在目錄,可多次指定,可簡(jiǎn)寫為-I。如不指定,則在當(dāng)前目錄下進(jìn)行搜索。

--cpp_out=: 指定編譯后的文件為c++文件。

DST_DIR; 文件路徑

path/to/file.proto:要編譯的文件。

上述的例子中,每個(gè)字段都有設(shè)置和獲取的方法,getter 的名稱與小寫字段完全相同,setter 方法以 set_ 開頭,每個(gè)字段都有一個(gè)clear_的方法,可以將字段設(shè)置為empty狀態(tài)。比如:

#include <iostream>
#include "test.ph.h"
using namespace sss;
using namespace std;
int main()
{
    person p1;
    p1.set_name("張三");
    p1.set_age(11);
    p1.set_sex("nan");
    return 0;
}

在消息類的父類MessageLift中,提供了讀寫消息實(shí)例的方法,包括序列化方法和反序列化方法。

class MessageLite {
public:
//序列化:
bool SerializeToOstream(ostream* output) const; // 將序列化后數(shù)據(jù)寫入文件
//流
bool SerializeToArray(void *data, int size) const;
bool SerializeToString(string* output) const;
//反序列化:
bool ParseFromIstream(istream* input); // 從流中讀取數(shù)據(jù),再進(jìn)行反序列化
//動(dòng)作
bool ParseFromArray(const void* data, int size);
bool ParseFromString(const string& data);
};

注意:

序列化的方法為二進(jìn)制方法,非文本格式;以上三種序列化的方法沒有本質(zhì)區(qū)別,只是序列化之后輸出的格式不同,可以提供不同場(chǎng)景使用,序列化的 API 函數(shù)均為const成員函數(shù),因?yàn)樾蛄谢粫?huì)改變類對(duì)象的內(nèi)容, 而是將序列化的結(jié)果保存到函數(shù)入?yún)⒅付ǖ牡刂分小?/p>

更多詳細(xì)API函數(shù)參考:message.h | Protocol Buffers Documentation (protobuf.dev)

序列化和反序列化的使用:看結(jié)果

#include "contact.pb.h"
using namespace std;
int main()
{
    string people_str;
    contact::PersonInit p1;
    p1.set_age(100);
    p1.set_name("張三");
    p1.set_sex("boy");
    if (!p1.SerializeToString(&people_str)) 
    {
        cout << "序列化聯(lián)系人失敗." << endl;
    }
    // 打印序列化結(jié)果
    cout << "序列化后的 people_str: " << people_str << endl;
    if(!p1.SerializePartialToString(&people_str))
    {
        cout<<"反序列化失敗:"<<endl;
    }
    cout<<"反序列化成功:"<<endl<<people_str<<endl;
    return 0;
}

這就是簡(jiǎn)單的Protobuf的應(yīng)用。

接下來是ProtoBuf的語(yǔ)法詳解。

三.ProtoBuf語(yǔ)法詳解

在這部分,我們使用一個(gè)簡(jiǎn)單的項(xiàng)目推進(jìn)的方式來講解語(yǔ)法內(nèi)容,通過一個(gè)通訊錄的實(shí)現(xiàn),完成我們從最基礎(chǔ)的內(nèi)容到網(wǎng)絡(luò)版本的知識(shí)。

1.字段規(guī)則

singular :消息中可以包含該字段零次或一次(不超過一次)。 proto3 語(yǔ)法中,字段默認(rèn)使用該規(guī)則,可以不用設(shè)置。

repeated :消息中可以包含該字段任意多次(包括零次),其中重復(fù)值的順序會(huì)被保留。可以理解為定義了一個(gè)數(shù)組。

比如:

syntax="proto3";
package contacts2;
message person
{
    string name=1;
    int32 age=2;
    repeated string phone_num=3;
}

2.消息類型的定義與使用

在單個(gè).proto文件中,可以定義多個(gè)message,也可以嵌套定義,每個(gè)message中的字段編號(hào)可以重復(fù),并且,消息字段也可以作為字段類型來使用,比如:

syntax="proto3";
package contacts2;
// message Phone{
//     string phone_num=1;
// }
message person
{
    string name=1;
    int32 age=2;
    //第一種,單獨(dú)寫法
    //repeated Phone=3;
    //第二種,嵌套寫法
    message Phone{
        string phone_num=1;
    }
    repeated Phone=3;
}

也可以將其他.proto文件中定義的消息導(dǎo)入并使用,但一定要加import 比如:

syntax="proto3";
package contacts2;
import "Photo.proto";//將其他文件中的消息字段導(dǎo)入
message person
{
    string name=1;
    int32 age=2;
    //第三種
    repeated Phone.Phone phone=3;
}
syntax="proto3";
package Phone;
message Phone{
    string phone_num=1;
}

注:在 proto3 文件中可以導(dǎo)入 proto2 消息類型并使用它們,反之亦然

編譯

我們可以將以上的內(nèi)容稍加修改,變?yōu)槲覀兊牡谝话嫱ㄓ嶄洠?/p>

syntax="proto3";
package contacts2;
//import "Photo.proto";//將其他文件中的消息字段導(dǎo)入
message peopleInfo
{
    string name=1;
    int32 age=2;
    message Phone{
        string phone_num=1;
    }
    repeated Phone=3;
}
message Contact{
    repeated PeopleInfo contact=1;
}

然后編譯,編譯后生成的 contacts.pb.h contacts.pb.cc 會(huì)將老版本的代碼覆蓋掉,由于代碼過長(zhǎng),就不展示啦?。。?/p>

上述的代碼中:

  • 每個(gè)字段都有一個(gè) clear_ 方法,可以將字段重新設(shè)置回 empty 狀態(tài)。
  • 每個(gè)字段都有設(shè)置和獲取的方法, 獲取方法的方法名稱與小寫字段名稱完全相同。但如果是消息類型的字段,其設(shè)置方法為 mutable_ 方法,返回值為消息類型的指針,這類方法會(huì)為我們開辟好空間,可以直接對(duì)這塊空間的內(nèi)容進(jìn)行修改。
  • 對(duì)于使用 repeated 修飾的字段,也就是數(shù)組類型,pb 為我們提供了 add_ 方法來新增一個(gè)值,并且提供了 _size 方法來判斷數(shù)組存放元素的個(gè)數(shù)。

我們可以對(duì)編譯好的代碼試用一下:

#include <iostream>
#include <fstream>
#include "contact.pb.h"
using namespace std;
using namespace contacts;
void AddPeopleInfo(PeopleInfo *people_info_ptr)
{
    cout << "-------------新增聯(lián)系人-------------" << endl;
    cout << "請(qǐng)輸入聯(lián)系人姓名: ";
    string name;
    getline(cin, name);
    people_info_ptr->set_name(name);//輸入用set_函數(shù)
    cout << "請(qǐng)輸入聯(lián)系人年齡: ";
    int age;
    cin >> age;
    people_info_ptr->set_age(age);
    cin.ignore(256, '\n');
    for (int i = 1;; i++)
    {
        cout << "請(qǐng)輸入聯(lián)系人電話" << i << "(只輸入回車完成電話新增): ";
        string number;
        getline(cin, number);
        if (number.empty())
        {
            break;
        }
        //number需要添加才可以用
        PeopleInfo_Phone *phone = people_info_ptr->add_phone();
        phone->set_number(number);
    }
    cout << "-----------添加聯(lián)系人成功-----------" << endl;
}
int main()
{
    Contacts contacts;
    // 先讀取已存在的 contacts
    fstream input("text.bin",ios::in | ios::binary);
    if (!input)
    {
        cout << ": File not found. Creating a new file." << endl;
    }
    else if (!contacts.ParseFromIstream(&input))//序列化
    {
        cerr << "Failed to parse contacts." << endl;
        input.close();
        return -1;
    }
    // 新增一個(gè)聯(lián)系人
    AddPeopleInfo(contacts.add_contacts());
    //向磁盤文件寫入新的 contacts
    fstream output("test.bin",ios::out | ios::trunc | ios::binary);
    if (!contacts.SerializeToOstream(&output))
    {
        cerr << "Failed to write contacts." << endl;
        input.close();
        output.close();
        return -1;
    }
    input.close();
    //output.close();
    return 0;
}

3:enum類型的使用

語(yǔ)法支持我們定義枚舉類型來使用,我們可以定義一個(gè)enum類型的使用,

syntax="proto3";
package Phone;
message{
    enum PhoneType{
        MP=0;//移動(dòng)電話
        TEL=1;//固定電話
    }
}

要注意枚舉類型的定義有以下幾種規(guī)則:
1. 0 值常量必須存在,且要作為第一個(gè)元素。這是為了與 proto2 的語(yǔ)義兼容:第一個(gè)元素作為默認(rèn)值,且值為 0。
2. 枚舉類型可以在消息外定義,也可以在消息體內(nèi)定義(嵌套)。
3. 枚舉的常量值在 32 位整數(shù)的范圍內(nèi)。但因負(fù)值無(wú)效因而不建議使用(與編碼規(guī)則有關(guān))。

定義時(shí)注意:
將兩個(gè) ‘具有相同枚舉值名稱’ 的枚舉類型放在單個(gè) .proto 文件下測(cè)試時(shí),編譯后會(huì)報(bào)錯(cuò):某某某常量已經(jīng)被定義!所以這里要注意:
• 同級(jí)(同層)的枚舉類型,各個(gè)枚舉類型中的常量不能重名。
• 單個(gè) .proto 文件下,最外層枚舉類型和嵌套枚舉類型,不算同級(jí)。
• 多個(gè) .proto 文件下,若一個(gè)文件引入了其他文件,且每個(gè)文件都未聲明 package,每個(gè) proto 文件中的枚舉類型都在最外層,算同級(jí)。
• 多個(gè) .proto 文件下,若一個(gè)文件引入了其他文件,且每個(gè)文件都聲明了 package,不算同級(jí)。

下面我們來使用一下:

message Address
{
    string home_address=1;
    string unit_address=2;
}
message PeopleInfo
{
    string name=1;
    int32 age=2;
    message Phone
    {
        string number=1;
        enum PhoneType 
        {
            MP = 0; // 移動(dòng)電話
            TEL = 1; // 固定電話
        }
        PhoneType type=2;
    }
    //多組電話
    repeated Phone phone=3;
}

編譯生成后的文件:對(duì)于在.proto文件中定義的枚舉類型,編譯生成的代碼中會(huì)含有與之對(duì)應(yīng)的枚舉類型、校驗(yàn)枚舉值是否有效的方法 _IsValid、以及獲取枚舉值名稱的方法 _Name。對(duì)于使用了枚舉類型的字段,包含設(shè)置和獲取字段的方法,已經(jīng)清空字段的方法clear_;

4.any類型

字段還可以聲明為 Any 類型,可以理解為泛型類型。使用時(shí)可以在 Any 中存儲(chǔ)任意消息類型。Any 類型的字段也用 repeated 來修飾,Any 類型是 google 已經(jīng)幫我們定義好的類型,在安裝 ProtoBuf 時(shí),其中的 include 目錄下查找所有g(shù)oogle 已經(jīng)定義好的 .proto 文件,在此目錄下查找:/usr/local/protobuf/include/google/protobuf/

我們可以來使用一下:

message Address
{
    string home_address=1;
    string unit_address=2;
}
message PeopleInfo
{
    string name=1;
    int32 age=2;
    message Phone
    {
        string number=1;
        enum PhoneType 
        {
            MP = 0; // 移動(dòng)電話
            TEL = 1; // 固定電話
        }
        PhoneType type=2;
    }
    //多組電話
    repeated Phone phone=3;
    google.protobuf.Any data = 4;
    oneof other_contact 
    {
        // repeated string qq = 5;  // 不能使用 repeated
        string qq = 5;
        string wechat = 6;
    }
    //備注信息
    map<string,string> remake=7;    
}

上述的代碼中,對(duì)于 Any 類型字段:
設(shè)置和獲?。韩@取方法的方法名稱與小寫字段名稱完全相同。設(shè)置方法可以使用 mutable_ 方
法,返回值為Any類型的指針,這類方法會(huì)為我們開辟好空間,可以直接對(duì)這塊空間的內(nèi)容進(jìn)行
修改;之前說過,我們可以在 Any 字段中存儲(chǔ)任意消息類型,這就要涉及到任意消息類型 和 Any 類型的互轉(zhuǎn)。這部分代碼就在 Google為我們寫好的頭文件 any.pb.h 中。使用方法:

  • 使用 PackFrom() 方法可以將任意消息類型轉(zhuǎn)為 Any 類型。
  • 使用 UnpackTo() 方法可以將 Any 類型轉(zhuǎn)回之前設(shè)置的任意消息類型。
  • 使用 Is() 方法可以用來判斷存放的消息類型是否為 typename T。

方法使用:

Address address;
cout << "請(qǐng)輸入聯(lián)系人家庭地址: ";
string home_address;
getline(cin, home_address);
address.set_home_address(home_address);
cout << "請(qǐng)輸入聯(lián)系人單位地址: ";
string unit_address;
getline(cin, unit_address);
address.set_unit_address(unit_address);
google::protobuf::Any * data = people_info_ptr->mutable_data();
data->PackFrom(address);
if (people.has_data() && people.data().Is<Address>()) 
{
    Address address;
    people.data().UnpackTo(&address);
    if (!address.home_address().empty()) 
    {
        cout << "家庭地址:" << address.home_address() << endl;
    }
    if (!address.unit_address().empty()) 
    {
    cout << "單位地址:" << address.unit_address() << endl;
    }
}

5.oneof 類型

如果消息中有很多可選字段, 并且將來同時(shí)只有一個(gè)字段會(huì)被設(shè)置, 那么就可以使用 oneof 加強(qiáng)這個(gè)行為,也能有節(jié)約內(nèi)存的效果。

oneof other_contact 
 {
    // repeated string qq = 5;  // 不能使用 repeated
    string qq = 5;
    string wechat = 6;
 }

注意:

  • 可選字段中的字段編號(hào),不能與非可選字段的編號(hào)沖突。
  • 不能在 oneof 中使用 repeated 字段。
  • 將來在設(shè)置 oneof 字段中值時(shí),如果將 oneof 中的字段設(shè)置多個(gè),那么只會(huì)保留最后一次設(shè)置的成員,之前設(shè)置的 oneof 成員會(huì)自動(dòng)清除。
  • 上述的代碼中,對(duì)于 oneof 字段:會(huì)將 oneof 中的多個(gè)字段定義為一個(gè)枚舉類型。設(shè)置和獲取:對(duì) oneof 內(nèi)的字段進(jìn)行常規(guī)的設(shè)置和獲取即可,但要注意只能設(shè)置一個(gè)。如果設(shè)置多個(gè),那么只會(huì)保留最后一次設(shè)置的成員。清空oneof字段:clear_ 方法,獲取當(dāng)前設(shè)置了哪個(gè)字段:_case 方法。
    std::cout << "請(qǐng)選擇要添加的其他聯(lián)系方式(1、qq     2、wechat):";
    int other_contact;
    std::cin>>other_contact;
    std::cin.ignore(256, '\n');
    if(other_contact==1)
    {
        std::cout<<"輸入QQ: ";
        std::string qq;
        getline(std::cin, qq);
        people->set_qq(qq);
    }
    else if(other_contact==2)
    {
        std::cout<<"輸入wechat: ";
        std::string wechat;
        getline(std::cin, wechat);
        people->set_wechat(wechat);
    }
    else std::cout<<"輸入錯(cuò)誤!"<<std::endl;
    switch (people.other_contact_case())
    {
        case PeopleInfo::OtherContactCase::kQq:
            cout << "qq號(hào): " << people.qq() << endl;
            break;
        case PeopleInfo::OtherContactCase::kWeixin:
            cout << "微信號(hào): " << people.weixin() << endl;
            break;
        case PeopleInfo::OtherContactCase::OTHER_CONTACT_NOT_SET:
            break;
     }

6.map類型

語(yǔ)法支持創(chuàng)建一個(gè)關(guān)聯(lián)映射字段,也就是可以使用 map 類型去聲明字段類型,格式為:
map<key_type, value_type> map_field = N;
要注意的是:
• key_type 是除了 float 和 bytes 類型以外的任意標(biāo)量類型。 value_type 可以是任意類型。
• map 字段不可以用 repeated 修飾。
• map 中存入的元素是無(wú)序的。

使用:

map<string, string> remark = 7; // 備注

清空map: clear_ 方法
• 設(shè)置和獲?。韩@取方法的方法名稱與小寫字段名稱完全相同。設(shè)置方法為 mutable_ 方法,返回
值為Map類型的指針,這類方法會(huì)為我們開辟好空間,可以直接對(duì)這塊空間的內(nèi)容進(jìn)行修改。

7.默認(rèn)值

反序列化消息時(shí),如果被反序列化的二進(jìn)制序列中不包含某個(gè)字段,反序列化對(duì)象中相應(yīng)字段時(shí),就會(huì)設(shè)置為該字段的默認(rèn)值。不同的類型對(duì)應(yīng)的默認(rèn)值不同:

  • 對(duì)于字符串,默認(rèn)值為空字符串。
  •  對(duì)于字節(jié),默認(rèn)值為空字節(jié)。
  •  對(duì)于布爾值,默認(rèn)值為 false。
  •  對(duì)于數(shù)值類型,默認(rèn)值為 0。
  •  對(duì)于枚舉,默認(rèn)值是第一個(gè)定義的枚舉值, 必須為 0。
  •  對(duì)于消息字段,未設(shè)置該字段。它的取值是依賴于語(yǔ)言。
  •  對(duì)于設(shè)置了 repeated 的字段的默認(rèn)值是空的( 通常是相應(yīng)語(yǔ)言的一個(gè)空列表 )。
  •  對(duì)于消息字段、oneof字段和any字段,C++ 和 Java 語(yǔ)言中都有 has_ 方法來檢測(cè)當(dāng)前字段是否被設(shè)置。

8.更新消息

更新規(guī)則·

  • 禁止修改任何已有字段的字段編號(hào)。
  •  若是移除老字段,要保證不再使用移除字段的字段編號(hào)。正確的做法是保留字段編號(hào)(reserved),以確保該編號(hào)將不能被重復(fù)使用。不建議直接刪除或注釋掉字段。
  •  int32, uint32, int64, uint64 和 bool 是完全兼容的??梢詮倪@些類型中的一個(gè)改為另一個(gè),而不破壞前后兼容性。若解析出來的數(shù)值與相應(yīng)的類型不匹配,會(huì)采用與 C++ 一致的處理方案(例如,若將 64 位整數(shù)當(dāng)做 32 位進(jìn)行讀取,它將被截?cái)酁?32 位)。
  •  sint32 和 sint64 相互兼容但不與其他的整型兼容。
  •  string 和 bytes 在合法 UTF-8 字節(jié)前提下也是兼容的。
  •  bytes 包含消息編碼版本的情況下,嵌套消息與 bytes 也是兼容的。
  •  fixed32 與 sfixed32 兼容, fixed64 與 sfixed64兼容。
  • enum 與 int32,uint32, int64 和 uint64 兼容(注意若值不匹配會(huì)被截?cái)啵5⒁猱?dāng)反序列化消息時(shí)會(huì)根據(jù)語(yǔ)言采用不同的處理方案:例如,未識(shí)別的 proto3 枚舉類型會(huì)被保存在消息中,但是當(dāng)消息反序列化時(shí)如何表示是依賴于編程語(yǔ)言的。整型字段總是會(huì)保持其的值。
  • oneof:
  • 將一個(gè)單獨(dú)的值更改為 新 oneof 類型成員之一是安全和二進(jìn)制兼容的。若確定沒有代碼一次性設(shè)置多個(gè)值那么將多個(gè)字段移入一個(gè)新 oneof 類型也是可行的。將任何字段移入已存在的 oneof 類型是不安全的。

保留字段:reserved

  • 如果通過 刪除 或 注釋掉 字段來更新消息類型,未來的用戶在添加新字段時(shí),有可能會(huì)使用以前已經(jīng)存在,但已經(jīng)被刪除或注釋掉的字段編號(hào)。將來使用該 .proto 的舊版本時(shí)的程序會(huì)引發(fā)很多問題:數(shù)據(jù)損壞、隱私錯(cuò)誤等等。
  •         確保不會(huì)發(fā)生這種情況的一種方法是:使用 reserved 將指定字段的編號(hào)或名稱設(shè)置為保留項(xiàng) 當(dāng)我們?cè)偈褂眠@些編號(hào)或名稱時(shí),protocol buffer 的編譯器將會(huì)警告這些編號(hào)或名稱不可用。

未知字段

未知字段:解析結(jié)構(gòu)良好的 protocol buffer 已序列化數(shù)據(jù)中的未識(shí)別字段的表示方式。例如,當(dāng)舊程序解析帶有新字段的數(shù)據(jù)時(shí),這些新字段就會(huì)成為舊程序的未知字段。
本來,proto3 在解析消息時(shí)總是會(huì)丟棄未知字段,但在 3.5 版本中重新引入了對(duì)未知字段的保留機(jī)制。所以在 3.5 或更高版本中,未知字段在反序列化時(shí)會(huì)被保留,同時(shí)也會(huì)包含在序列化的結(jié)果中。

9.UnknownFieldSet 類介紹

  • UnknownFieldSet 包含在分析消息時(shí)遇到但未由其類型定義的所有字段。
  • 若要將 UnknownFieldSet 附加到任何消息,請(qǐng)調(diào)用 Reflection::GetUnknownFields()。
  • 類定義在 unknown_field_set.h 中。

10.UnknownField 類介紹

  • 表示未知字段集中的一個(gè)字段。
  •  類定義在 unknown_field_set.h 中。

使用:

        const Reflection *reflection = PeopleInfo::GetReflection();
        const UnknownFieldSet &unknowSet = reflection->GetUnknownFields(people);
        for (int j = 0; j < unknowSet.field_count(); j++)
        {
            const UnknownField &unknow_field = unknowSet.field(j);
            cout << "未知字段" << j + 1 << ":"
                 << " 字段編號(hào): " << unknow_field.number()
                 << " 類型: " << unknow_field.type();
            switch (unknow_field.type())
            {
            case UnknownField::Type::TYPE_VARINT:
                cout << " 值: " << unknow_field.varint() << endl;
                break;
            case UnknownField::Type::TYPE_LENGTH_DELIMITED:
                cout << " 值: " << unknow_field.length_delimited() << endl;
                break;
            }
        }

11.前后兼容性

前后兼容的作用,當(dāng)我們維護(hù)一個(gè)很大的分布式系統(tǒng)時(shí),由于無(wú)法同時(shí)升級(jí)說有模塊,為了在保證升級(jí)過程中,整個(gè)系統(tǒng)能夠盡可能不受影響,就需要盡量保證通訊協(xié)議的“向后兼容”或“向前兼容”。

12.選項(xiàng) option

proto 文件中可以聲明許多選項(xiàng),使用 option 標(biāo)注。選項(xiàng)能影響 proto 編譯器的某些處理方式。

選項(xiàng)的完整列表在google/protobuf/descriptor.proto中定義。文件分為字段級(jí),消息級(jí),文件級(jí),但并沒有所有的選項(xiàng)能作用于所有類型,常見舉例

optimize_for:為文件選項(xiàng),可以設(shè)置 protoc 編譯器的優(yōu)化級(jí)別,分別為 SPEED 、
CODE_SIZE 、LITE_RUNTIME 。受該選項(xiàng)影響,設(shè)置不同的優(yōu)化級(jí)別,編譯 .proto 文件后生成的代碼內(nèi)容不同。

  • SPEED:protoc編譯器將生成的代碼是高度優(yōu)化的,代碼運(yùn)行效率高,但生成的代碼更占空間,SPEED為默認(rèn)選項(xiàng)。
  • CODE_SIZE : proto 編譯器將生成最少的類,會(huì)占用更少的空間,是依賴基于反射的代碼來實(shí)現(xiàn)序列化、反序列化和各種其他操作。但和SPEED 恰恰相反,它的代碼運(yùn)行效率較低。這種方式適合用在包含大量的.proto文件,但并不盲目追求速度的應(yīng)用中。
  • LITE_RUNTIME : 生成的代碼執(zhí)行效率高,同時(shí)生成代碼編譯后的所占用的空間也是非常少。這是以犧牲Protocol Buffer提供的反射功能為代價(jià)的,僅僅提供 encoding+序列化 功能,所以我們?cè)阪溄?BP 庫(kù)時(shí)僅需鏈接libprotobuf-lite,而非libprotobuf。這種模式通常用于資源有限的平臺(tái),例如移動(dòng)手機(jī)平臺(tái)中。
  • allow_alias : 允許將相同的常量值分配給不同的枚舉常量,用來定義別名。該選項(xiàng)為枚舉選項(xiàng)。

本地版本代碼實(shí)現(xiàn):

四:網(wǎng)絡(luò)版本通訊錄的實(shí)現(xiàn)

Protobuf 還常用于通訊協(xié)議、服務(wù)端數(shù)據(jù)交換場(chǎng)景。那么在這個(gè)示例中,我們將實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)版本通訊錄,模擬實(shí)現(xiàn)客戶端與服務(wù)端的交互,通過 Protobuf 來實(shí)現(xiàn)各端之間的協(xié)議序列化。

1.環(huán)境搭建

Httplib 庫(kù):cpp-httplib 是個(gè)開源的庫(kù),是一個(gè)c++封裝的http庫(kù),使用這個(gè)庫(kù)可以在linux、
windows平臺(tái)下完成http客戶端、http服務(wù)端的搭建。使用起來非常方便,只需要包含頭文件
httplib.h 即可。編譯程序時(shí),需要帶上 -lpthread 選項(xiàng)。
鏡像倉(cāng)庫(kù):weixin_30886717 / cpp-httplib · GitCode

centos 下編寫的注意事項(xiàng):
如果使用 centOS 環(huán)境,yum源帶的 g++ 最新版本是4.8.5,發(fā)布于2015年,年代久遠(yuǎn)。編譯該項(xiàng)目會(huì)出現(xiàn)異常。將 gcc/g++ 升級(jí)為更高版本可解決問題。

實(shí)現(xiàn):

// 自定義異常類
class ContactException
{
private:
    std::string message;
public:
    ContactException(std::string str = "A problem") : message{str} {}
    std::string what() const { return message; }
};
#include <iostream>
#include "contact.pb.h"
#include "ContactException.h"
void menu()
{
    std::cout << "-----------------------------------------------------" << std::endl
              << "--------------- 請(qǐng)選擇對(duì)通訊錄的操作 ----------------" << std::endl
              << "------------------ 1、新增聯(lián)系人 --------------------" << std::endl
              << "------------------ 2、刪除聯(lián)系人 --------------------" << std::endl
              << "------------------ 3、查看聯(lián)系人列表 ----------------" << std::endl
              << "------------------ 4、查看聯(lián)系人詳細(xì)信息 ------------" << std::endl
              << "------------------ 0、退出 --------------------------" << std::endl
              << "-----------------------------------------------------" << std::endl;
}
int main()
{
    enum OPERATE
    {
        QUIT = 0,
        ADD,
        DEL,
        FIND_ALL,
        FIND_ONE
    };
    while (true)
    {
        menu();
        std::cout << "---> 請(qǐng)選擇:";
        int choose;
        std::cin >> choose;
        std::cin.ignore(256, '\n');
        try
        {
            switch (choose)
            {
            case OPERATE::ADD:
                contactsServer.addContact();
                break;
            case OPERATE::DEL:
                contactsServer.delContact();
                break;
            case OPERATE::FIND_ALL:
                contactsServer.findContacts();
                break;
            case OPERATE::FIND_ONE:
                contactsServer.findContact();
                break;
            case 0:
                std::cout << "---> 程序已退出" << std::endl;
                return 0;
            default:
                std::cout << "---> 無(wú)此選項(xiàng),請(qǐng)重新選擇!" << std::endl;
                break;
            }
        }
        catch (const ContactException &e)
        {
            std::cerr << "---> 操作通訊錄時(shí)發(fā)現(xiàn)異常?。。? << std::endl
                      << "---> 異常信息:" << e.what() << std::endl;
        }
        catch (const std::exception &e)
        {
            std::cerr << "---> 操作通訊錄時(shí)發(fā)現(xiàn)異常?。。? << std::endl
                      << "---> 異常信息:" << e.what() << std::endl;
        }
    }
}
class ContactsServer
{
public:
    void addContact();
    void delContact();
    void findContacts();
    void findContact();
private:
    void buildAddContactRequest(add_contact_req::AddContactRequest* req);
    void printFindOneContactResponse(find_one_contact_resp::FindOneContactResponse&
    resp);
    void printFindAllContactsResponse(find_all_contacts_resp::FindAllContactsResponse&
        resp);
};

五:總結(jié):

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

相關(guān)文章

  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能(1)

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能(1)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能的第一部分,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • C語(yǔ)言代碼實(shí)現(xiàn)2048游戲

    C語(yǔ)言代碼實(shí)現(xiàn)2048游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言代碼實(shí)現(xiàn)2048游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • VS2019中QT連接及使用的方法步驟

    VS2019中QT連接及使用的方法步驟

    這篇文章主要介紹了VS2019中QT連接及使用的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • windows系統(tǒng)下C++調(diào)用matlab程序的方法詳解

    windows系統(tǒng)下C++調(diào)用matlab程序的方法詳解

    這篇文章主要給大家介紹了關(guān)于在windows系統(tǒng)下C++調(diào)用matlab程序的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • C++簡(jiǎn)易版Tensor實(shí)現(xiàn)方法詳解

    C++簡(jiǎn)易版Tensor實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了C++簡(jiǎn)易版Tensor的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值
    2022-08-08
  • C++重載運(yùn)算符你真的了解嗎

    C++重載運(yùn)算符你真的了解嗎

    這篇文章主要為大家詳細(xì)介紹了C++重載運(yùn)算符,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C語(yǔ)言實(shí)現(xiàn)學(xué)生選課系統(tǒng)完整版

    C語(yǔ)言實(shí)現(xiàn)學(xué)生選課系統(tǒng)完整版

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)學(xué)生選課系統(tǒng)的完整版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • C++實(shí)現(xiàn)堆排序?qū)嵗榻B

    C++實(shí)現(xiàn)堆排序?qū)嵗榻B

    大家好,本篇文章主要講的是C++實(shí)現(xiàn)堆排序?qū)嵗榻B,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • C++實(shí)現(xiàn)簡(jiǎn)易的彈球小游戲

    C++實(shí)現(xiàn)簡(jiǎn)易的彈球小游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)易的彈球小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • C++?Socket實(shí)現(xiàn)TCP與UDP網(wǎng)絡(luò)編程

    C++?Socket實(shí)現(xiàn)TCP與UDP網(wǎng)絡(luò)編程

    本文主要介紹了C++?Socket實(shí)現(xiàn)TCP與UDP網(wǎng)絡(luò)編程,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評(píng)論