1.字段规则
消息的字段可以用下面几种规则来修饰:
●singular: 消息中可以包含该字段零次或一次(不超过一次)。proto3语法中,字段默认使用该
规则。
●repeated :消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理
解为定义了一个数组。
更新contacts.proto,PeopleInfo 消息中新增phone_ numbers 字段,表示一个联系人有多个
号码,可将其设置为repeated,写法如下:
syntax = "proto3"
package contacts;message PeopleInfo {string name = 1;int32 age=2;repeated string phone_number = 3;
}
2.消息类型的定义与使用
2.1定义
在单个.proto文件中可以定义多个消息体,且支持定义嵌套类型的消息(任意多层) 。每个消息体中
的字段编号可以重复。
更新contacts.proto,我们可以将phone_ number 提取出来,单独成为一一个消息:
// -------------------------- 嵌套写法 -------------------------
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;}
}
// -------------------------- 非嵌套写法 -------------------------
message Phone {string number = 1;
}
message PeopleInfo {string name = 1;int32 age = 2;
}
2.2使用
●消息类型可作为字段类型使用
contacts.proto
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;}repeated Phone phone = 3;
}
可导入其他.proto文件的消息并使用
例如Phone消息定义在phone.proto文件中:
syntax = "proto3";
package phone;message Phone {string number = 1;
}
contacts.proto中的PeopleInfo使用Phone 消息:
import "phone.proto"; // 使⽤ import 将 phone.proto ⽂件导⼊进来 !!!message PeopleInfo {string name = 1;int32 age = 2;// 引⼊的⽂件声明了package,使⽤消息时,需要⽤ ‘命名空间.消息类型’ 格式repeated phone.Phone phone = 3;
}
注:在proto3文件中可以导入proto2消息类型并使用它们,反之亦然。
2.3创建通讯录2.0版本
通讯录2.x的需求是向文件中写入通讯录列表,以上我们只是定义了一个联系人的消息,并不能存放通讯录列表,所以还需要在完善一下contacts.proto (终版通讯录2.0):
//联系人
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;}repeated Phone phone = 3;
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}
接着进行一次编译:
protoc --cpp_out=. contacts.proto
编译后生成的contacts. pb.h contacts.pb.cc 会将前面的生成文件覆盖掉。
contacts.pb.h更新的部分代码展示
// 新增了 PeopleInfo_Phone 类
class PeopleInfo_Phone final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const PeopleInfo_Phone &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const PeopleInfo_Phone &from){PeopleInfo_Phone::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName(){return "PeopleInfo.Phone";}// string number = 1;void clear_number();const std::string &number() const;template <typename ArgT0 = const std::string &, typename... ArgT>void set_number(ArgT0 &&arg0, ArgT... args);std::string *mutable_number();PROTOBUF_NODISCARD std::string *release_number();void set_allocated_number(std::string *number);
};
// 更新了 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const PeopleInfo &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const PeopleInfo &from){PeopleInfo::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName(){return "PeopleInfo";}typedef PeopleInfo_Phone Phone;// repeated .PeopleInfo.Phone phone = 3;int phone_size() const;void clear_phone();::PeopleInfo_Phone *mutable_phone(int index);::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo_Phone> *mutable_phone();const ::PeopleInfo_Phone &phone(int index) const;::PeopleInfo_Phone *add_phone();const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo_Phone> &phone() const;
};
// 新增了 Contacts 类
class Contacts final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const Contacts &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const Contacts &from){Contacts::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName(){return "Contacts";}// repeated .PeopleInfo contacts = 1;int contacts_size() const;void clear_contacts();::PeopleInfo *mutable_contacts(int index);::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo> *mutable_contacts();const ::PeopleInfo &contacts(int index) const;::PeopleInfo *add_contacts();const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<::PeopleInfo> &contacts() const;
};
上述的例子中: .
●每个字段都有一个clear_方法,可以将字段重新设置回empty状态。
●每个字段都有设置和获取的方法,获取方法的方法名称与小写字段名称完全相同。但如果是消息类型的字段,其设置方法为mutable_方法,返回值为消息类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改。
对于使用repeated修饰的字段,也就是数组类型,pb 为我们提供了add_方法来新增一个值,
并且提供了_ size 方法来判断数组存放元素的个数。
2.3.1通讯录2.0的写入实现
write.cc (通讯录2.0)
#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;
void AddPeopleInfo(PeopleInfo* people) {cout << "-------------新增联系⼈-------------" << endl;cout << "请输入联系人姓名:";string name;getline(cin,name);people->set_name(name);cout << "请输入联系人年龄:";int age;cin >> age;people->set_age(age);cin.ignore(256,'\n');for(int i = 1; ; ++i) {cout << "请输入联系人电话" << i << "(输入回车完成电话新增):" ;string number;getline(cin,number);if(number.empty()) break;PeopleInfo_Phone* phone = people->add_phone();phone->set_number(number);}cout << "-------------添加联系人成功-------------" << endl;}
int main()
{Contacts contacts;//先读取已经存在的contacts:fstream input("contacts.bin",ios::in | ios ::binary);if(!input) {cout << "file not exist,create new file"<< endl;} else if(!contacts.ParseFromIstream(&input)) {cerr << "Parse failed!" << endl;input.close();return -1;}//向通讯录添加一个联系人:AddPeopleInfo(contacts.add_contacts());//将通讯录写入到本地文件中:fstream output("contacts.bin",ios::out | ios :: binary | ios :: trunc);if(!contacts.SerializePartialToOstream(&output)) {cerr << "write failed !" << endl;input.close();output.close();return -1;}cout << "write sucess !" << endl;input.close();output.close();return 0;
}
makefile:
write:write.cc contacts.pb.ccg++ write.cc contacts.pb.cc -o write -std=c++11 -lprotobuf
.PHONY:clean
clean:rm -rf write
运行write:
2.3.2通讯录2.0的读取实现
read.cc (通讯录2.0)
#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;void PrintContacts(const Contacts& contacts) {for(int i = 0; i < contacts.contacts_size(); ++i) {const PeopleInfo people = contacts.contacts(i);cout << "------------联系⼈" << i+1 << "------------" << endl;cout << "联系人姓名:" << people.name() << endl;cout << "联系人年龄:" << people.age() << endl;int j = 1;for(auto& phone : people.phone()) {cout << "电话" << j++ << ": " << phone.number() << endl;}}
}
int main()
{Contacts contacts;fstream input("contacts.bin",ios::in | ios :: binary);if(!contacts.ParseFromIstream(&input)){cerr << "Parse failed!" << endl;input.close();return -1; } //打印contactsPrintContacts(contacts);input.close();return 0;
}
makefile:
all:write read
write:write.cc contacts.pb.ccg++ write.cc contacts.pb.cc -o write -std=c++11 -lprotobuf
read:read.cc contacts.pb.ccg++ read.cc contacts.pb.cc -o read -std=c++11 -lprotobuf
.PHONY:clean
clean:rm -rf write read
make后运行read
3.enum类型
3.1定义规则
语法支持我们定义枚举类型并使用。在.proto文件中枚举类型的书写规范为:
枚举类型名称:
使用驼峰命名法,首字母大写。例如: MyEnum
常量值名称:
全大写字母,多个字母之间用_连接。 例如: ENUM_ CONST = 0;
我们可以定义一个名为PhoneType的枚举类型,定义如下:
enum PhoneType {MB = 0;TEL = 1;
}
要注意枚举类型的定义有以下几种规则:
1. 0值常量必须存在,且要作为第一-个元素。这是为了与proto2的语义兼容:第一个元素作为默认
值,且值为0。
2.枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)。
3.枚举的常量值在32位整数的范围内。但因负值无效因而不建议使用(与编码规则有关)。
3.2定义时注意
将两个‘具有相同枚举值 名称’的枚举类型放在 单个.proto文件下测试时,编译后会报错:某某某常
量已经被定义!所以这里要注意:
●同级(同层)的枚举类型,各个枚举类型中的常量不能重名。
●单个.proto 文件下,最外层枚举类型和嵌套枚举类型,不算同级。
●多个.proto文件下,若一个文件引入了其他文件,且每个文件都未声明package,每个proto文
件中的枚举类型都在最外层,算同级。
●多个.proto文件下,若一个文件引入了其他文件,且每个文件都声明了package,不算同级。
// ---------------------- 情况1:同级枚举类型包含相同枚举值名称--------------------
enum PhoneType
{MP = 0; // 移动电话TEL = 1; // 固定电话
}
enum PhoneTypeCopy
{MP = 0; // 移动电话 // 编译后报错:MP 已经定义
}
// ---------------------- 情况2:不同级枚举类型包含相同枚举值名称-------------------
enum PhoneTypeCopy
{MP = 0; // 移动电话 // ⽤法正确
}
message Phone
{string number = 1; // 电话号码enum PhoneType{MP = 0; // 移动电话TEL = 1; // 固定电话}
}
// ---------------------- 情况3:多⽂件下都未声明package--------------------
// phone1.proto
import "phone1.proto"
enum PhoneType
{MP = 0; // 移动电话 // 编译后报错:MP 已经定义TEL = 1; // 固定电话
}
// phone2.proto
enum PhoneTypeCopy
{MP = 0; // 移动电话
}
// ---------------------- 情况4:多⽂件下都声明了package--------------------
// phone1.proto
import "phone1.proto" package phone1;
enum PhoneType
{MP = 0; // 移动电话 // ⽤法正确TEL = 1; // 固定电话
}
// phone2.proto
package phone2;
enum PhoneTypeCopy
{MP = 0; // 移动电话
}
3.3升级通讯录至2.1版本
更新contacts.proto (通讯录2.1),新增枚举字段并使用,更新内容如下:
//联系人
message PeopleInfo {string name = 1;int32 age = 2;message Phone {string number = 1;enum PhoneType {MB = 0;TEL = 1;}PhoneType type = 2; }repeated Phone phone = 3;
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}
编译:
protoc --cpp_out=. contacts.proto
// 新⽣成的 PeopleInfo_Phone_PhoneType 枚举类
enum PeopleInfo_Phone_PhoneType : int
{PeopleInfo_Phone_PhoneType_MP = 0,PeopleInfo_Phone_PhoneType_TEL = 1,PeopleInfo_Phone_PhoneType_PeopleInfo_Phone_PhoneType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),PeopleInfo_Phone_PhoneType_PeopleInfo_Phone_PhoneType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
};
// 更新的 PeopleInfo_Phone 类
class PeopleInfo_Phone final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:typedef PeopleInfo_Phone_PhoneType PhoneType;static inline bool PhoneType_IsValid(int value){return PeopleInfo_Phone_PhoneType_IsValid(value);}template <typename T>static inline const std::string &PhoneType_Name(T enum_t_value) { ... }static inline bool PhoneType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PhoneType *value) { ... }// .contacts.PeopleInfo.Phone.PhoneType type = 2;void clear_type();::contacts::PeopleInfo_Phone_PhoneType type() const;void set_type(::contacts::PeopleInfo_Phone_PhoneType value);
};
上述的代码中:
对于在.proto文件中定义的枚举类型,编译生成的代码中会含有与之对应的枚举类型、校验枚举
值是否有效的方法__IsValid、 以及获取枚举值名称的方法_ Name。
●对于使用了枚举类型的字段,包含设置和获取字段的方法,已经清空字段的方法clear__。
更新write.cc(通讯录2.1)
#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;
void AddPeopleInfo(PeopleInfo *people)
{cout << "-------------新增联系⼈-------------" << endl;cout << "请输入联系人姓名:";string name;getline(cin, name);people->set_name(name);cout << "请输入联系人年龄:";int age;cin >> age;people->set_age(age);cin.ignore(256, '\n');for (int i = 1;; ++i){cout << "请输入联系人电话" << i << "(输入回车完成电话新增):";string number;getline(cin, number);if (number.empty())break;PeopleInfo_Phone *phone = people->add_phone();phone->set_number(number);//更新代码:cout << "选择此电话类型 (1、移动电话 2、固定电话) : " ;int type;cin >> type;cin.ignore(256,'\n');switch (type) {case 1:phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MB);break;case 2:phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);break;default:cout << "输入有误,请重新输入" << endl;}}cout << "-------------添加联系人成功-------------" << endl;
}
int main()
{Contacts contacts;// 先读取已经存在的contacts:fstream input("contacts.bin", ios::in | ios ::binary);if (!input){cout << "file not exist,create new file" << endl;}else if (!contacts.ParseFromIstream(&input)){cerr << "Parse failed!" << endl;input.close();return -1;}// 向通讯录添加一个联系人:AddPeopleInfo(contacts.add_contacts());// 将通讯录写入到本地文件中:fstream output("contacts.bin", ios::out | ios ::binary | ios ::trunc);if (!contacts.SerializePartialToOstream(&output)){cerr << "write failed !" << endl;input.close();output.close();return -1;}cout << "write sucess !" << endl;input.close();output.close();return 0;
}
更新read.cc(通讯录2.1)
#include <iostream>
#include <fstream>
#include "contacts.pb.h"using namespace std;
using namespace contacts;void PrintContacts(const Contacts& contacts) {for(int i = 0; i < contacts.contacts_size(); ++i) {const PeopleInfo people = contacts.contacts(i);cout << "------------联系⼈" << i+1 << "------------" << endl;cout << "联系人姓名:" << people.name() << endl;cout << "联系人年龄:" << people.age() << endl;int j = 1;for(auto& phone : people.phone()) {cout << "电话" << j++ << ": " << phone.number();//更新代码:cout << " (" << phone.PhoneType_Name(phone.type()) << ")" << endl;}}
}
int main()
{Contacts contacts;fstream input("contacts.bin",ios::in | ios :: binary);if(!contacts.ParseFromIstream(&input)){cerr << "Parse failed!" << endl;input.close();return -1; } //打印contactsPrintContacts(contacts);input.close();return 0;
}
运行截图:
4. Any类型
字段还可以声明为Any类型,可以理解为泛型类型。使用时可以在Any中存储任意消息类型。Any类型的字段也用repeated来修饰。
Any类型是google已经帮我们定义好的类型,在安装ProtoBuf时,其中的include目录下查找所有
google已经定义好的.proto文件。
4.1升级通讯录至2.2版本
通讯录2.2版本会新增联系人的地址信息,我们可以使用any类型的字段来存储地址信息。
更新contacts.proto (通讯录2.2),更新内容如下:
import "google/protobuf/any.proto"; //引入any.proto文件
//地址:
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 {MB = 0;TEL = 1;}PhoneType type = 2; }repeated Phone phone = 3;google.protobuf.Any data = 4;
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}
编译
protoc --cpp_ out=. contacts.proto
contacts.pb.h更新的部分代码展示:
// 新⽣成的 Address 类
class Address final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const Address &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const Address &from){Address::MergeImpl(*this, from);}// string home_address = 1;void clear_home_address();const std::string &home_address() const;template <typename ArgT0 = const std::string &, typename... ArgT>void set_home_address(ArgT0 &&arg0, ArgT... args);std::string *mutable_home_address();PROTOBUF_NODISCARD std::string *release_home_address();void set_allocated_home_address(std::string *home_address);// string unit_address = 2;void clear_unit_address();const std::string &unit_address() const;template <typename ArgT0 = const std::string &, typename... ArgT>void set_unit_address(ArgT0 &&arg0, ArgT... args);std::string *mutable_unit_address();PROTOBUF_NODISCARD std::string *release_unit_address();void set_allocated_unit_address(std::string *unit_address);
};
// 更新的 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:// .google.protobuf.Any data = 4;bool has_data() const;void clear_data();const ::PROTOBUF_NAMESPACE_ID::Any &data() const;PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Any *release_data();::PROTOBUF_NAMESPACE_ID::Any *mutable_data();void set_allocated_data(::PROTOBUF_NAMESPACE_ID::Any *data);
};
上述的代码中,对于Any类型字段:
●设置和获取:获取方法的方法名称与小写字段名称完全相同。设置方法可以使用mutable_ 方
法,返回值为Any类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行
修改。
之前讲过,我们可以在Any字段中存储任意消息类型,这就要涉及到任意消息类型和Any类型的互
转。这部分代码就在Google为我们写好的头文件any.pb.h中。对any.pb. h部分代码展示:
class PROTOBUF_EXPORT Any final : public ::PROTOBUF_NAMESPACE_ID::Message
{bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message &message){...}bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message *message) const{...}template <typename T>bool Is() const{return _impl_._any_metadata_.Is<T>();}
};
解释:
使用 PackFrom() ⽅法可以将任意消息类型转为 Any 类型。
使用 UnpackTo() ⽅法可以将 Any 类型转回之前设置的任意消息类型。
使用 Is() 方法可以用来判断存放的消息类型是否为 typename T。
更新write.cc(通讯录2.2)
Address address;cout << "请输入联系人家庭地址:";string home_address;getline(cin,home_address);cin.ignore(256,'\n');address.set_home_address(home_address);cout << "请输入联系人单位地址:";string unit_address;getline(cin,unit_address);cin.ignore(256,'\n');address.set_unit_address(unit_address);google::protobuf::Any* data = people->mutable_data();data->PackFrom(address);
更新read.cc(通讯录2.2):
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类型
如果消息中有很多可选字段,并且将来同时只有一个字段会被设置,那么就可以使用oneof 加强这
个行为,也能有节约内存的效果。
5.1升级通讯录至2.3版本
通讯录2.3版本想新增联系人的其他联系方式,比如qq或者微信号二选一, 我们就可以使用oneof字段来加强多选一这个行为。oneof 字段定义的格式为: oneof 字段名{字段1;字段2; ... }
更新contacts.proto (通讯录2.3),更新内容如下:
mport "google/protobuf/any.proto"; //引入any.proto文件
//地址:
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 {MB = 0;TEL = 1;}PhoneType type = 2; }repeated Phone phone = 3;google.protobuf.Any data = 4;oneof other_Ways {string qq = 5;string weixin = 6;}
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}
注意:
●可选字段中的字段编号,不能与非可选字段的编号冲突。
不能在oneof中使用repeated字段。
●将来在设置oneof字段中值时,如果将oneof中的字段设置多个,那么只会保留最后一次设置的成
员,之前设置的oneof成员会自动清除。
编译
protoc --cpp_ out=. contacts.proto
contacts.pb.h更新的部分代码展示:
// 更新的 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message {enum OtherContactCase {kQq = 5,kWeixin = 6,OTHER_CONTACT_NOT_SET = 0,};// string qq = 5;bool has_qq() const;void clear_qq();const std::string& qq() const;template <typename ArgT0 = const std::string&, typename... ArgT>void set_qq(ArgT0&& arg0, ArgT... args);std::string* mutable_qq();PROTOBUF_NODISCARD std::string* release_qq();void set_allocated_qq(std::string* qq);// string weixin = 6;bool has_weixin() const;void clear_weixin();const std::string& weixin() const;template <typename ArgT0 = const std::string&, typename... ArgT>void set_weixin(ArgT0&& arg0, ArgT... args);std::string* mutable_weixin();PROTOBUF_NODISCARD std::string* release_weixin();void set_allocated_weixin(std::string* weixin);void clear_other_contact();OtherContactCase other_contact_case() const;
};
上述的代码中,对于oneof字段:
●会将 oneof中的多个字段定义为一个枚举类型。
●设置和获取:对oneof内的字段进行常规的设置和获取即可,但要注意只能设置- -个。 如果设置
多个,那么只会保留最后一次设置的成员。
●清空oneof字段: clear_ 方法
●获取当前设置了哪个字段:_ case方法
更新write.cc (通讯录2.3),更新内容如下:
cout << "选择添加⼀个其他联系⽅式 (1、qq号 2、微信号) : " ;int other_contacts;cin >> other_contacts;cin.ignore(256,'\n');if(1 == other_contacts) {cout << "请输入qq号: " ;string qq;getline(cin,qq);cin.ignore(256,'\n');people->set_qq(qq);} else if(2 == other_contacts) {cout << "请输入微信号:";string weixin;getline(cin,weixin);cin.ignore(256,'\n');people->set_weixin(weixin);} else {cout << "非法选择,设置失败" << endl;}
更新read.cc (通讯录2.3),更新内容如下:
switch (people.other_Ways_case()) {case PeopleInfo::OtherWaysCase::kQq: cout << "qq号: " << people.qq() << endl;break;case PeopleInfo::OtherWaysCase::kWeixin:cout << "微信号:" << people.weixin() << endl;break;case PeopleInfo::OtherWaysCase::OTHER_WAYS_NOT_SET:break;}
运行截图:
6. map类型
语法支持创建一个关联映射字段,也就是可以使用map类型去声明字段类型,格式为:
map<key_ type, value_type> map _field = N;
要注意的是:
●key_ type是除了float和bytes类型以外的任意标量类型。 value_type可以是任意类型。
map字段不可以用repeated修饰
●map中存入的元素是无序的
6.1升级通讯录至2.4版本
最后,通讯录2.4版本想新增联系人的备注信息,我们可以使用map类型的字段来存储备注信息。
更新contacts.proto (通讯录2.4),更新内容如下:
import "google/protobuf/any.proto"; //引入any.proto文件
//地址:
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 {MB = 0;TEL = 1;}PhoneType type = 2; }repeated Phone phone = 3;google.protobuf.Any data = 4;oneof other_Ways {string qq = 5;string weixin = 6;}map<string,string> remark = 7; //备注
}//通讯录:
message Contacts {repeated PeopleInfo contacts = 1;
}
编译
protoc --cpp_ out=. contacts.proto
contacts.pb.h更新的部分代码展示:
// 更新的 PeopleInfo 类
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message {// map<string, string> remark = 7;int remark_size() const;void clear_remark();const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&remark() const;::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*mutable_remark();
};
上述的代码中,对于Map类型的字段: .
●清空map: clear_ 方法
●设置和获取:获取方法的方法名称与小写字段名称完全相同。设置方法为mutable_ 方法,返回
值为Map类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改。
更新write.cc (通讯录2.4),更新内容如下:
for(int i = 1;; ++i) {cout << "请输⼊备注" << i << "标题 (只输入回车完成备注): ";string remark_key;getline(cin,remark_key);if(remark_key.empty()) {break;}cout << "请输入备注:" << i << "内容:";string remark_value;getline(cin,remark_value);people->mutable_remark()->insert({remark_key,remark_value});}
更新read.cc (通讯录2.4),更新内容如下:
if(people.remark_size()) {cout << "备注信息:" << endl;}for(auto it = people.remark().cbegin(); it != people.remark().cend(); ++it) {cout << " " << it->first << " " << it->second << endl;}
运行截图:
到此,我们对通讯录2.x要求的任务全部完成。在这个过程中我们将通讯录升级到了2.4 版本,同时对ProtoBuf的使用也进一步熟练了 ,并且也掌握了ProtoBuf的proto3语法支持的大部分类型及其使
用,但只是正常使用还是完全不够的。通过接下来的学习,我们就能更进一-步了解到ProtoBuf深入的内容。