Protobuf使用摘要和demo

来源:互联网 发布:framecheck是什么软件 编辑:程序博客网 时间:2024/05/17 03:07

作为Google出品的开源项目,其性能和效率是非常出众的,只是支持的语言不多,普及率没有thrift、JSon等广,但我相信众人拾柴火焰高,其使用率肯定会越来越高的。使用Protobuf时间不长,仅仅摘记普遍的流程方法,供大家参考,也方便自己温故。


1.protoc协议编写


package com.MyCompany;    //包


message Person
{
    required string name = 1;
    required sint32  age  = 2;
    required string home = 3;
    enum HOBBY
    {
        RUNNING = 0;
        BADMINTION = 1;
        PINGPONG = 3;
    }


    optional HOBBY hobby = 4[default = RUNNING];
}


message Company
{
    repeated Person staff = 1[packed=true];
    required Person boss = 2;
    enum FIELD
    {
        IT = 0;
        MECHINE = 1;
        SECURITY = 2;
    }


    required FIELD field = 3;
    required fixed32 staff_num = 4;
    optional fixed32 rank = 5[default = 1];
    repeated uint32 vecInt = 6;


}

protobuf的一些数据类型就不说了,大家可以参考别的一些资料。 
从上面的例子中,大家可以知道protobuf是支持嵌套类型的,protoc2需要在声明成员前标记修饰符,但在protoc3中除了repeated,optional和required是不需要的,以下仅就protoc2讨论:


required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;


optional:消息格式中该字段可以有0个或1个值(不超过1个)。


repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java和C++中的List。


注意:变量后的赋值 如 required FIELD field = 2;这个不是对成员field 进行赋值,只是作为变量的位置标记,具体的成员赋值是在实例化后进行的,但修饰符为optional时,可以设置默认值,如: 
optional fixed32 rank = 4[default = 1];


分配标签:每个field都是唯一数字的标记,这是用来标记这个field在message二进制格式中的位置的,一旦使用就不能再修改顺序了。 
注:标记从1-15只有一个字节编码,包括自增长属性(更多的见Protocol Buffer Encoding)标记从16-2047占用两个字节。因此尽量频繁使用1-15,记住为未来的扩展留下一些位置。最小的tag你可以定义为1,最大2的29次方-1 :536870922.你同样不能使用19000-19999(这个位置已经被GPB自己实现)。


指定field规则:由于历史原因,repeated字段如果是基本数字类型的话,不能有效地编码。现在代码可以使用特殊选项[packed=true]来得到更有效率的编码。


注: 由于required是永远的,应该非常慎重地给message某个字段设置为required。如果未来你希望停止写入或者输出某个required字段,那就会成为问题;因为旧的reader将以为没有这个字段无法初始化message,会丢掉这部分信息。一些来自google的工程师们指出使用required弊大于利,尽量使用optional和repeated。


2.编译 
写好.proto文件后,需要用protoc命令将其进行


protoc -I=输入目录  - -cpp_out=输出目录   yourprotobuf.proto

如果yourprotobuf.proto在当前目录下,且编译到当前目录,则命令为:


protoc -I=. --cpp_out=. yourprotobuf.proto

生成两个文件yourprotobuf.pb.cc和yourprotobuf.pb.h。 
我么只需#include “yourprotobuf.pb.h”就行。


3.序列化/反序列化 
先附上代码:


#include "yourprotobuf.pb.h"
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/pool/pool.hpp>
using namespace com::MyCompany;
using namespace std;
using namespace boost;


int main(int argc, char *argv[])
{
    pool<> pl(sizeof(char) * 100);


    char *pTo = static_cast<char *>(pl.malloc());
 //   char *pFrom = static_cast<char *>(pl.malloc());


    Company company;
    Person *person = company.add_staff();
    person->set_age(27);
    person->set_name("Kasen");
    person->set_home("NB");
    person->set_hobby(Person_HOBBY_BADMINTION);


    Person *person1 = company.add_staff();
    person1->set_age(27);
    person1->set_name("wangzhijie");
    person1->set_home("shandong");
    person1->set_hobby(Person_HOBBY_BADMINTION);


    Person *boss = new Person();
    boss->set_age(50);
    boss->set_name("i_do_not_know");
    boss->set_home("china");
    boss->set_hobby(Person_HOBBY_BADMINTION);
    company.set_allocated_boss(boss);


    company.set_staff_num(10000);
    company.set_field(Company_FIELD_SECURITY);
    company.set_rank(1);
    for(int i = 0; i < 5; ++i)
        company.add_vecint(5);


    company.set_vecint(4,9);


    int iNeedSize = company.ByteSize();
    company.SerializeToArray(pTo,iNeedSize);


    Company company1;
    company1.ParseFromArray(pTo,iNeedSize);
    cout<<company1.staff(0).name()<<endl
        <<"staff number:"<<company1.staff_size()<<endl;


    return 1;
}

我们需要注意一下几点:


3.1 命令空间 
如果没有


using namespace com::MyCompany;

则对Person和Company的实例化变为:


com::hikvision::Person person
com::hikvision::Company company

3.2 repeated和required修饰符成员赋值 
如果成员是类类型的,如上述代码中,staff和boss都是Person类型的,用repeated和required两种修饰符进行修饰时,赋值方式不一样。 
repeated:


Person *person = company.add_staff();

已经将对象person装入company对象的list< Person >容器中了。 
required:


    Person boss;
    .......
    company.set_allocated_boss(&boss);

生成一个Person类对象后,赋值完后再添加到对象company中. 
####*3.3 类大小* 
对类对象(修饰符为required或者optional)的赋值有两种方法,如在以上代码中,对成员boss的赋值除了以上的方法外,还有一种:


    Person *boss = company.mutable_boss();
    boss->set_age(50);
    boss->set_name("i_do_not_know");
    boss->set_home("china");
    boss->set_hobby(Person_HOBBY_BADMINTION);

切记不可如下:


    Person boss;
    boss.set_age(50);
    boss.set_name("i_do_not_know");
    boss.set_home("china");
    boss.set_hobby(Person_HOBBY_BADMINTION);
    company.set_allocated_boss(&boss);

这样boss为局部变量,函数返回时就释放。


####*3.4 类大小* 
在用接口SerializeToArray()进行序列化时,需要输入参数类大小,从.cc文件里我们可以看到每个类都有接口ByteSize(),可以知道对象company实际大小,而函数sizeof()只能计算类的固定大小,而不会计算对象实际用了多大的内存。

0 0
原创粉丝点击