Google Protocol Buffersって結局なんなのよ

Google Protocol Bufferは構造化したデータを定義する方式です、と言ってもピンとこない人もいるだろうから、実例を。

前に作ったキャラクターのBMI判定のデータをこの方式で定義する場合、以下のようなprotoファイルを作成します。

package girldata;
message personal_data
{
required int32 id = 1;
required string name = 2;
required string title = 3;
optional int32 height = 4;
optional int32 weight = 5;
optional int32 b = 6;
optional int32 w = 7;
optional int32 h = 8;
}
message girldata_db
{
repeated personal_data pdata = 1;
}

personal_dataにデータが込められていて、girldata_dbは上記のpersonal_dataが繰り返されている、ということを示します。(messageというのが構造データの一単位になるわけです。

これを、protocコマンドにかけると、二つのファイル(C++の場合)生成され、それらはgirldata.pb.ccとgirldata.pb.hとなります。

これらと、protocのコードを組み合わせることによってデータを作る場合以下のように行うことができます。

girldata::personal_data *pd = girl->add_pdata();
pd->set_id(1)
pd->set_name("Konomi Yuzuhara");
pd->set_title("ToHeart2");
pd->set_height(150);
pd->set_b(74);
pd->set_w(55);
pd->set_h(77);

読み出すときには以下のようなコードで。

for (int i = 0; i < girl->pdata_size(); i++)
{
const girldata::personal_data &pd = girl->pdata(i);
if(pd.IsInitialized())
{
// ちゃんと情報がprotoに従って入っている場合はここは埋まっているはずだから
// ここではチェックしない
// 埋まってなかったら上のpd.IsInitialized()でこける
cout << "ID: " << pd.id () << endl;
cout << "Name: " << pd.name() << endl;
cout << "Title: " << pb.title() << endl;
// Optional項目は存在する場合のみ表示
if(pd.has_height())
cout << "Height: " << pd.height() << endl;
if(pd.has_weight())
cout << "Weight: " << pd.weight() << endl;
if(pd.has_b())
cout << "B: " << pd.b() << endl;
if(pd.has_w())
cout << "W: " << pd.w() << endl;
if(pd.has_h())
cout << "H: " << pd.h() << endl;
}
}

データのファイルの読み書きも簡単。

bool saveData(string filename, girldata::girldata_db *girl)
{
fstream output(filename,ios::binary | ios::trunc | ios::out);
if(!girl->SerializeToOstream(&output))
{
cout << "書けません" << endl;
return false;
}
output.close();
return true;
}
bool loadData(string filename girldata::girldata_db *girl)
{
fstream input(filename,ios::in | ios::binary);
if(!girl->ParseFromIstream(&input))
{
cout << "読めません" << endl;
return false;
}
cout << "Loaded " << girl->pdata_size() << " record(s) (" << girl->ByteSize() << " byte(s)) of data" << endl;
input.close();
return true;
}

書き出されるファイルはバイナリフォーマットになりますから、高速に処理することができます。何より、protoファイルさえあれば、そのファイルの互換性を持たせたソフトを作ることは簡単ですから、その利点もあります。

.NETでXMLファイルの使用が非常に簡単ですが、これを使うと同じようにC++で構造化されたバイナリフォーマットの扱いが楽になるので、書き捨てするようなプログラムから気合いの入ったプログラムまで、幅広い使い方ができると思います。

Visual C++ Express Editionで遊んでみたい方はライブラリファイルをどうぞ。

Debug構成の場合はDebugでコンパイルしたライブラリを使わないとファイルI/Oでこけるので要注意。

また、上記のprotoファイルに基づいて作成したデータ(girldata.dat)もアップロードしておきましたので、お暇な人は互換ソフトを作って読み出してみて下さい。