语法⽀持我们定义枚举类型并使⽤。在.proto⽂件中枚举类型的书写规范为:
枚举类型名称:
使⽤驼峰命名法,⾸字⺟⼤写。例如: MyEnum常量值名称:
全⼤写字⺟,多个字⺟之间⽤连接。例如: ENUM_CONST = 0;
比如我们定义一个PhoneType类型
enum PhoneType{
MP= 0; //移动电话
TEL= 1; //固定电话
}
要注意枚举类型的定义有以下⼏种规则:
将两个‘具有相同枚举值名称’的枚举类型放在单个.proto⽂件下测试时,编译后会报错:某某某常
量已经被定义!所以这⾥要注意:
情况一:同级出现常量重名的情况
syntax="proto3";
package enum1;
enum PhoneType1 {
MP=0;
TEL=1;
}
enum PhoneType2 {
MP=0;
}
情况二:嵌套
enum PhoneType1 {
MP=0;
TEL=1;
}
message phone {
string number=1;
enum PhoneType2 {
MP=0;
}
}
情况三:多文件未使用package
编译,发现报错。错误信息为重复定义。
情况四:多文件使用package
更新通讯录。为电话号码加上类型。
修改contacts.proto文件
enum PhoneType {
MP=0; //移动电话
TEL=1; //固定电话
}
message Phone {
string number=1;
PhoneType type=2;
}
//定义联系人消息
message PeopleInfo{
string name=1;
string age=2;
repeated Phone phone=3;
}
message Contacts{
repeated PeopleInfo people=1;
}
更新write.cc文件:添加电话的类型
void AddPeopleInfo(contacts1::Contacts *contacts_ptr)
{
for (int j = 0;; j++)
{
std::cout << "--------- 新增联系人(输入“退出”即可结束新增联系人) --------" << std::endl;
std::cout << " 请输入联系人姓名: ";
string name;
getline(std::cin, name);
if(name=="退出"){
break;
}
contacts1::PeopleInfo *peopleinfo = contacts_ptr->add_people(); //添加一个people消息
peopleinfo->set_name(name);
std::cout << " 请输入联系人年龄: ";
string age;
getline(std::cin, age);
peopleinfo->set_age(age);
for (int i = 0;; i++)
{
std::cout << " 请输入联系人电话 " << i + 1 << "(只输入回车完成电话的新增): ";
string number;
getline(std::cin, number);
if (number.empty())
{
break;
}
contacts1::Phone *phone = peopleinfo->add_phone(); //添加一个phone信息
phone->set_number(number);
std::cout<< " 请输入电话类型:(MP/移动电话:0 TEL/固定电话:1): ";
int type=0;
std::cin>>type;
std::cin.ignore(256,'\n');
switch (type)
{
case 0:
phone->set_type(contacts1::MP);
break;
case 1:
phone->set_type(contacts1::TEL);
break;
default:
cout << "⾮法选择,使⽤默认值!" << endl;
break;
}
}
}
}
执行结果:
更新read.cc文件
void PrintContacts(contacts1::Contacts& contacts){
for(int i=0;i<contacts.people_size();i++){
contacts1::PeopleInfo peopleinfo=contacts.people(i); //通过下标获取people
std:cout<<"----------联系人"<<i+1<<"----------"<<std::endl;
std::cout<<"联系人姓名: "<<peopleinfo.name()<<std::endl;
std::cout<<"联系人年龄: "<<peopleinfo.age()<<std::endl;
int j=1;
for(;j<=peopleinfo.phone_size();j++){
std::cout<<"电话: "<<peopleinfo.phone(j-1).number();
contacts1::Phone phone=peopleinfo.phone(j-1);
std::cout<<" 类型["<<phone.type()<<"]"<<std::endl;
}
}
}
输出结果:
当我们未设置值时,默认第一个值为默认值。
字段还可以声明为Any类型,可以理解为泛型类型。使⽤时可以在Any中存储任意消息类型。Any类型的字段也⽤repeated来修饰。(可以理解为Go语言中的interface)
Any类型是google已经帮我们定义好的类型,在安装ProtoBuf时,其中的include⽬录下查找所有google已经定义好的.proto⽂件。
修改contacts.proto文件,添加Any类型。
syntax="proto3";
package contacts1;
import "google/protobuf/any.proto"; //引入any.proto文件
message Address{
string home_address=1; //家庭地址
string unit_address=2; //单位地址
}
enum PhoneType {
MP=0; //移动电话
TEL=1; //固定电话
}
message Phone {
string number=1;
PhoneType type=2;
}
//定义联系人消息
message PeopleInfo{
string name=1;
string age=2;
repeated Phone phone=3;
google.protobuf.Any address=4;
}
message Contacts{
repeated PeopleInfo people=1;
}
修改write.cc:将message Address转化为Any类型
void AddPeopleInfo(contacts1::Contacts *contacts_ptr)
{
for (int j = 0;; j++)
{
std::cout << "--------- 新增联系人(输入“退出”即可结束新增联系人) --------" << std::endl;
std::cout << " 请输入联系人姓名: ";
string name;
getline(std::cin, name);
if(name=="退出"){
break;
}
contacts1::PeopleInfo *peopleinfo = contacts_ptr->add_people(); //添加一个people消息
peopleinfo->set_name(name);
std::cout << " 请输入联系人年龄: ";
string age;
getline(std::cin, age);
peopleinfo->set_age(age);
for (int i = 0;; i++)
{
std::cout << " 请输入联系人电话 " << i + 1 << "(只输入回车完成电话的新增): ";
string number;
getline(std::cin, number);
if (number.empty())
{
break;
}
contacts1::Phone *phone = peopleinfo->add_phone(); //添加一个phone信息
phone->set_number(number);
std::cout<< " 请输入电话类型:(MP/移动电话:0 TEL/固定电话:1): ";
int type=0;
std::cin>>type;
std::cin.ignore(256,'\n');
switch (type)
{
case 0:
phone->set_type(contacts1::MP);
break;
case 1:
phone->set_type(contacts1::TEL);
break;
default:
std::cout << "⾮法选择,使⽤默认值!" << std::endl;
break;
}
}
contacts1::Address address;
std::cout<<" 请输入家庭地址:";
string home_address;
getline(std::cin,home_address);
address.set_home_address(home_address);
std::cout<<" 请输入单位地址:";
string unit_address;
getline(std::cin,unit_address);
address.set_unit_address(unit_address);
google::protobuf::Any * data=peopleinfo->mutable_address(); //若该对象存在,则直接返回该对象,若不存在则新new 一个。
//类型转化:转化为Any类型。Any类型也就是通用类型
data->PackFrom(address);
}
}
修改read.cc:将Any类型转化为message address类型
void PrintContacts(const contacts1::Contacts& contacts){
for(int i=0;i<contacts.people_size();i++){
const contacts1::PeopleInfo& peopleinfo=contacts.people(i); //通过下标获取people
std::cout<<"----------联系人"<<i+1<<"----------"<<std::endl;
std::cout<<"联系人姓名: "<<peopleinfo.name()<<std::endl;
std::cout<<"联系人年龄: "<<peopleinfo.age()<<std::endl;
int j=1;
for(;j<=peopleinfo.phone_size();j++){
std::cout<<"电话: "<<peopleinfo.phone(j-1).number();
contacts1::Phone phone=peopleinfo.phone(j-1);
std::cout<<" 类型["<<phone.type()<<"]"<<std::endl;
}
//如果有address成员,并且address成员的类型是Address
if(peopleinfo.has_address()&&peopleinfo.address().Is<contacts1::Address>()){
contacts1::Address address;
peopleinfo.address().UnpackTo(&address);
//如果家庭地址不为空
if(!address.home_address().empty()){
std::cout<<" 家庭地址:"<<address.home_address()<<std::endl;
}
//如果单位地址不为空
if(!address.unit_address().empty()){
std::cout<<" 单位地址:"<<address.unit_address()<<std::endl;
}
}
}
如果消息中有很多可选字段,并且将来同时只有⼀个字段会被设置,那么就可以使⽤oneof 加强这个⾏为,也能有节约内存的效果。
考虑以下的场景:一个人的性别只能说男或者女。只能是两种性别中的一个,既不能都选也不能都不选。
message Address{
string home_address=1; //家庭地址
string unit_address=2; //单位地址
}
enum PhoneType {
MP=0; //移动电话
TEL=1; //固定电话
}
message Phone {
string number=1;
PhoneType type=2;
}
//定义联系人消息
message PeopleInfo{
string name=1;
string age=2;
repeated Phone phone=3;
google.protobuf.Any address=4;
oneof gender {
string male=5;
string female=6;
}
}
message Contacts{
repeated PeopleInfo people=1;
}
可以看到oneof类型都被转化为了enum类型,其成员就是oneof包含的值加上NOT_SET。
注意:
修改write.cc文件:添加oneof类型
void AddPeopleInfo(contacts1::Contacts *contacts_ptr)
{
for (int j = 0;; j++)
{
std::cout << "--------- 新增联系人(输入“退出”即可结束新增联系人) --------" << std::endl;
std::cout << " 请输入联系人姓名: ";
string name;
getline(std::cin, name);
if(name=="退出"){
break;
}
contacts1::PeopleInfo *peopleinfo = contacts_ptr->add_people(); //添加一个people消息
peopleinfo->set_name(name);
std::cout << " 请输入联系人年龄: ";
string age;
getline(std::cin, age);
peopleinfo->set_age(age);
for (int i = 0;; i++)
{
std::cout << " 请输入联系人电话 " << i + 1 << "(只输入回车完成电话的新增): ";
string number;
getline(std::cin, number);
if (number.empty())
{
break;
}
contacts1::Phone *phone = peopleinfo->add_phone(); //添加一个phone信息
phone->set_number(number);
std::cout<< " 请输入电话类型:(MP/移动电话:0 TEL/固定电话:1): ";
int type=0;
std::cin>>type;
std::cin.ignore(256,'\n');
switch (type)
{
case 0:
phone->set_type(contacts1::MP);
break;
case 1:
phone->set_type(contacts1::TEL);
break;
default:
std::cout << "⾮法选择,使⽤默认值!" << std::endl;
break;
}
}
contacts1::Address address;
std::cout<<" 请输入家庭地址:";
string home_address;
getline(std::cin,home_address);
address.set_home_address(home_address);
std::cout<<" 请输入单位地址:";
string unit_address;
getline(std::cin,unit_address);
address.set_unit_address(unit_address);
google::protobuf::Any * data=peopleinfo->mutable_address(); //若该对象存在,则直接返回该对象,若不存在则新new 一个。
//类型转化:转化为Any类型
data->PackFrom(address);
//设置oneof类型
std::cout<<" 请输入联系人性别:(1.male 2.female):";
int male=0;
std::cin>>male;
std::cin.ignore(256,'\n');
if(male==1){
string male="male";
peopleinfo->set_male(male);
}
else if(male==2){
string female="female";
peopleinfo->set_female(female);
}
else{
std::cout<<" 非法选择,设置为默认值!"<<std::endl;
}
}
}
修改read.cc文件:读取oneof类型
void PrintContacts(const contacts1::Contacts& contacts){
for(int i=0;i<contacts.people_size();i++){
const contacts1::PeopleInfo& peopleinfo=contacts.people(i); //通过下标获取people
std::cout<<"----------联系人"<<i+1<<"----------"<<std::endl;
std::cout<<"联系人姓名: "<<peopleinfo.name()<<std::endl;
std::cout<<"联系人年龄: "<<peopleinfo.age()<<std::endl;
int j=1;
for(;j<=peopleinfo.phone_size();j++){
std::cout<<"电话: "<<peopleinfo.phone(j-1).number();
contacts1::Phone phone=peopleinfo.phone(j-1);
std::cout<<" 类型["<<phone.type()<<"]"<<std::endl;
}
//如果有address成员,并且address成员的类型是Address
if(peopleinfo.has_address()&&peopleinfo.address().Is<contacts1::Address>()){
contacts1::Address address;
peopleinfo.address().UnpackTo(&address);
if(!address.home_address().empty()){
std::cout<<"家庭地址:"<<address.home_address()<<std::endl;
}
if(!address.unit_address().empty()){
std::cout<<"单位地址:"<<address.unit_address()<<std::endl;
}
}
//读取oneof类型
/*
第一种写法
if (people.has_qq()) {
} else if (people.has_weixin()) {
} */
//写法二
switch (peopleinfo.gender_case())
{
case contacts1::PeopleInfo::kMale:
std::cout<<"性别:male"<<std::endl;
break;
case contacts1::PeopleInfo::kFemale:
std::cout<<"性别:female"<<std::endl;
break;
case contacts1::PeopleInfo::GENDER_NOT_SET:
std::cout<<"性别:未设置"<<std::endl;
break;
default:
std::cout<<"未知错误! "<<std::endl;
break;
}
}
}
语法⽀持创建⼀个关联映射字段,也就是可以使⽤map类型去声明字段类型,格式为:
map
要注意的是:
message Address{
string home_address=1; //家庭地址
string unit_address=2; //单位地址
}
enum PhoneType {
MP=0; //移动电话
TEL=1; //固定电话
}
message Phone {
string number=1;
PhoneType type=2;
}
//定义联系人消息
message PeopleInfo{
string name=1;
string age=2;
repeated Phone phone=3;
google.protobuf.Any address=4;
oneof gender {
string male=5;
string female=6;
}
map remarks=7;
}
message Contacts{
repeated PeopleInfo people=1;
}
上述的代码中,对于Map类型的字段:
修改write.cc文件:添加备注项
void AddPeopleInfo(contacts1::Contacts *contacts_ptr)
{
for (int j = 0;; j++)
{
std::cout << "--------- 新增联系人(输入“退出”即可结束新增联系人) --------" << std::endl;
std::cout << " 请输入联系人姓名: ";
string name;
getline(std::cin, name);
if(name=="退出"){
break;
}
contacts1::PeopleInfo *peopleinfo = contacts_ptr->add_people(); //添加一个people消息
peopleinfo->set_name(name);
std::cout << " 请输入联系人年龄: ";
string age;
getline(std::cin, age);
peopleinfo->set_age(age);
for (int i = 0;; i++)
{
std::cout << " 请输入联系人电话 " << i + 1 << "(只输入回车完成电话的新增): ";
string number;
getline(std::cin, number);
if (number.empty())
{
break;
}
contacts1::Phone *phone = peopleinfo->add_phone(); //添加一个phone信息
phone->set_number(number);
std::cout<< " 请输入电话类型:(MP/移动电话:0 TEL/固定电话:1): ";
int type=0;
std::cin>>type;
std::cin.ignore(256,'\n');
switch (type)
{
case 0:
phone->set_type(contacts1::MP);
break;
case 1:
phone->set_type(contacts1::TEL);
break;
default:
std::cout << "⾮法选择,使⽤默认值!" << std::endl;
break;
}
}
//增加Any类型
contacts1::Address address;
std::cout<<" 请输入家庭地址:";
string home_address;
getline(std::cin,home_address);
address.set_home_address(home_address);
std::cout<<" 请输入单位地址:";
string unit_address;
getline(std::cin,unit_address);
address.set_unit_address(unit_address);
google::protobuf::Any * data=peopleinfo->mutable_address(); //若该对象存在,则直接返回该对象,若不存在则新new 一个。
//类型转化:转化为Any类型
data->PackFrom(address);
//增加oneof类型
std::cout<<" 请输入联系人性别:(1.male 2.female):";
int male=0;
std::cin>>male;
std::cin.ignore(256,'\n');
if(male==1){
string male="male";
peopleinfo->set_male(male);
}
else if(male==2){
string female="female";
peopleinfo->set_female(female);
}
else{
std::cout<<" 非法选择,设置为默认值!"<<std::endl;
}
//增加map类型
for(int i=1;;i++){
std::cout<<"添加第"<<i<<"条备注的标题:(只输入回车表示添加结束)";
string remark_key;
getline(std::cin,remark_key);
if(remark_key.empty()){
break;
}
std::cout<<"添加第"<<i<<"条备注的内容:";
string remark_value;
getline(std::cin,remark_value);
//开辟remarks空间
google::protobuf::Map<std::string, std::string> *remarks=peopleinfo->mutable_remarks();
remarks->insert({remark_key,remark_value});
}
}
}
修改read.cc文件
void PrintContacts(const contacts1::Contacts& contacts){
for(int i=0;i<contacts.people_size();i++){
const contacts1::PeopleInfo& peopleinfo=contacts.people(i); //通过下标获取people
std::cout<<"----------联系人"<<i+1<<"----------"<<std::endl;
std::cout<<"联系人姓名: "<<peopleinfo.name()<<std::endl;
std::cout<<"联系人年龄: "<<peopleinfo.age()<<std::endl;
int j=1;
for(;j<=peopleinfo.phone_size();j++){
std::cout<<"电话: "<<peopleinfo.phone(j-1).number();
contacts1::Phone phone=peopleinfo.phone(j-1);
std::cout<<" 类型["<<phone.type()<<"]"<<std::endl;
}
//如果有address成员,并且address成员的类型是Address
if(peopleinfo.has_address()&&peopleinfo.address().Is<contacts1::Address>()){
contacts1::Address address;
peopleinfo.address().UnpackTo(&address);
if(!address.home_address().empty()){
std::cout<<"家庭地址:"<<address.home_address()<<std::endl;
}
if(!address.unit_address().empty()){
std::cout<<"单位地址:"<<address.unit_address()<<std::endl;
}
}
/*
第一种写法
if (people.has_qq()) {
} else if (people.has_weixin()) {
} */
//写法二
switch (peopleinfo.gender_case())
{
case contacts1::PeopleInfo::kMale:
std::cout<<"性别:male"<<std::endl;
break;
case contacts1::PeopleInfo::kFemale:
std::cout<<"性别:female"<<std::endl;
break;
case contacts1::PeopleInfo::GENDER_NOT_SET:
std::cout<<"性别:未设置"<<std::endl;
break;
default:
std::cout<<"未知错误! "<<std::endl;
break;
}
//打印备注
if(peopleinfo.remarks_size()){
for(auto it=peopleinfo.remarks().cbegin();it!=peopleinfo.remarks().end();it++){
std::cout << " " << it->first << ": " << it->second << std::endl;
}
}
}
反序列化消息时,如果被反序列化的⼆进制序列中不包含某个字段,反序列化对象中相应字段时,就会设置为该字段的默认值。不同的类型对应的默认值不同:
当我们需要删除或者修改一些字段时,如果是直接进行修改就会出现错误。因此需要使用reserved关键字。
我们将文件分别在client和server目录下保留一份。然后修改server目录下contacts.proto文件的内容:
分别执行read程序和server程序观察结果
执行write程序
执行read程序
直接进行修改,就会出现数据丢失的现象。这和protobuf的规则有关:protobuf通过字段序号进行确认。
更新规则
如果现有的消息类型已经不再满⾜我们的需求,例如需要扩展⼀个字段,在不破坏任何现有代码的情况下更新消息类型⾮常简单。遵循如下规则即可 :
message Message {
// 设置保留项
reserved 100, 101, 200 to 299;
reserved "field3", "field4";
// 注意:不要在⼀⾏ reserved 声明中同时声明字段编号和名称。
// reserved 102, "field5";
// 设置保留项之后,下⾯代码会告警
int32 field1 = 100; //告警:Field 'field1' uses reserved number 100
int32 field2 = 101; //告警:Field 'field2' uses reserved number 101
int32 field3 = 102; //告警:Field name 'field3' is reserved
int32 field4 = 103; //告警:Field name 'field4' is reserved
}
使用reserved关键字,修改server的消息
执行write程序
执行read程序
在上面的通讯录程序中,我们向service⽬录下的contacts.proto新增了‘birthday’字段,但对于client相关的代码并没有任何改动。验证后发现新代码序列化的消息(service)也可以被旧代码(client)解析。并且这⾥要说的是,新增的‘⽣⽇’字段在旧程序(client)中其实并没有丢失,⽽是会作为旧程序的未知字段。
打印未知字段
修改client.cc文件的内容
void PrintContacts(const r_contacts::Contacts &contacts)
{
for (int i = 0; i < contacts.people_size(); i++)
{
const r_contacts::PeopleInfo &peopleinfo = contacts.people(i); // 通过下标获取people
std::cout << "----------联系人" << i + 1 << "----------" << std::endl;
std::cout << "联系人姓名: " << peopleinfo.name() << std::endl;
std::cout << "联系人年龄: " << peopleinfo.age() << std::endl;
int j = 1;
for (; j <= peopleinfo.phone_size(); j++)
{
std::cout << "电话: " << peopleinfo.phone(j - 1).number();
r_contacts::Phone phone = peopleinfo.phone(j - 1);
std::cout << " 类型[" << phone.type() << "]" << std::endl;
}
// 如果有address成员,并且address成员的类型是Address
if (peopleinfo.has_address() && peopleinfo.address().Is<r_contacts::Address>())
{
r_contacts::Address address;
peopleinfo.address().UnpackTo(&address);
if (!address.home_address().empty())
{
std::cout << "家庭地址:" << address.home_address() << std::endl;
}
if (!address.unit_address().empty())
{
std::cout << "单位地址:" << address.unit_address() << std::endl;
}
}
/*
第一种写法
if (people.has_qq()) {
} else if (people.has_weixin()) {
} */
// 写法二
switch (peopleinfo.gender_case())
{
case r_contacts::PeopleInfo::kMale:
std::cout << "性别:male" << std::endl;
break;
case r_contacts::PeopleInfo::kFemale:
std::cout << "性别:female" << std::endl;
break;
case r_contacts::PeopleInfo::GENDER_NOT_SET:
std::cout << "性别:未设置" << std::endl;
break;
default:
std::cout << "未知错误! " << std::endl;
break;
}
// 打印备注
if (peopleinfo.remarks_size())
{
for (auto it = peopleinfo.remarks().cbegin(); it != peopleinfo.remarks().end(); it++)
{
std::cout << " " << it->first << ": " << it->second << std::endl;
}
}
/*
打印未知字段
*/
const google::protobuf::Reflection *reflect = r_contacts::PeopleInfo::GetReflection(); // 获取未知字段
const google::protobuf::UnknownFieldSet &unknowset = reflect->GetUnknownFields(peopleinfo);
for (int j = 0; j < unknowset.field_count(); j++)
{
const google::protobuf::UnknownField &unknow_field = unknowset.field(j);
cout << "未知字段" << j + 1 << ":"
<< " 字段编号: " << unknow_field.number()
<< " 类型: " << unknow_field.type();
switch (unknow_field.type())
{
case google::protobuf::UnknownField::Type::TYPE_VARINT:
cout << " 值: " << unknow_field.varint() << endl;
break;
case google::protobuf::UnknownField::Type::TYPE_LENGTH_DELIMITED:
cout << " 值: " << unknow_field.length_delimited() << endl;
break;
}
}
}
}
MessageLite类介绍
**Message类介绍 **
我们⾃定义的message类,都是继承⾃Message。
//google::protobuf::Message 部分代码展⽰
const Descriptor* GetDescriptor() const;
const Reflection* GetReflection() const;
**Descriptor类介绍 **
// 部分代码展⽰
class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase {
string& name () const
int field_count() const;
const FieldDescriptor* field(int index) const;
const FieldDescriptor* FindFieldByNumber(int number) const;
const FieldDescriptor* FindFieldByName(const std::string& name) const;
const FieldDescriptor* FindFieldByLowercaseName(
const std::string& lowercase_name) const;
const FieldDescriptor* FindFieldByCamelcaseName(
const std::string& camelcase_name) const;
int enum_type_count() const;
const EnumDescriptor* enum_type(int index) const;
const EnumDescriptor* FindEnumTypeByName(const std::string& name) const;
const EnumValueDescriptor* FindEnumValueByName(const std::string& name)const;
}
**Reflection类介绍 **
Reflection接⼝类,主要提供了动态读写消息字段的接⼝,对消息对象的⾃动读写主要通过该类完成。
提供⽅法来动态访问/修改message中的字段,对每种类型,Reflection都提供了⼀个单独的接⼝⽤于读写字段对应的值。
◦ 针对所有不同的field类型 FieldDescriptor::TYPE_* ,需要使⽤不同的 Get*()/Set*()/Add*() 接⼝;
◦ repeated类型需要使⽤ GetRepeated*()/SetRepeated*() 接⼝,不可以和⾮repeated
类型接⼝混⽤;
◦ message对象只可以被由它⾃⾝的 reflection(message.GetReflection()) 来操
作;
类中还包含了访问/修改未知字段的⽅法。
类定义在google提供的message.h中。
UnknownFieldSet类介绍
class PROTOBUF_EXPORT UnknownFieldSet {
inline void Clear();
void ClearAndFreeMemory();
inline bool empty() const;
inline int field_count() const;
inline const UnknownField& field(int index) const;
inline UnknownField* mutable_field(int index);
// Adding fields ---------------------------------------------------
void AddVarint(int number, uint64_t value);
void AddFixed32(int number, uint32_t value);
void AddFixed64(int number, uint64_t value);
void AddLengthDelimited(int number, const std::string& value);
std::string* AddLengthDelimited(int number);
UnknownFieldSet* AddGroup(int number);
// Parsing helpers -------------------------------------------------
// These work exactly like the similarly-named methods of Message.
bool MergeFromCodedStream(io::CodedInputStream* input);
bool ParseFromCodedStream(io::CodedInputStream* input);
bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);
bool ParseFromArray(const void* data, int size);
inline bool ParseFromString(const std::string& data) {
return ParseFromArray(data.data(), static_cast<int>(data.size()));
}
// Serialization.
bool SerializeToString(std::string* output) const;
bool SerializeToCodedStream(io::CodedOutputStream* output) const;
static const UnknownFieldSet& default_instance();
};
**UnknownField类介绍 **
class PROTOBUF_EXPORT UnknownField {
public:
enum Type {
TYPE_VARINT,
TYPE_FIXED32,
TYPE_FIXED64,
TYPE_LENGTH_DELIMITED,
TYPE_GROUP
};
inline int number() const;
inline Type type() const;
// Each method works only for UnknownFields of the corresponding type.
inline uint64_t varint() const;
inline uint32_t fixed32() const;
inline uint64_t fixed64() const;
inline const std::string& length_delimited() const;
inline const UnknownFieldSet& group() const;
inline void set_varint(uint64_t value);
inline void set_fixed32(uint32_t value);
inline void set_fixed64(uint64_t value);
inline void set_length_delimited(const std::string& value);
inline std::string* mutable_length_delimited();
inline UnknownFieldSet* mutable_group();
};
.proto⽂件中可以声明许多选项,使⽤option标注。选项能影响proto编译器的某些处理⽅式。
选项分类
syntax = "proto2"; // descriptor.proto 使⽤ proto2 语法版本
message FileOptions { ... } // ⽂件选项 定义在 FileOptions 消息中
message MessageOptions { ... } // 消息类型选项 定义在 MessageOptions 消息中
message FieldOptions { ... } // 消息字段选项 定义在 FieldOptions 消息中
message OneofOptions { ... } // oneof字段选项 定义在 OneofOptions 消息中
message EnumOptions { ... } // 枚举类型选项 定义在 EnumOptions 消息中
message EnumValueOptions { .. } // 枚举值选项 定义在 EnumValueOptions 消息中
message ServiceOptions { ... } // 服务选项 定义在 ServiceOptions 消息中
message MethodOptions { ... } // 服务⽅法选项 定义在 MethodOptions 消息中
...
由此可⻅,选项分为 ⽂件级、消息级、字段级等等,但并没有⼀种选项能作⽤于所有的类型 。
**常⽤选项列举 **
option optimize_for = LITE_RUNTIME;
enum PhoneType {
option allow_alias = true;
MP = 0;
TEL = 1;
LANDLINE = 1; // 若不加 option allow_alias = true; 这⼀⾏会编译报错
}