Windows Protobuf C++ 示例
1. Protobuf Installation
看到CMake越来越流行,我还真是有点沾沾自喜。谁让我那么早就看上它了。现在越来越多的开源库在支持CMake,Protobuf也是其中一员。使用CMake,可以非常轻松地将Protobuf构建起来。具体的构建方式如下:
- 下载Protobuf: 使用git clone将Protobuf下载到自建的某个目录中。
F:\OpenSource\protobuf>git clone https://github.com/google/protobuf.git .
-
CMAKE: 这个是重点。如果还不太熟悉的话,可以查看我之前翻译的CMake的教程。我使用的编译器是VS2015 x64。
其中,-
protobuf_BUILD_SHARED_LIBS
决定了最终构建为静态库还是动态库, -
protobuf_MSVC_STATIC_RUNTIME
决定了使用静态的MSVC运行时库还是使用动态的MSVC运行时库。我暂时使用动态的MSVC运行时库。
-
然后点击Configure
以及Generate
。此时,在F:\OpenSource\protobuf\VC14
中就生成了protobuf.sln。打开它。
-
编译: 运行
ALL_BUILD
,经过了不算长的编译之后,输出面板显示好消息,全部成功。
-
安装:最后是安装环节,把零散的东西放到一起是安装环节的最重要的意义。同样成功。
生成INSTALL
安装目录
2. Building examples
接着,来把例子试一试吧~进入install目录下的examples文件夹。我们看到,有.cc, .go, .py, .java四种后缀。对应着C++, GO, Python, Java四种语言。我们仅来尝试C++的版本。看到文件夹中有CMakeLists.txt。因此,仍然使用CMake。
例子文件夹 CMake(1)此时会报错,因为找不到protobuf_DIR。手动选择即可。
CMake(2)再按
Configure
, Generate
即可。
CMake(3)
打开build\protobuf-examples.sln
。生成ALL_BUILD
。全部成功。
此时,即可调试例子了。先看add_person_cpp
。
在属性的命令参数中添加addressbook。如此,会将结果输出到addressbook中。运行add_person_cpp。
运行add_person_cpp(1) 运行add_person_cpp(2)同样的,在list_people_cpp的属性页中增加命令参数addressbook,来读取addressbook的参数。运行list_people_cpp。
list_people_cpp属性页 list_people_cpp运行结果本着DRY(Don't Repeat yourself)原则,就不进行代码的解读了,若需要对代码详情进一步了解的,请转向官方网站文档
3. Prototxt introduction
想接触ProtoBuf完全是为了Caffe,谁让Caffe用了这玩意儿呢。但Caffe还用到了.prototxt
格式来存储信息,而上文中却还尚未提及。因此,有必要继续尝试。在网上翻查了一些资料后,我写下了如下的例子来把整个文章的内容串起来。
#include <string>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include "addressbook.pb.h"
#include <fstream>
#include <iostream>
#include <io.h>
#include <windows.h>
using namespace std;
#ifdef _MSC_VER
#define open _open
#define close _close
#endif
namespace tutorial{
using google::protobuf::io::FileInputStream;
using google::protobuf::io::FileOutputStream;
using google::protobuf::io::ZeroCopyInputStream;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::ZeroCopyOutputStream;
using google::protobuf::io::CodedOutputStream;
using google::protobuf::Message;
bool ReadProtoFromTextFile(const char* filename, Message* proto){
int fd = open(filename, O_RDONLY);
if (fd == -1)
cerr << "File not found: " << filename;
FileInputStream* input = new FileInputStream(fd);
bool success = google::protobuf::TextFormat::Parse(input, proto);
delete input;
close(fd);
return success;
}
void WriteProtoToTextFile(const Message& proto, const char* filename){
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
FileOutputStream* output = new FileOutputStream(fd);
google::protobuf::TextFormat::Print(proto, output);
delete output;
close(fd);
}
// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const AddressBook& address_book) {
for (int i = 0; i < address_book.people_size(); i++) {
const tutorial::Person& person = address_book.people(i);
cout << "Person ID: " << person.id() << endl;
cout << " Name: " << person.name() << endl;
if (person.email() != "") {
cout << " E-mail address: " << person.email() << endl;
}
for (int j = 0; j < person.phones_size(); j++) {
const Person::PhoneNumber& phone_number = person.phones(j);
switch (phone_number.type()) {
case Person::MOBILE:
cout << " Mobile phone #: ";
break;
case Person::HOME:
cout << " Home phone #: ";
break;
case Person::WORK:
cout << " Work phone #: ";
break;
}
cout << phone_number.number() << endl;
}
}
}
// This function fills in a Person message based on user input.
void PromptForAddress(Person* person) {
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n');
cout << "Enter name: ";
getline(cin, *person->mutable_name());
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}
while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
}
Person::PhoneNumber* phone_number = person->add_phones();
phone_number->set_number(number);
cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(Person::MOBILE);
}
else if (type == "home") {
phone_number->set_type(Person::HOME);
}
else if (type == "work") {
phone_number->set_type(Person::WORK);
}
else {
cout << "Unknown phone type. Using default." << endl;
}
}
}
}
int main(int argc, char* argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
string ans;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
tutorial::ReadProtoFromTextFile(argv[1], &address_book);
tutorial::ListPeople(address_book);
cout << "Want to add new person?" << endl;
cin >> ans;
tutorial::Person person;
if (ans == "yes" || ans == "y")
{
tutorial::PromptForAddress(address_book.add_people());
tutorial::WriteProtoToTextFile(address_book, argv[1]);
}
else if (ans == "no" || ans == "n")
{
return 0;
}
else
{
cout << "Please input yes or no!" << endl;
return 0;
}
return 0;
}
这个程序首先从.prototxt
文件中读取数据,然后列出读取到的内容,询问是否增加人员,最后将结果输出回.prototxt
的文件中。.prototxt
的样子是这样的:
people {
name: "xingzhi"
id: 1
email: "xingzhi@xxx.com"
phones {
number: 1231234123
type: HOME
}
phones {
number: 4564567456
type: WORK
}
}
people {
name: "yi-an"
id: 2
email: "yi-an@xxx.com"
phones {
number:7897890789
type:MOBILE
}
}
后来我增加了一个xyz的联系人,输出之后的结果为:
people {
name: "xingzhi"
id: 1
email: "xingzhi@xxx.com"
phones {
number: "1231234123"
type: HOME
}
phones {
number: "4564567456"
type: WORK
}
}
people {
name: "yi-an"
id: 2
email: "yi-an@xxx.com"
phones {
number: "7897890789"
}
}
people {
name: "xyz"
id: 3
email: "xyz@xxx.com"
phones {
number: "12345678901"
type: HOME
}
}
完美!利用这样的prototxt,非常方便人工修改。