Skip to content
On this page

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) with struct_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.

shell
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 the struct_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.

cpp
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

cpp
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

cpp
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;
    }
  }
}

the demo code

This website is released under the MIT License.