1. 4FipS.com
  2. Photos
  3. Videos
  4. Code
  5. Forums
  6. pfQuizzz
  7. About

4FipS.com Forums

Forums dedicated to the projects hosted at 4FipS.com

Skip to content

Google Protocol Buffers in action (C++)

> About coding, hacking, photography, and such...

Google Protocol Buffers in action (C++)

Postby FipS on Sun Sep 16, 2012 1:56 pm

I ran across Google Protocol Buffers quite some time ago in my regular pursuit of having a simple to use and efficient data exchange format that I would widely use in all my applications and tools. PB seemed quite promising in concept at that time, but I was too lazy to actually test it properly as the supplied Visual C++ projects always seemed outdated. However, It all changed quite recently, when I decided to spend some time with the project setup and managed to run a simple test (it was actually easier then expected, surely worth the effort).

First, there's a list of PB key features that I find crucial:

  • Data structures are described using a very simple .proto files (what a relief considering the verbosity of DTD/XSDs).
  • .proto files are then used to generate data access classes (no need for hand-written loaders/savers any more).
  • The produced binary stream is very compact and efficient, and can be easily translated back and forth to a human readable JSON-like textual form, using an offline compiler (this is a killer feature that allows one to edit/hack the binary files in a regular text editor or more importantly use a scripting language to make various data transformations if necessary = BIG WIN!).
  • The data access classes can be generated for both of my favourite languages: C++ (run-time) and Python (tools).
  • PB sources can be directly dropped into and existing project (no need to maintain the countless number of lib variants on all the platforms, I find this very convenient in general as it dramatically reduces the maintenance cost, I'm a big fan of single-source-file amalgamated libs, but that's a completely different story...).

So here's a bunch of steps to follow if you want to get a nice self-contained VC2010 project ready to use (or you can just download the whole package HERE):

  • Create an empty console project.
  • Add '.' to the include directories.
  • From 'protobuf-2.4.1.zip/vsprojects' copy './config.h'.
  • From 'protobuf-2.4.1.zip/src/' copy:
    • './google/protobuf/*.*'
    • './google/protobuf/io/*.*'
    • './google/protobuf/stubs/*.*'
  • Delete:
    • './google/protobuf/test_util.h&cc'
    • './google/protobuf/test_util_lite.h&cc'
  • Delete the unit-test related stuff, everything matching: '*_unittest.*', '*.proto'.
  • Add all the '*.h' and '*.cc' to the project, preserving the directory structure.
  • From 'protoc-2.4.1-win32.zip' also copy './protoc.exe'.

Then, it's time to define our first .proto file, it might look like this (business.proto):
Code: Select all
package business;

message Employee
{
    required string first_name = 1;
    required string last_name = 2;
    required string email = 3;
}

message Company
{
    required string name = 1;
    optional string url = 2;
    repeated Employee employee = 3;
}

We can easily translate it to the C++ data access classes by calling:
Code: Select all
protoc.exe -I=. --cpp_out=. business.proto

Which produces:
Code: Select all
business.pb.h
business.pb.cc
(remember to add these two files to the project as well)

Now, it's time to test our effort. There's nothing easier than filling in a demo company object, saving it to 'company.bin' and loading it back from the binary file, as shows the example below:

VIEW THE CODE BELOW IN FULL-SCREEN (protobuf_sample.cpp)
Code: Select all
/*
(c) 2012 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=807
*/

#include <iostream>
#include <fstream>
#include "business.pb.h"

using namespace std;

/// Saves a demo company object to 'company.bin'.
void save()
{
    business::Company company;
    company.set_name("Example Ltd.");
    company.set_url("http://www.example.com");

    // 1st employee
    {
        business::Employee *employee = company.add_employee();
        employee->set_first_name("John");
        employee->set_last_name("Doe");
        employee->set_email("john.doe@example.com");
    }

    // 2nd employee
    {
        business::Employee *employee = company.add_employee();
        employee->set_first_name("Jane");
        employee->set_last_name("Roe");
        employee->set_email("jane.roe@example.com");
    }

    fstream output("company.bin", ios::out | ios::trunc | ios::binary);
    company.SerializeToOstream(&output);
}

/// Loads a demo company object from 'company.bin' and dumps its data.
void load()
{
    business::Company company;
    fstream input("company.bin", ios::in | ios::binary);

    company.ParseFromIstream(&input);
    cout << "Company: " << company.name() << "\n";
    cout << "URL: " << (company.has_url() ? company.url() : "N/A") << "\n";

    cout << "\nEmployees: \n\n";
    for(int i = 0, n = company.employee_size(); i < n; ++i)
    {
        const business::Employee &employee = company.employee(i);
        cout << "First name: " << employee.first_name() << "\n";
        cout << "Last name: " << employee.last_name() << "\n";
        cout << "Email: " << employee.email() << "\n";
        cout << "\n";
    }
}

int main()
{
    save();
    load();
    return 0;
}

// output:
// Company: Example Ltd.
// URL: http://www.example.com
//
// Employees:
//
// First name: John
// Last name: Doe
// Email: john.doe@example.com

// First name: Jane
// Last name: Roe
// Email: jane.roe@example.com

And that's not all! By calling:
Code: Select all
protoc.exe business.proto --decode=business.Company < company.bin > company.txt

We can dump 'binary.bin' into a human readable 'company.txt':
Code: Select all
name: "Example Ltd."
url: "http://www.example.com"
employee {
  first_name: "John"
  last_name: "Doe"
  email: "john.doe@example.com"
}
employee {
  first_name: "Jane"
  last_name: "Roe"
  email: "jane.roe@example.com"
}

Modify it, and then save it back to 'company.bin':
Code: Select all
protoc.exe business.proto --encode=business.Company < company.txt > company.bin
Isn't that sweet!
User avatar
FipS
Site Admin
 
Posts: 144
Joined: Wed Nov 12, 2008 9:49 pm
Location: Prague

Google Protocol Buffers in action (C++)

Sponsor

Sponsor
 

Return to 4FipS.com General Discussion & Blog

Who is online

Users browsing this forum: Baidu [Spider] and 2 guests