Proto3语法详解01

news2024/11/15 14:22:14

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.cc
	g++ 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; 
    } 
    //打印contacts
    PrintContacts(contacts);
    input.close();
    return 0;
}

makefile:

all:write read
write:write.cc contacts.pb.cc
	g++ write.cc contacts.pb.cc -o write -std=c++11 -lprotobuf
read:read.cc contacts.pb.cc
	g++ 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_U
        SE_ = std::numeric_limits<int32_t>::min(),
    PeopleInfo_Phone_PhoneType_PeopleInfo_Phone_PhoneType_INT_MAX_SENTINEL_DO_NOT_U
        SE_ = 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; 
    } 
    //打印contacts
    PrintContacts(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深入的内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1251106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS新特性(2-2)

CSS新特性&#xff08;2-2&#xff09; 前言box相关box-shadow background背景rgba颜色与透明度transform:rotate(Xdeg) 2D旋转transform:tranlate 平移 前言 本文继续讲解CSS3其他的新特性&#xff0c;想看之前新特性点击这里&#xff0c;那么好本文正式开始。 box相关 box…

BUUCTF [GUET-CTF2019]KO 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 得到的 flag 请包上 flag{} 提交。 密文&#xff1a; 下载附件得到一个.txt文本。 解题思路&#xff1a; 1、结合题目提示和文本特征&#xff0c;可以确定为Ook&#xff01;编码。 Ook. Ook. Ook. Ook. Ook. Ook…

MyBatis的功能架构,MyBatis的框架架构设计,Mybatis都有哪些Executor执行器,Mybatis中如何指定使用哪一种Executor执行器

文章目录 MyBatis的功能架构是怎样的把Mybatis的功能架构分为三层&#xff1a; **MyBatis的框架架构设计**是怎么样的架构图如下Mybatis都有哪些Executor执行器&#xff1f;它们之间的区别是什么&#xff1f;Mybatis中如何指定使用哪一种Executor执行器&#xff1f; MyBatis的功…

FreeRTOS学习之路,以STM32F103C8T6为实验MCU(2-4:内核控制与时间管理函数)

学习之路主要为FreeRTOS操作系统在STM32F103&#xff08;STM32F103C8T6&#xff09;上的运用&#xff0c;采用的是标准库编程的方式&#xff0c;使用的IDE为KEIL5。 注意&#xff01;&#xff01;&#xff01;本学习之路可以通过购买STM32最小系统板以及部分配件的方式进行学习…

jconsole的基本使用和死锁的检测

jconsole的基本使用和死锁的检测 因为jconsole是JDK自带的&#xff0c;所以安装了JDK就可以直接打开了。 1. 打开方式 cmd命令行打开&#xff1a;输入jconsole&#xff0c;然后按Enter JDK安装目录&#xff0c;bin目录下&#xff0c;双击即可打开 选择一个进程然后打开 可…

怎样禁止邮件发送涉密信息

数字化时代&#xff0c;电子邮件已成为人们生活和工作中不可或缺的通讯工具。然而&#xff0c;随着互联网的普及&#xff0c;涉密信息的泄露风险也随之增加。为了保护敏感数据&#xff0c;禁止邮件发送涉密信息显得尤为重要。以下是一些建议&#xff0c;帮助你实现这一目标。 1…

数据丢失抢救神器之TOP10 Android 数据恢复榜单

在快节奏的数字时代&#xff0c;我们的生活越来越与智能手机交织在一起&#xff0c;使它们成为重要数据和珍贵记忆的存储库。由于意外删除、软件故障或硬件故障而丢失数据可能是一种痛苦的经历。值得庆幸的是&#xff0c;技术领域提供了 Android 数据恢复软件形式的解决方案。这…

【每日一题】二叉树中的伪回文路径

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;递归&#xff08;DFS&#xff09;方法二&#xff1a;位运算 写在最后 Tag 【递归/DFS】【伪回文】【二叉树】【2023-11-25】 题目来源 1457. 二叉树中的伪回文路径 题目解读 伪回文路径指的是路径中的节点值经过重新…

Linux基本指令汇总

本专栏内容为&#xff1a;Linux学习专栏&#xff0c;分为系统和网络两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握Linux。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;Linux从入门到精通 &#x1f69a;代码仓库&#xff1a;小…

buuctf web [极客大挑战 2019]PHP

提示有备份,dirsearch扫描网站备份 GitHub - maurosoria/dirsearch: Web path scanner下载.zip格式文件 解压到python目录下 在上图位置cmd打开窗口 输入python setup.py install安装dirseach 安装好后输入命令使用dirseach python dirseach.py -u http://44296191-973d-448…

电子学会C/C++编程等级考试2021年09月(二级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:字符统计 给定一个由a-z这26个字符组成的字符串,统计其中哪个字符出现的次数最多。输入 输入包含一行,一个字符串,长度不超过1000。输出 输出一行,包括出现次数最多的字符和该字符出现的次数,中间以一个空格分开。如果有多…

微信消息推送说明

1 打开任务清单 2 编辑任务清单设置 名字解释 姓名&#xff1a;微信名字 内容&#xff1a;要发送消息 定时&#xff1a;从几点开始发送 每隔几分钟&#xff1a;每隔几分钟重复发送一次 重复次数&#xff1a;每隔几分钟重复发送几次 响玲&#xff1a;定时语音电话&#x…

Redis主从复制(新)

像一些大型的项目&#xff0c;为了保证系统的稳定性&#xff0c;会有一台Redis服务器专门作为主机(master)&#xff0c;还会有多台服务器作为从机(slave)&#xff0c;主机可专门用作写数据&#xff0c;然后根据配置和策略&#xff0c;自动将数据同步到从机&#xff0c;而从机专…

Redis数据备份和还原

Redis SAVE 命令用于创建当前数据库的备份文件&#xff0c;文件名默认为dump.rdb。备份数据库数据可以增强对数据的保护&#xff0c;提升数据的安全性。当数据不小心丢失或者被删除时&#xff0c;我们就可以通过相应的操作进行数据恢复。本节介绍 Redis 的数据备份和数据还原操…

mybatis的使用,mybatis的实现原理,mybatis的优缺点,MyBatis缓存,MyBatis运行的原理,MyBatis的编写方式

文章目录 MyBatis简介结构图Mybatis缓存&#xff08;一级缓存、二级缓存&#xff09;MyBatis是什么&#xff1f;mybatis的实现原理JDBC编程有哪些不足之处&#xff0c;MyBatis是如何解决这些问题的&#xff1f;Mybatis优缺点优点缺点映射关系 MyBatis的解析和运行原理MyBatis的…

浅谈安科瑞剩余电流继电器在智能建筑中的应用

摘要&#xff1a;分析了智能建筑应用剩余电流继电器的必要性&#xff0c;介绍了ASJ剩余电流继电器的主要功能、工作原理、分类情况和提出了在选择剩余电流保护断路器时的原则和注意事项。 Abstract: the necessity of applying residual current relay in intelligent buildin…

不同视频格式如何一键生成二维码?二维码视频制作在线技巧

现在通过扫码看视频是很多人在使用的一种方式&#xff0c;通过制作视频二维码来引导用户了解自己的内容&#xff0c;但是视频有很多种不同的格式&#xff0c;那么有什么方法能够将不同格式的视频生成二维码呢&#xff1f;下面来教大家一招&#xff0c;通过在线的视频二维码生成…

MLFlow 入门(Model管理,生命周期管理)

最近需求需要使用mlflow&#xff0c;去学习了下&#xff0c;记录。 简介 MLflow是一个开源平台&#xff0c;专门为了帮助机器学习的从业者和团队处理机器学习过程中的复杂性而设计的。MLflow关注机器学习项目的完整生命周期&#xff0c;确保每个阶段都是可管理的、可追溯的和可…

邮件泄密案例分析

近日&#xff0c;一起令人震惊的事件在美军方内部引发了广泛关注。据报道&#xff0c;美军方意外将数百万封包含敏感信息的邮件发至非洲国家马里。这些邮件涉及的内容十分广泛&#xff0c;包括军事行动计划、人员部署、战术策略等&#xff0c;甚至还有部分涉及国家安全和战略决…

前缀和——238. 除自身以外数组的乘积

文章目录 &#x1f377;1. 题目&#x1f378;2. 算法原理&#x1f365;解法一&#xff1a;暴力求解&#x1f365;解法二&#xff1a;前缀和&#xff08;积&#xff09; &#x1f379;3. 代码实现 &#x1f377;1. 题目 题目链接&#xff1a;238. 除自身以外数组的乘积 - 力扣&a…