1. 编写proto文件

首先需要一个proto文件,其中定义了我们程序中需要处理的结构化数据:

// Filename: addressbook.proto
 
 syntax="proto2";
 package addressbook;
 
 import "src/help.proto";      //举例用,编译时去掉
 
 message Person {
     required string name = 1;
     required int32 id = 2;
     optional string email = 3;
 
     enum PhoneType {
         MOBILE = 0;
         HOME = 1;
         WORK = 2;
     }
 
     message PhoneNumber {
         required string number = 1;
         optional PhoneType type = 2 [default = HOME];
     }
 
     repeated PhoneNumber phone = 4;
 }
 
 message AddressBook {
     repeated Person person_info = 1;
}

2. 代码解释 

// Filename: addressbook.proto


syntax="proto2";


package addressbook; 声明了一个包名,用来防止不同的消息类型命名冲突,类似于 namespace


import "src/help.proto";  导入了一个外部proto文件中的定义,类似于C++中的 include 。不过好像只能import当前目录及当前目录的子目录中的proto文件,比如import父目录中的文件时编译会报错(Import "../xxxx.proto" was not found or had errors.),使用绝对路径也不行,尚不清楚原因,官方文档说使用 -I=PATH 或者 --proto_path=PATH

 

message


required string name = 1;


protobuf一共有三个字段修饰符:  

       - required:该值是必须要设置的;  

        - optional :该字段可以有0个或1个值(不超过1个);  

       - repeated:该字段可以重复任意多次(包括0次),类似于C++中的list; 


使用建议:除非确定某个字段一定会被设值,否则使用optional代替required。  


string


name标识号的范围在:1 ~ 229 - 1,其中[19000-19999]为Protobuf预留,不能使用。


Person 内部声明了一个enum和一个message,这类似于C++中的类内声明,Person外部的结构可以用 Person.PhoneType 的方式来使用PhoneType。当使用外部package中的结构时,要使用 pkgName.msgName.typeName



optional PhoneType type = 2 [default


另外,一个proto文件中可以声明多个message,在编译的时候他们会被编译成为不同的类。


3. 生成C++文件 


  protoc是proto文件的编译器,目前可以将proto文件编译成C++、Java、Python三种代码文件,编译格式如下: 

protoc -I=$SRC_DIR --cpp_out=$DST_DIR /path/to/file.proto


        上面的命令会生成xxx.pb.h 和 xxx.pb.cc两个C++文件。 



4. C++文件使用 

  现在编写一个main.cc文件:

#include <iostream>
#include "addressbook.pb.h"

int main(int argc, const char* argv[])
{
    addressbook::AddressBook person;
    addressbook::Person* pi = person.add_person_info();

    pi->set_name("aut");
    pi->set_id(1219);
    std::cout << "before clear(), id = " << pi->id() << std::endl;
    pi->clear_id();
    std::cout << "after  clear(), id = " << pi->id() << std::endl;
    pi->set_id(1087);
    if (!pi->has_email())
        pi->set_email("autyinjing@126.com");

    addressbook::Person::PhoneNumber* pn = pi->add_phone();
    pn->set_number("021-8888-8888");
    pn = pi->add_phone();
    pn->set_number("138-8888-8888");
    pn->set_type(addressbook::Person::MOBILE);

    uint32_t size = person.ByteSize();
    unsigned char byteArray[size];
    person.SerializeToArray(byteArray, size);

    addressbook::AddressBook help_person;
    help_person.ParseFromArray(byteArray, size);
    addressbook::Person help_pi = help_person.person_info(0);

    std::cout << "*****************************" << std::endl;
    std::cout << "id:    " << help_pi.id() << std::endl;
    std::cout << "name:  " << help_pi.name() << std::endl;
    std::cout << "email: " << help_pi.email() << std::endl;

    for (int i = 0; i < help_pi.phone_size(); ++i)
    {
        auto help_pn = help_pi.mutable_phone(i);
        std::cout << "phone_type: " << help_pn->type() << std::endl;
        std::cout << "phone_number: " << help_pn->number() << std::endl;
    }
    std::cout << "*****************************" << std::endl;

    return 0;
}