probuf是goole推出的微型RPC框架,這裏記錄下安裝測試。
參考文章:
Google Protocol Buffers淺析(一)
一、Ubuntu 18.04安裝 C++ Protocol
首先參考官方README文檔進行安裝:
前提
sudo apt-get install autoconf automake libtool curl make g++ unzip
當然可以在如下網站進行源碼下載離線安裝。
https://github.com/protocolbuffers/protobuf/releases/latest
這裏通過git直接在終端進行安裝:
$ git clone https://github.com/protocolbuffers/protobuf.git
$ cd protobuf
$ git submodule update --init --recursive
$ ./autogen.sh
$ ./configure
$ make
$ make check
$ sudo make install
$ sudo ldconfig # refresh shared library cache.
默認安裝在/usr/local下面,可以進行查看庫文件。
pkg-config --cflags protobuf # print compiler flags
pkg-config --libs protobuf # print linker flags
pkg-config --cflags --libs protobuf # print both
查看安裝版本:
protoc --version
二、官方examples代碼編譯測試
通常,編寫一個protocol buffers應用需要經歷如下三步:
1、定義消息格式文件,最好以proto作爲後綴名
2、使用Google提供的protocol buffers編譯器來生成代碼文件,一般爲.h和.cc文件,主要是對消息格式以特定的語言方式描述
3、使用protocol buffers庫提供的API來編寫應用程序
首先編寫addressbook.proto文件。
// See README.txt for information and build instructions.
//
// Note: START and END tags are used in comments to define sections used in
// tutorials. They are not part of the syntax for Protocol Buffers.
//
// To get an in-depth walkthrough of this file and the related examples, see:
// https://developers.google.com/protocol-buffers/docs/tutorials
// [START declaration]
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
// [END declaration]
// [START java_declaration]
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
// [END java_declaration]
// [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
// [END csharp_declaration]
// [START messages]
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
// Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
}
// [END messages]
之後對其進行編譯,產生對應的xxx.pb.cc和xxx.pb.h文件。
protoc -I=./ --cpp_out=./ addressbook.proto
最後,
- 編寫add_person.cc文件,該應用程序實現控制檯輸入person的相關信息並寫入到文本文件:
// See README.txt for information and build instructions.
#include <ctime>
#include <fstream>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
using google::protobuf::util::TimeUtil;
// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::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;
}
tutorial::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(tutorial::Person::MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
*person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL));
}
// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// Add an address.
PromptForAddress(address_book.add_people());
{
// Write the new address book back to disk.
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
- 編寫list_people.cc文件,該應用程序實現將add_person.cc寫入的文本文件進行可視化讀取。
// See README.txt for information and build instructions.
#include <fstream>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
using google::protobuf::util::TimeUtil;
// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::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 tutorial::Person::PhoneNumber& phone_number = person.phones(j);
switch (phone_number.type()) {
case tutorial::Person::MOBILE:
cout << " Mobile phone #: ";
break;
case tutorial::Person::HOME:
cout << " Home phone #: ";
break;
case tutorial::Person::WORK:
cout << " Work phone #: ";
break;
default:
cout << " Unknown phone #: ";
break;
}
cout << phone_number.number() << endl;
}
if (person.has_last_updated()) {
cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << endl;
}
}
}
// Main function: Reads the entire address book from a file and prints all
// the information inside.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
ListPeople(address_book);
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
分別進行編譯,產生對應的可執行文件
c++ add_person.cc addressbook.pb.cc -o add_person_cpp `pkg-config --cflags --libs protobuf`
c++ list_people.cc addressbook.pb.cc -o list_people_cpp `pkg-config --cflags --libs protobuf`
分別運行
./add_person_cpp address_book_file
./list_people_cpp address_book_file
之後對寫入的文件進行讀取
三、protobuf文件生成分析
參考:
一般每一個message對應生成一個類,枚舉型對應在高級語言中仍是一個枚舉型,值從0開始。
對於一個類型爲string的變量,主要產生如下方法:
// string number = 1;
void clear_number();
const std::string& number() const;
void set_number(const std::string& value);
void set_number(std::string&& value);
void set_number(const char* value);
void set_number(const char* value, size_t size);
std::string* mutable_number();
std::string* release_number();
void set_allocated_number(std::string* number);
具體分析參考引文的博主文章:
可以看出,對於每個字段會生成一個has函數(has_number)、clear清除函數(clear_number)、set函數(set_number)、get函數(number和mutable_number)。這兒解釋下get函數中的兩個函數的區別,對於原型爲const std::string &number() const的get函數而言,返回的是常量字段,不能對其值進行修改。但是在有一些情況下,對字段進行修改是必要的,所以提供了一個mutable版的get函數,通過獲取字段變量的指針,從而達到改變其值的目的。
而對於字段修飾符爲repeated的字段生成的函數,則稍微有一些不同,如phone字段,則編譯器會爲其產生如下的代碼:
// repeated .tutorial.Person.PhoneNumber phones = 4;
int phones_size() const;
void clear_phones();
::tutorial::Person_PhoneNumber* mutable_phones(int index);
::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >*
mutable_phones();
const ::tutorial::Person_PhoneNumber& phones(int index) const;
::tutorial::Person_PhoneNumber* add_phones();
const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >&
phones() const;
可以看出,set函數變成了add函數,這個其實很好理解。上面也說過,repeated修飾的字段在高級語言中的實現可能是個數組或動態數組,所以當然通過添加的方式來加入新的字段值。而且get函數也變化很大,這個也不用多說了。
另外,google protobuf提供了常用於調試輸出的函數
virtual void CopyFrom(const Message & from); //Make this message int a copy of the given message.
virtual void MergeFrom(const Message & from); //Merge the fields from the given message into this message.