赞
踩
proto文件名称为addressbook.proto。
syntax = "proto3"; import "google/protobuf/any.proto"; // package类似于namespace,可以避免命名冲突 package AddressBookInfo; // message类似于class message Person { string name = 1; int32 id = 2; string email = 3; // 枚举类型 enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; // proto3中enum没有default选项,把第一个值作为default PhoneType type = 2; } // repeated表示message或者filed可以重复多次 repeated PhoneNumber phones = 4; } message Address { string address = 1; } message AddressBook { string owner = 1; repeated Person person_infos = 2; /* ** oneof类似于union类型,某一个时刻只能设置一个field,所有的field共享同一段内存。 ** 设置oneof字段将自动清除oneof的所有其他字段,即只能同时设置(set_)一个,不然就会core dump。 ** 可以在oneof内部添加和删除field,但是删除和添加oneof要小心。 ** oneof中数据成员的编号建议承上启下,尽量不要随意编号。 */ oneof PayType { string type_ali = 3; string type_wx = 4; } /* ** map是key-value类型,key可以是int或者string,value可以是自定义message。 ** Any用来实现泛型,可以表示任意类型。 */ map<string, google.protobuf.Any> owner_address = 5; }
使用protoc编译器对proto文件进行编译,生成addressbook.pb.h
和addressbook.pb.cc
。
打开addressbook.pb.h
和addressbook.pb.cc
,可以看到自动生成了很多的API,后续可以使用这些API读写数据。
addressbook.cpp
文件中阐述了如何使用protobuf自动生成的API读写数据,基本上覆盖了常用的消息类型。
具体的API及使用方式可以参考:https://developers.google.cn/protocol-buffers/docs/reference/cpp-generated
#include <iostream> #include <fstream> #include "addressbook.pb.h" #include <google/protobuf/any.h> using namespace std; void SaveInfo() { AddressBookInfo::AddressBook adbook; // 使用set_设置message中filed的值,函数名都是小写(即使在proto中是大写) adbook.set_owner("eric"); // Person1 // 使用add_添加message,返回的是指针类型 AddressBookInfo::Person *person1 = adbook.add_person_infos(); person1->set_name("mark"); person1->set_id(123456); person1->set_email("mark@163.com"); AddressBookInfo::Person::PhoneNumber *person1_phone = person1->add_phones(); person1_phone->set_number("12345678"); // 枚举类型中的元素名称是唯一的,所以可以直接用作用域限定符访问元素,不需要通过枚举名访问 person1_phone->set_type(AddressBookInfo::Person::HOME); person1_phone = person1->add_phones(); person1_phone->set_number("1234"); person1_phone->set_type(AddressBookInfo::Person::WORK); // Person2 AddressBookInfo::Person *person2 = adbook.add_person_infos(); person2->set_name("mike"); person2->set_id(654321); person2->set_email("mike@163.com"); AddressBookInfo::Person::PhoneNumber *person2_phone = person2->add_phones(); person2_phone->set_number("87654321"); person2_phone->set_type(AddressBookInfo::Person::HOME); person2_phone = person2->add_phones(); person2_phone->set_number("5678"); person2_phone->set_type(AddressBookInfo::Person::WORK); // map和any类型的初始化,mutable返回的是非const指针类型 google::protobuf::Map<string, google::protobuf::Any> *owner_address = adbook.mutable_owner_address(); // 定义一个Any类型,用于接收message google::protobuf::Any *any = new google::protobuf::Any; AddressBookInfo::Address adbook_address; adbook_address.set_address("HB"); // 使用PackFrom将message类型存储为Any类型 any->PackFrom(adbook_address); // map类型的初始化方式和STL中的map类似 (*owner_address)[adbook.owner()] = *any; // 设置oneof中某一个成员的值,之后如果再使用set_则会core dump adbook.set_type_ali("AliPay"); fstream output("address_book_file", ios::out | ios::trunc | ios::binary); // 使用SerializeToOstream将序列化后的数据写入文件中 if (!adbook.SerializeToOstream(&output)) { cerr << "Failed to write address book." << endl; } } void ShowMsg(const AddressBookInfo::AddressBook &adbook) { cout << adbook.owner() << endl; // 对于重复message,_size表示有多少个重复message,使用索引取出每一个message for (int i = 0; i < adbook.person_infos_size(); ++i) { const AddressBookInfo::Person &person = adbook.person_infos(i); // 取出各个字段的值 cout << person.name() << endl; cout << person.id() << endl; cout << person.email() << endl; for (int j = 0; j < person.phones_size(); ++j) { // 使用索引获取枚举类型的所有值 const AddressBookInfo::Person::PhoneNumber &person_phone = person.phones(j); switch(person_phone.type()) { case AddressBookInfo::Person::MOBILE : cout << "MOBILE phone #: "; break; case AddressBookInfo::Person::HOME : cout << "HOME phone #: "; break; case AddressBookInfo::Person::WORK : cout << "WORK phone #: "; break; } cout << person_phone.number() << endl; } } cout << adbook.type_ali() << endl; } void ShowMapMsg(const AddressBookInfo::AddressBook &adbook) { // 取出map类型的字段成员 const google::protobuf::Map<string, google::protobuf::Any> &adbook_map = adbook.owner_address(); // 使用迭代器访问map(和STL中的map类似) auto iter = adbook_map.begin(); cout << iter->first << endl; google::protobuf::Any any = iter->second; AddressBookInfo::Address adbook_address; // 使用UnpackTo从Any类型解析出message,注意参数中有一个& if (any.UnpackTo(&adbook_address)) { cout << adbook_address.address() << endl; } else { cout << "UnpackTo data error" << endl; } } void LoadInfo() { AddressBookInfo::AddressBook adbook; fstream input("address_book_file", ios::in | ios::binary); // 使用ParseFromIstream从文件中反序列化 if (!input) { cout << ": File not found. Creating a new file." << endl; } else if (!adbook.ParseFromIstream(&input)) { cout << "Failed to parse address book." << endl; } ShowMsg(adbook); cout << endl; ShowMapMsg(adbook); } int main(int argc, char const *argv[]) { SaveInfo(); LoadInfo(); // 删除所有已分配的内存(注意any是堆上的内存,清除内存的操作要小心) google::protobuf::ShutdownProtobufLibrary(); return 0; }
使用g++编译所有的CPP文件,注意编译参数需要加上:-std=c++11、-lprotobuf、-lpthread
。
(1)如果没有添加-std=c++11选项则会出现以下问题:
(2)针对oneof类型,如果同时设置了多个字段或者字段编号混乱,则会出现以下问题:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。