Protocol Buffers C++ 入门

译自 https://developers.google.com/protocol-buffers/docs/cpptutorial    

本文为C++程序员介绍如何使用protocol buffers。通过创建一个简单的示例应用,告诉你如何:

· 在.proto文件中定义消息格式

· 使用protocol buffers编译器

· 使用protocol buffers C++ API 写入和读取消息

为什么使用protocol buffers?

   我们要使用的示例是一个简单的“地址簿”应用,它可以从一个文件读取联系人信息,或者把联系人信息写入到文件中。地址簿中每个联系人有名字、ID、Email地址和电话号码信息。

   如何序列化和获取这种结构化数据?常见的方法是:

· 以二进制形式发送/存储原始的内存数据结构。这种方法通常比较脆弱,因为接收/读取代码必须使用完全相同的内存布局、大小端等。而且,随着文件中原始格式数据的累积,以及使用这种格式的软件的分发,格式的扩展是非常困难的。

· 你可以发明一种将数据条目编码成单个字符串的特别方法,比如说把4编码成"12:3:-23:67"。虽然需要编写一些一次性的编码和解析代码,而且解析需要耗费一些运行时间,这种方法还是比较简单和灵活的。这种方法最适合于编码非常简单的数据。

· 将数据序列化成XML。这种方法很有吸引力,因为XML是人类可读的,而且有很多种语言的XML解析库存在。如果想和其他应用/项目共享数据,XML是个很好的选择。然而,XML很占空间,而且编码/解析XML会严重影响应用的性能。此外,在XML DOM树中导航比在类中的简单字段间导航要复杂得多。

    Protocol buffers是这个问题的灵活、高效、自动化的解决方案。使用protocol buffers时,你编写一个.proto文件描述你想存储的数据结构,用protocol buffers编译器创建一个使用高效二进制格式自动编码和解析这种结构的类。生成的类为字段提供getter和setter方法,形成一个protocol buffer,并且会关注将其作为一个单元进行读取和写入的细节。重要的是,protocol buffer格式支持扩展,而且新的代码仍然可以读取以旧的格式编码的数据。

定义协议格式

   Protocol <wbr>Buffers <wbr>C++ <wbr>入门

· 编号1到15所需的编码长度比大于16的编号要少一个字节,所以一般对于常用的字段使用编号1到15;而将16以及更大的编号用于不常用的可选字段。

· 字段修饰符 

required:规定必须提供字段,否则消息被认为是“未初始化”的。调试版运行时未初始化的消息会导致断言失败;发行版会忽略断言,没有断言失败,但是解析函数会返回失败。

optional:可以设置,也可以不设置字段。不设置字段值的时候会使用默认值。可以指定可选字段的默认值。如果没有指定默认值,则对于数值类型,0是默认值;对于字符串,空字符串是默认值;对于bool类型,false是默认值。对于嵌入的消息,默认值是消息的“默认实例”或者“元类型”,其中每个字段都没有设置。对于没有显式设定值的可选(或者必须)字段调用访问器总是返回字段的默认值。

repeated:定义字段可能重复多次(包括零次)。各个重复的值的次序会被保留。可以把重复字段看做是动态数组。

· 仔细处理required字段。如果以后不想写入或者发送required字段,把它改成optional字段可能会导致问题:较老的读取程序会认为没有设置这个字段的消息是不完整的。

编译.proto文件

     Protocol <wbr>Buffers <wbr>C++ <wbr>入门

     编译会生成两个文件:xxx.pb.h和xxx.pb.cc。

Protocol Buffer API

   Protocol <wbr>Buffers <wbr>C++ <wbr>入门

· 访问器方法:getter方法名字与小写的字段名相同;setter方法名字带前缀set_。

· has_方法:判断是否为(必须或者可选)字段设置过值。

· clear_方法:清除字段值,使之成为空状态。

· 字符串:多了mutable_方法,可以取得字段值的直接指针;还多了一个set_方法,使用char指针作为参数。

· 重复字段 

_size方法:判断有多少个元素

没有has_方法

通过索引取得或者更新元素

add_方法:加入新的元素

4.1 枚举和嵌套类型

· 生成的代码含有枚举类型Person::PhoneType,其值可以是Person::MOBILE、Person::HOME、Person::WORK

· 生成的代码含有嵌套类Person::PhoneNumber,但是实际的名称是Person_PhoneNumber,如果需要前向声明,则需要使用名字Person_PhoneNumber。

4.2 标准消息方法

· bool IsInitialized() const:是否所有必需的字段都已经设置

· string DebugString() const:返回表示消息的人类可读的字符串,对于调试特别有用

· void CopyFrom(const Person& from):复制

· void Clear():清除所有字段

4.3 解析和序列化

· bool SerializeToString(string* output) const:序列化消息,存储到string中。注意其中的字节是二进制的,而不是文本。

· bool ParseFromString(const string& data):从字符串中解析出消息

· bool SerializeToOstream(ostream* output) const:将消息写入到C++ ostream

· bool ParseFromIstream(istream* input):从C++ ostream中解析出消息

写入消息

  Protocol <wbr>Buffers <wbr>C++ <wbr>入门

读取消息

Protocol <wbr>Buffers <wbr>C++ <wbr>入门

扩展Protocol Buffer

   应用发布之后,将来很可能需要改进protocol buffer定义。为保证新的格式向后兼容,旧的格式向前兼容,对于新版本的定义:

· 不得修改任何已有字段的序号

· 不得增加或者删除required字段

· 可以删除optional或者repeated字段

· 可以添加新的optional或者repeated字段,但是必须使用新的序号

   只要遵循上述规则,则旧的程序可以很好地处理新的消息(简单地忽略新的字段)。

· 对于旧的程序:被删除的optional字段具有默认值;被删除的repeated字段是空的。新的程序也可以透明地处理旧的消息。

· 但是:新的optional字段不会出现在旧的消息中,所以(新的程序)要么显式地调用has_方法进行检查,要么在.proto文件中字段序号后面用[default=value]定义默认值。

· 而且:新的repeated字段不会出现在旧的消息中,(新的程序)没法判断是(新的程序)把它设置成空了,还是(旧的程序)根本就没有设置这个字段,因为没有has_方法!

关于优化

   C++ Protocol Buffers库已经进行了大量优化。然而,正确合理的使用可以进一步改进性能。

· 可能的时候请重用消息对象。但是,如果消息大小变化很大,则应该调用Message::SpaceUsed方法进行监测,删除太大的消息。

· 系统的存储分配器可能没有为多个线程分配大量小对象而优化,请试试Google的tcmalloc。

高级用法

    Protocol Buffers的能力不仅仅限于简单的访问器和序列化。Protocol消息类的一个主要特征是提供了反射能力。可以遍历消息的所有字段,操作其值,而不需要编写任何特定消息类型相关的代码。反射的一个非常有用的用途是与XML、JSON等相互转化。更高级的用法可能是找出相同类型的两个消息的不同,或者开发一种“protocol消息正则表达式”,让你可以编写匹配特定消息内容的表达式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值