struct_pb Quick Start
Note: if you are not familiar with the official c++ implement, Protocol Buffer Basics: C++ is a good material to learn.
This tutorial provides a basic C++ programmer's introduction to work with struct_pb, which is the protobuf compatible solution of the yalantinglibs.
By walking through creating a simple example application, it shows you how to
- Define message formats in a
.proto
file. (same as official protobuf document) - Use the protocol buffer compiler (
protoc
) withstruct_pb
plugin. - Use the
struct_pb
low-level API to write and read messages.
Defining Your Protocol Format
we use proto3 here.
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
Compiling Your Protocol Buffers
Now that you have a .proto
, the next thing you need to do is generate the struct you'll need to read and write AddressBook
(and hence Person
and PhoneNumber
) messages. To do this, you need to run the protocol buffer compiler protoc
with struct_pb
plugin protoc-gen-struct_pb
on your .proto
.
protoc -I=$SRC_DIR --plugin=$PATH_TO_protoc-gen-struct_pb --struct_pb_out=$DST_DIR $SRC_DIR/addressbook.proto
see Generating source code for details.
This generates the following files in your specified destination directory:
addressbook.struct_pb.h
, the header which declares your generated struct.addressbook.struct_pb.cc
, which contains thestruct_pb
low-level API implementation of your struct.
The struct_pb low-level API
Let's look at some generated code and see what struct and functions the compiler has created for you. If you look in addressbook.struct_pb.h
, you can see that you have a struct for each message you specified in addressbook.proto
.
namespace tutorial {
struct Person;
struct AddressBook;
struct Person {
enum class PhoneType: int {
MOBILE = 0,
HOME = 1,
WORK = 2,
};
struct PhoneNumber {
std::string number; // string, field number = 1
::tutorial::Person::PhoneType type; // enum, field number = 2
};
std::string name; // string, field number = 1
int32_t id; // int32, field number = 2
std::string email; // string, field number = 3
std::vector<::tutorial::Person::PhoneNumber> phones; // message, field number = 4
};
struct AddressBook {
std::vector<::tutorial::Person> people; // message, field number = 1
};
} // namespace tutorial
namespace struct_pb {
namespace internal {
// ::tutorial::Person::PhoneNumber
template<>
std::size_t get_needed_size<::tutorial::Person::PhoneNumber>(const ::tutorial::Person::PhoneNumber&, const ::struct_pb::UnknownFields& unknown_fields);
template<>
void serialize_to<::tutorial::Person::PhoneNumber>(char*, std::size_t, const ::tutorial::Person::PhoneNumber&, const ::struct_pb::UnknownFields& unknown_fields);
template<>
bool deserialize_to<::tutorial::Person::PhoneNumber>(::tutorial::Person::PhoneNumber&, const char*, std::size_t, ::struct_pb::UnknownFields& unknown_fields);
template<>
bool deserialize_to<::tutorial::Person::PhoneNumber>(::tutorial::Person::PhoneNumber&, const char*, std::size_t);
// ::tutorial::Person
template<>
std::size_t get_needed_size<::tutorial::Person>(const ::tutorial::Person&, const ::struct_pb::UnknownFields& unknown_fields);
template<>
void serialize_to<::tutorial::Person>(char*, std::size_t, const ::tutorial::Person&, const ::struct_pb::UnknownFields& unknown_fields);
template<>
bool deserialize_to<::tutorial::Person>(::tutorial::Person&, const char*, std::size_t, ::struct_pb::UnknownFields& unknown_fields);
template<>
bool deserialize_to<::tutorial::Person>(::tutorial::Person&, const char*, std::size_t);
// ::tutorial::AddressBook
template<>
std::size_t get_needed_size<::tutorial::AddressBook>(const ::tutorial::AddressBook&, const ::struct_pb::UnknownFields& unknown_fields);
template<>
void serialize_to<::tutorial::AddressBook>(char*, std::size_t, const ::tutorial::AddressBook&, const ::struct_pb::UnknownFields& unknown_fields);
template<>
bool deserialize_to<::tutorial::AddressBook>(::tutorial::AddressBook&, const char*, std::size_t, ::struct_pb::UnknownFields& unknown_fields);
template<>
bool deserialize_to<::tutorial::AddressBook>(::tutorial::AddressBook&, const char*, std::size_t);
} // internal
} // struct_pb
The struct_pb high-level API
We encapsulate the low-level API to make struct_pb
easier to use.
/*
* High-Level API for struct_pb user
* If you need more fine-grained operations, encapsulate the internal API yourself.
*/
template<typename Buffer = std::vector<char>, typename T>
Buffer serialize(const T& t, const UnknownFields& unknown_fields = {}) {
Buffer buffer;
auto size = struct_pb::internal::get_needed_size(t, unknown_fields);
buffer.resize(size);
struct_pb::internal::serialize_to(buffer.data(), buffer.size(), t, unknown_fields);
return buffer;
}
template<typename T, typename Buffer>
bool deserialize_to(T& t, UnknownFields& unknown_fields, const Buffer& buffer) {
return struct_pb::internal::deserialize_to(t, buffer.data(), buffer.size(), unknown_fields);
}
template<typename T, typename Buffer>
bool deserialize_to(T& t, const Buffer& buffer) {
return struct_pb::internal::deserialize_to(t, buffer.data(), buffer.size());
}
Writing A Message
void prompt_for_address(tutorial::Person& person) {
std::cout << "==================================" << std::endl;
std::cout << " Add People " << std::endl;
std::cout << "==================================" << std::endl;
std::cout << "Enter person ID number: ";
std::cin >> person.id;
std::cin.ignore(256, '\n');
std::cout << "Enter name: ";
std::getline(std::cin, person.name);
std::cout << "Enter email address (blank for none): ";
std::getline(std::cin, person.email);
while (true) {
std::cout << "Enter a phone number (or leave blank to finish): ";
tutorial::Person::PhoneNumber phone_number;
std::getline(std::cin, phone_number.number);
if (phone_number.number.empty()) {
break ;
}
std::cout << "Is this a mobile, home, or work phone? ";
std::string type;
std::getline(std::cin, type);
if (type == "mobile") {
phone_number.type = tutorial::Person::PhoneType::MOBILE;
}
else if (type == "home") {
phone_number.type = tutorial::Person::PhoneType::HOME;
}
else if (type == "work") {
phone_number.type = tutorial::Person::PhoneType::WORK;
}
else {
std::cout << "Unknown phone type: Using default." << std::endl;
}
person.phones.push_back(phone_number);
}
}
Reading A Message
void list_people(const tutorial::AddressBook& address_book) {
std::cout << "==================================" << std::endl;
std::cout << " List People " << std::endl;
std::cout << "==================================" << std::endl;
for(const auto& person: address_book.people) {
std::cout << " Person ID: " << person.id << std::endl;
std::cout << " Name: " << person.name << std::endl;
if (!person.email.empty()) {
std::cout << "E-mail address: " << person.email << std::endl;
}
for(const auto& phone: person.phones) {
switch (phone.type) {
case tutorial::Person::PhoneType::MOBILE:
std::cout << "Mobile phone #: ";
break;
case tutorial::Person::PhoneType::HOME:
std::cout << " Home phone #: ";
break;
case tutorial::Person::PhoneType::WORK:
std::cout << " Work phone #: ";
break;
}
std::cout << phone.number << std::endl;
}
}
}