Protocol Buffer を使ってみた
まだ全然ドキュメントとか読んでないけど。
マイコミジャーナルで紹介されてたのを読んでいたら、興味がわいてきた。
http://journal.mycom.co.jp/articles/2008/07/18/protocolbuffer/menu.html
まずはインストールから。
なんと、既に MacPorts でインストールできるようになっていたので、それでインストール。
$ sudo port install protobuf-cpp $ rehash $ protoc --version libprotoc 2.0.0
パッケージ名からわかるように、C++ でしか使えるようになりません。
Java とかでも使えるようにする場合は、http://code.google.com/p/protobuf/ から手動でダウンロード&インストールすればいいのかな。
まぁ今回は C++ だけでいいや。
んで、とりあえずチュートリアル的なものを書いてみた。
まずはデータ構造を示す proto ファイルを書いた。
基本的な書き方は上記記事を参考。
foo.proto
package foo; //C++ では、namespace にあたる message Bar { //C++ では、foo::Bar というクラスになる required string str = 1; //Bar は str がもっていて、必ず設定する必要がある optional int32 number = 2; //Bar は number をもっている。デフォルトは 0 repeated Baz baz = 3; //Bar は Baz をゼロ個以上もっている message Baz { //C++ では、foo::Bar_Baz というクラスになる optional Type type = 1 [default = FUGA]; //Baz は type をもっている。デフォルトは FUGA(=1) enum Type { HOGE = 0; FUGA = 1; PIYO = 2; } } }
続いて、このデータ構造を扱う C++ コードを生成する。
$ protoc foo.proto --cpp_out=.
これで、カレントディレクトリに
- foo.pb.cc
- foo.pb.h
というファイルができる。pb ってのは Protocol Buffer の略かな?
何故か実行属性がついてるけど、気にしないでおこう。
次に、新しくデータを生成するコードを書いた。
上記記事にはあまり詳しく書いてなかった、repeated なデータを扱うようなコードを書いた。
write.cpp
#include <iostream> #include <fstream> #include <string> #include "foo.pb.h" using namespace std; int main(int argc, char *argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; //Protocol Buffer を扱う場合、最初にこれをやっておく foo::Bar bar; char fname[] = "foo.data"; bar.set_str("なんとかかんとか"); bar.set_number(55); foo::Bar_Baz *new_baz = bar.add_baz(); //新しく Bar_Baz を Bar の中で生成し、そのポインタを返す new_baz->set_type(foo::Bar_Baz::HOGE); new_baz = bar.add_baz(); //何も設定しないと、デフォルトの foo::Bar_Baz::FUGA(=1) になる new_baz = bar.add_baz(); new_baz->set_type(foo::Bar_Baz::PIYO); { ofstream fout(fname, ios::binary); if (!bar.SerializeToOstream(&fout)) { cerr << "Failed to serialize" << endl; return -1; } } return 0; }
これをコンパイル&実行すれば、foo.data ができる。
最後に、foo.data を読むコードを書いた。
read.cpp
#include <iostream> #include <fstream> #include "foo.pb.h" using namespace std; int main(int argc, char *argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; foo::Bar bar; char fname[] = "foo.data"; { ifstream fin(fname, ios::binary); if (!fin) { cout << fname << " does not exist" << endl; return -1; } else if (!bar.ParseFromIstream(&fin)) { cerr << "Failed to parse " << fname << endl; return -1; } } cout << "bar.str: " << bar.str() << endl; cout << "bar.number: " << bar.number() << endl; int size = bar.baz_size(); //ちょっとした疑問。なんで unsigned int じゃないんだろ? for (int i = 0; i < size; i++) { cout << "bar.baz[" << i << "].type: " << bar.baz(i).type() << endl; } //他にも、STL-like なイテレータを使って同じことができるみたい return 0; }
これをコンパイル&実行。
$ ./read bar.baz: なんとかかんとか bar.number: 55 bar.baz[0].type: 0 bar.baz[1].type: 1 bar.baz[2].type: 2
ちゃんと読み書きできました。
最低限の機能はこんなカンジ。
XML とかにはない機能もあるみたいだから、まだまだ興味は尽きない。
まずは C++ から使う上でのドキュメントを読まねば。ちょうど明後日まで休みだしね。