第一个 Protocol Buffers 小程序:电话本
Jul 08
C/C++, 程序设计 cpp, Google, Protocol Buffers 10 Comments
晚上刚写了一篇《Protocol Buffers 简介》,然后就开始动手实战试用一下 Protocol Buffers 这个 Google 出品的东东。
首先从 Protocol Buffers 的主页上把源码下回来,注意是源码,那个 Win32 包不必下,它里面只有一个编译好的 protoc.exe,没有开发所必须的库,光有那个 Protocol Buffers 编译器是没用。
将源码解压出来以后,直接到 vsprojects 目录下用 Visual Studio 2005(其它版本我没有试)打开解决方案文件 protobuf.sln 开始编译。编译成功结束后会在解决方案目录下生成一个 google 目录,里面是单元测试的程序;另外就是 Debug 目录和 Release 目录了,生成的对应版本的 .lib 和 .dll 还有 protoc.exe 编译器全在里面。
那么,头文件呢?在解决方案目录下有一个 extract_includes.bat 文件看到了吗,执行后会生成一个 include 目录,里面就是使用 Protocol Buffers 开发必须的所有头文件了。
HiaHia~ 有了这几样东西,Protocol Buffers 这个强大武器才算是真正到手了。在动手写我们自己的程序之前,为了方便,你也可以把库文件和头文件单独存放在别处,然后在 VC 里设置 VC++ 目录就行了,还有别忘了把 .dll 所在的目录加到系统的 Path 环境变量中。好了,现在我们可以开始动手写自己的程序啦!其实这个程序就是 Protocol Buffers 文档中的电话本例子,只不过我自己试了一遍而已。
第一步,写一个 .proto 文件描述数据结构:
//
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2; // Unique ID number for this person.
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;
}
// Our address book file is just one of these.
message AddressBook {
repeated Person person = 1;
}
第二步,使用命令 protoc.exe -I=. --cpp_out=. addressbook.proto 编译 addressbook.proto,或者方便起见,在 VC 里给 addressbook.proto 设置自定义生成步骤,命令还是这一行,输出文件设置为 addressbook.pb.h;addressbook.pb.cc。
第三步,新建 testProtocolBuffers.cpp,写入下面的代码:
//
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
#pragma comment(lib, "libprotobuf.lib")
void PromptForAddress(tutorial::Person* person);
void ListPeople(const tutorial::AddressBook& addressbook);
int AddPerson();
int ViewPerson();
int _tmain(int argc, _TCHAR* argv[])
{
AddPerson();
ViewPerson();
return 0;
}
void PromptForAddress(tutorial::Person* person)
{
std::cout <<"Enter person ID number: ";
int id;
std::cin>> id;
person->set_id(id);
std::cin.ignore(256, '\n');
std::cout <<"Enter name: ";
std::getline(std::cin, *person->mutable_name());
std::cout <<"Enter email address (blank for none): ";
std::string email;
std::getline(std::cin, email);
if (!email.empty())
person->set_email(email);
while (true)
{
std::cout <<"Enter a phone number (or leave blank to finish): ";
std::string number;
std::getline(std::cin, number);
if (number.empty())
break;
tutorial::Person::PhoneNumber* phonenum = person->add_phone();
phonenum->set_number(number);
std::cout <<"Is it a mobile, home or work phone? ";
std::string type;
std::getline(std::cin, type);
if ("mobile" == type)
phonenum->set_type(tutorial::Person::MOBILE);
else if ("home" == type)
phonenum->set_type(tutorial::Person::HOME);
else if ("work" == type)
phonenum->set_type(tutorial::Person::WORK);
else
std::cout <<"Unknown phone type. Using default." <<std::endl;
}
}
void ListPeople(const tutorial::AddressBook& addressbook)
{
for (int i = 0; i <addressbook.person_size(); i++)
{
const tutorial::Person& person = addressbook.person(i);
std::cout <<"Person ID: " <<person.id() <<std::endl;
std::cout <<"Name: " <<person.name() <<std::endl;
if (person.has_email())
std::cout <<"Email: " <<person.email() <<std::endl;
for (int j = 0; j <person.phone_size(); j++)
{
const tutorial::Person::PhoneNumber& phonenum = person.phone(j);
switch (phonenum.type())
{
case tutorial::Person::MOBILE:
std::cout <<"Mobile phone #: ";
break;
case tutorial::Person::HOME:
std::cout <<"Home phone #: ";
break;
case tutorial::Person::WORK:
std::cout <<"Work phone #: ";
break;
}
std::cout <<phonenum.number() <<std::endl;
}
}
}
int AddPerson()
{
tutorial::AddressBook addressbook;
const std::string& filename = "addressbook";
std::fstream input(filename.c_str(), ios::in | ios::binary);
if (!input)
std::cout <<filename <<": File not found. Creating a new file." <<std::endl;
else if (!addressbook.ParseFromIstream(&input))
{
std::cerr <<"Failed to parse address book." <<std::endl;
return -1;
}
PromptForAddress(addressbook.add_person());
std::fstream output(filename.c_str(), ios::out | ios::trunc | ios::binary);
if (!addressbook.SerializeToOstream(&output))
{
std::cerr <<"Failed to write address book." <<std::endl;
return -1;
}
return 0;
}
int ViewPerson()
{
tutorial::AddressBook addressbook;
const std::string& filename = "addressbook";
std::fstream input(filename.c_str(), ios::in | ios::binary);
if (!addressbook.ParseFromIstream(&input))
{
std::cerr <<"Failed to parse address book." <<std::endl;
return -1;
}
ListPeople(addressbook);
return 0;
}
编译——链接,这样我们的第一个程序,基于 Protocol Buffers 的电话本就写好了。时间关系,这篇日志写得很匆忙。程序在 VC2005 下测试通过,但这只是一个小例子,至于 Protocol Buffers 的性能和效率还没有来得及进一步测试。^_^




Jul 09, 2008 @ 07:44:12
In my opinion, Protocol Buffers borrow the idea of JSON. Eliminate the different of data and application.
Jul 09, 2008 @ 08:25:06
@Kelvin Protocol Buffers 与 JSON 也有不同之处,其一:Protocol Buffers 反序列化速度快于 JSON 使用 exec() 的解析速度。其二:Protocol Buffers 为数据产生的是型别安全的类,而不仅仅是 JSON 的字符串键值对。^_^
Jul 11, 2008 @ 15:50:51
能否提供JAVA的项目。呵呵 我是搞JAVA的对你的这个项目很感兴趣,希望大家能提供JAVA的项目,如果提供不了能否提供你的思路。加我QQ 303216735
Jul 11, 2008 @ 16:12:59
思路很简单,为数据结构编写proto描述,编译为Java的类,然后就可以直接使用了。使用的详细方法请参阅文档。^_^
Aug 20, 2009 @ 15:23:02
我用这个的时候有个问题。。。老解决不了。。。。想请教你
message a中有一个required head myhead的myhead段
head为message head{required uint32 b=1}
用这个函数来初始化head:
int make(a* msg)
{
a::head* c=msg->mutable_head();
c->set_b(2);
return 0;
}
经测试到序列化为string前,用msg.has_head()查是有head存在的
但我最后从这个string解析出来他却报错。。。说missing required field:head。。。。。。。。请问是什么回事。。你知道吗?
Oct 04, 2011 @ 01:06:17
Very nice post. I just stumbled upon your blog and wished to say that I have really enjoyed browsing your blog posts. After all I will be subscribing to your rss feed and I hope you write again very soon!
Oct 07, 2011 @ 08:25:23
I don’t even know how I ended up here, but I thought this post was great. I do not know who you are but definitely you are going to a famous blogger if you are not already
Cheers!
Oct 15, 2011 @ 22:01:32
Very nice post. I just stumbled upon your blog and wanted to say that I have really enjoyed surfing around your blog posts. After all I’ll be subscribing to your feed and I hope you write again very soon!