google protocol buffers介绍(c++)

来源:互联网 发布:问卷调查数据分析报告 编辑:程序博客网 时间:2024/05/21 09:18

ref:http://blog.csdn.net/learnhard/archive/2010/09/04/5863846.aspx

源码:http://code.google.com/p/protobuf/downloads/list

1.什么是googleprotocol buffer

略)

2.proto文件格式介绍:

注释:使用//

package:名字空间

message:类似一个class/struct,格式:message{…;}

字段修饰符required---必需字段,否则对应的message“未初始化”,debug模式下导致断言,release模式下解析失败;optional---可选字段,不指定,使用默认值(数据类型0/string/boolfalse/嵌套message默认构造/枚举为第一个);repeated---重复字段,可看作是动态数组,在编码时,应该使用特殊选项[packed=true]来保证更高效的编码。

支持类型

.proto type

c++

notes

double

double

 

float

float

 

int32

int32

使用可变长编码方式,负数时不够高效,应该使用sint32

int64

int64

同上

uint32

uint32

使用可变长编码方式

uint64

uint64

同上

sint32

int32

使用可变长编码方式,有符号的整型值,编码时比通常的int32高效

sint64

sint64

同上

fixed32

uint32

总是4个字节,如果数值总是比2^28大的话,这个类型会比uint32高效

fixed64

uint64

总是8个字节,如果数值总是比2^56大的话,这个类型会比uint64高效

sfixed32

int32

总是4个字节

sfixed64

int64

总是8个字节

bool

bool

 

string

string

一个字符串必须是utf-8编码或者7-bitascii编码的文本

bytes

string

可能包含任意顺序的字节数据

tag:”=1”/”=2”指出该字段在二进制编码中使用的唯一tagtag1-15编码所需的字节数比更大的标志号使用的字节要少1个。重复字段中,每一项都要求重新编码tag,所以重复字段最好使用1-15tag号。

默认值:对于required/optional字段,可以使用[default=xxx];指定默认值

3.认识源码

下载源码包http://code.google.com/p/protobuf/downloads/list

editors:这里只是编辑时需要的插件,比如在vim中,添加代码高亮显示:~/.vimrc添加soproto.vim

examples:里面有个通讯录的例子

srcprotoc的源码,也就是一个小的语法解析器和代码生成器。主要用途就是从proto文件生成需要的代码。(当然,也可以安装protobuf-compiler包,直接安装protoc

使用过程:编译源码包,在protobuf-2.3.0/src目录下,会生成上面所说的protoc工具。进入examples目录,../src/protoc--cpp_out=./ addressbook.proto,当然,可以先makeinstall安装protoc,而不用使用相对路径。现在,应该有addressbook.pb.h/addressbook.pb.cc生成。编译:g++add_person.cc addressbook.pb.cc -I../src/ -lprotobuf -L../src/.libs/-lpthread-Wl,-rpath=../src/.libs/即可。注意:在使用protoc指定源文件/目标目录时,都需要使用绝对路径。

关于代码生成

required/optional:均有has_xxclear_xxtag号、set_xxxxmutable_xx等方法生成。xx返回引用,mutable_xx返回指针,但对数值类型没有后者,前者也只是返回副本。

repeated:包括xx_sizeclear_xxtag号、xx(i)mutable_xx(i)add_xx(返回指针)、xx()(返回整个)mutable_xx()(返回整个)

所有生成的类,均继承自message(protobuf-2.3.0/src/google/message.h)

IsInitialized()---检查全部的required字段是否都被set了值

DebugString()---调试时,返回一个易读的消息表示形式

CopyFrom(Message&from)---使用外部消息的值,覆盖调用者消息内部的值

Clear()---将所有项复位到空状态

SerializeToString(string*output)---将消息序列化,并存储在指定的string中。注意里面的内容是二进制的,我们只是使用string作为一个很方便的容器。

ParseFromString(conststring& data)---从给定的string解析消息

SerializeToOstream(ostream*out)---将消息写入给定的ostream

ParseFromIstream(istream*input)---从给定的istream中解析消息

更多的api见:http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference/cpp/index.html

4.我们需要做什么

1.搞定proto文件


2.使用protoc工具生成需要的代码


3.使用生成的代码编写程序


编写过程中需要注意:

1.在使用c++protocolbuffer库之前先执行GOOGLE_PROTOBUF_VERIFY_VERSION宏。检查链接的库文件和头文件是否兼容。

2.在程序结尾处调用google::protobuf::ShutdownProtobufLibrary(),删除由protocolbuffer库分配的全局对象。

3.链接库:-lprotobuf

4.枚举常量必须在32位整型值范围内,因为enum值是使用可变编码方式,对负数不够高效,不推荐enum中使用负数。

5.导入定义:import"myproject/other_protos.proto";//-I/-import_path指定,不提供就在调用目录下查找。

6.组的概念已经被弃用了,应使用嵌套消息类型来代替它。

7.关于扩展:1)对于已经存在的任何字段,不可修改tag2)不能添加或删除任何required字段;3)可以添加optional/repeated字段,但是tag必须使用新的;4)required字段可以移除,但tag号仍然保留,不可重用;5)可以添加optional/repeated字段,但是tag必须使用新的

对于老代码,会忽略掉任何新的字段,已经被删除的optional字段将被赋予默认值,已经被删除的repeated字段将为空。新代码也能透明的读取旧的消息。新的optional字段将不会出现在旧的消息中。如果添加了一个新的repeated字段,新的代码将无法告诉你它是否被留空了(被新代码),或者是否从未被set值(被旧代码),因为它没有has_标志;6)一个非required字段可以转换为一个扩展,反之亦然—只要它的类型和tag号保持不变;

8.关于兼容:1)int32/uint32/int64/uint64/bool是全部兼容的,类似强制转换;2)sint32/sint64是互相兼容的,但是它们与其他整数类型不兼容;3)stringbytes兼容—只要bytes是有效的utf-8编码;4)嵌套消息和bytes兼容--只要bytes包含该消息的一个编码过的版本;5)fixed32sfixed32兼容,fixed64sfixed64兼容

5.关于优化

1.尽可能重复利用message对象。但是,当消息大小不一致时,可以通过调用SpaceUsed函数监测消息对象的大小,并在它太大的时候删除它。

2.多线程中分配大量小对象时,可以尝试使用google'stcmalloc

6.关于技巧

将多个消息转化为流:protoc解析器无法自己决定一个消息结束于何处。所以:在写入每一个消息之前,先写入消息的大小。

大数据集:protocolbuffers不是设计来处理大消息的。如果单条消息大于1M,则……

联合类型:处理多种类型的消息

自描述的消息:对于原始消息,没有proto文件的情况下,可以通过使用-descriptor_set_out选项+google/protobuf/descriptor.proto+一个自描述的协议消息(见官方网站+DynamicMessage类来解析该原始消息。

7.关于其他

7.1 extensions

messageFoo{extensions 100 to 199;}//不可更改文件

extend Foo {optional int32 bar =126;}//外部增加bar字段给Foo消息

Foo foo; foo.SetExtension(bar,15);//这样的字段具有特殊的访问函数

类似,Foo类也生成了模板函数Has/Clear/Get/Mutable/AddExtension

 

7.2 NestedExtensions

messageBaz{extend Foo{optional int32 bar = 126;}}

Foo foo;foo.SetExtension(Baz::bar, 15);

在一个消息类型中嵌套声明一个extend块并没有暗示外部类型与扩展类型之间有任何联系。特别地,上面的例子并没有表明BazFoo的任何类型的子类。它所表明的仅仅是:符号bar只是再Baz的内部声明的;它只是一个静态成员罢了。

 

7.3 风格:

消息名/枚举名---每个单词首字母大写,字段名---小写下划线,枚举值---大写下划线

 

原创粉丝点击