protocol buffer 3 をビルドしてインストール

最近 version 3 が出た protobuf を試しに動かしてみたメモ。

導入手順

Releases から C++アーカイブをダウンロードしてきて展開する。(protobuf-cpp-3.0.0.tar.gz というやつ)

展開後のディレクトリに cd して、以下の手順でビルドする。

./configure
make -j $(nproc)

後は、sudo make install && sudo ldconfig すればインストールできるが、個人的に野良インストールはしたくないので、deb パッケージを作ることにする。別に deb を作りたくない人はこの手順は無視してOK。

# ./deb に make install する
mkdir deb
make install DESTDIR=$(pwd)/deb

mkdir ./deb/DEBIAN

# control ファイルを書く
cat > ./deb/DEBIAN/control <<EOF
Package: nojima-protobuf
Version: 3.0.0-1
Maintainer: Nobody <nobody@example.com>
Architecture: amd64
Description: protocol buffer
EOF

# postinst スクリプトを書く (ldconfig するだけ)
cat > ./deb/DEBIAN/postinst <<EOF
#!/bin/sh -e
ldconfig
EOF
chmod +x ./deb/DEBIAN/postinst

# deb パッケージ化する
fakeroot dpkg-deb --build -Z gzip ./deb ./

# できた deb パッケージをインストールする
sudo dpkg -i ./nojima-protobuf_3.0.0-1_amd64.deb

試しに動かしてみる

以下の内容を person.proto という名前で保存する。

syntax = "proto3";

message Person {
    string name = 1;
    int32 id = 2;
    string email = 3;
}

protocコンパイルして C++ソースコードを生成する。

protoc --cpp_out=./ person.proto

すると person.pb.hperson.pb.cc というファイルができる。

これを使って person を encode するコードを書いてみる。 以下の内容を encode_person.cc という名前で保存する。

#include <cstdlib>
#include <fstream>
#include <iostream>
#include "person.pb.h"

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) {
        std::cerr << "Usage: encode_person FILE\n";
        std::exit(1);
    }

    Person person;
    person.set_name("Yusuke Nojima");
    person.set_id(42);
    person.set_email("nojima@example.com");

    std::fstream output(argv[1], std::ios::out|std::ios::trunc|std::ios::binary);
    if (!output) {
        std::cerr << "ERROR: failed to open file: " << argv[1] << "\n";
        std::exit(1);
    }
    if (!person.SerializeToOstream(&output)) {
        std::cerr << "ERROR: failed to serialize person.\n";
        std::exit(1);
    }

    return 0;
}

そしてコンパイルして実行。

g++ -Wall -o encode_person encode_person.cc person.pb.cc -l protobuf
./encode_person person01

すると person01 というファイルにそれっぽい何かが出力される。

$ xxd person01
00000000: 0a0d 5975 7375 6b65 204e 6f6a 696d 6110  ..Yusuke Nojima.
00000010: 2a1a 126e 6f6a 696d 6140 6578 616d 706c  *..nojima@exampl
00000020: 652e 636f 6d                             e.com

次に、シリアライズされた person を decode するものを書いてみる。 以下の内容を decode_person.cc という名前で保存する。

#include <cstdlib>
#include <fstream>
#include <iostream>
#include "person.pb.h"

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) {
        std::cerr << "Usage: decode_person FILE\n";
        std::exit(1);
    }

    std::fstream input(argv[1], std::ios::in|std::ios::binary);
    if (!input) {
        std::cerr << "ERROR: failed to open file: " << argv[1] << "\n";
        std::exit(1);
    }

    Person person;
    if (!person.ParseFromIstream(&input)) {
        std::cerr << "ERROR: failed to parse person.\n";
        std::exit(1);
    }

    std::cout << "name = " << person.name() << "\n";
    std::cout << "id = " << person.id() << "\n";
    std::cout << "email = " << person.email() << "\n";

    return 0;
}

そしてコンパイルして実行。

$ g++ -Wall -o decode_person decode_person.cc person.pb.cc -l protobuf
$ ./decode_person person01
name = Yusuke Nojima
id = 42
email = nojima@example.com

読めた!!

TODO

ベンチマーク取りたい。

[追記] ベンチマーク取った → Protocol Buffers が本当に遅いのか実際に確かめてみた