2018/03/11

[c/c++]protobuf-c (3)

ようやく、続きをやる気になった。
C++の方のprotobufチュートリアルをやる。


前回、addressbook.protoをこちらからコピー&ペーストして作ったが、オリジナルではproto2と明示されていて、コンパイルしてもワーニングが出なかった。

$ protoc -I=. --cpp_out=. addressbook.proto
$

v3.5.1のexamplesにもaddressbook.protoがあり、こちらはproto3になっている。
proto2版では、PhoneNumberのPhoneTypeがoptionalだったのだが、proto3版にはそれがない。
試しに書き加えてみると、エラーになった。

addressbook.proto:39:14: Explicit 'optional' labels are disallowed in the Proto3 syntax. To define 'optional' fields in Proto3, simply remove the 'optional' label, as fields are 'optional' by default.

optionalを外した。

addressbook.proto: Explicit default values are not allowed in proto3.

proto3ではデフォルトで"optional"だし、デフォルト値を許可していないということか。
"explicit"と書いているから、暗黙のデフォルト値があるのか、デフォルト値を設定するAPIがあるとか、そういうことかもしれん。

まあ、深くは追うまい。


今回は、チュートリアルに従い、proto2にする。

https://developers.google.com/protocol-buffers/docs/cpptutorial


文法が今の時点と変わるかもしれないので、ソースを貼っておこう。

01: syntax = "proto2";
02: 
03: package tutorial;
04: 
05: message Person {
06:   required string name = 1;
07:   required int32 id = 2;
08:   optional string email = 3;
09: 
10:   enum PhoneType {
11:     MOBILE = 0;
12:     HOME = 1;
13:     WORK = 2;
14:   }
15: 
16:   message PhoneNumber {
17:     required string number = 1;
18:     optional PhoneType type = 2 [default = HOME];
19:   }
20: 
21:   repeated PhoneNumber phones = 4;
22: }
23: 
24: message AddressBook {
25:   repeated Person people = 1;
26: }

Javaっぽいというか、C++っぽいというかで、なんとなく読める。
こんな感じか。

  • namespaceは"tutorial"
  • classで"Person"と"AddressBook"が作られるし、"Person"の内側に"PhoneNumber"が作られる
  • enumでPhotoTypeが作られるが、これも"Person"の内側か
  • requiredは必須で、optionalはあってもなくてもよい。optionalはデフォルト値を付けられる。
  • repeatedは...?

生成されたヘッダファイルを見ると、classは6つで、1つのmessageに対して、その名前と、名前に"DefaultTypeInternal"が付加されたclassができている。

PhoneNumberのようにmessage内にあるmessageは、名前で見分けられるようになっている。
ここでは、Person_PhoneNumber、となっていた。
enumも同様で、Person_PhoneType、だ。


そして、それぞれに1だの2だのという数字が付いている。
これは"tag"というもので、1~15は効率がよいので、頻繁に使うものはそういう数字にしておくとよいらしい。
昔、その辺は調べた記憶がある。
4bitまでなら、keyが1byteで収まるかららしい。

requiredは後からoptionalにはできないから注意せよ、となっている。
だから、proto3ではデフォルトがoptionalになったのか。


自動生成されたAPIの説明。
https://developers.google.com/protocol-buffers/docs/cpptutorial#the-protocol-buffer-api

requireとoptionalは"has_"がある。
repeatedは、つまり複数持つことができるというわけか。だから代わりに"_size"がある。

setterは、文字列の場合はconst char*とstd::string&の両方が用意される。
数値は、たぶんそのままだろう。
ここの例ではint32だからint32_tかと思ったが、実際は::google::protobuf::int32になっていた。
https://developers.google.com/protocol-buffers/docs/proto#scalar

src/google/protobuf/stubs/port.hあたりか。



requireとoptionalにはgetter/setter/clearがある。
repeatedのclearはわかるが、getterはphones(index)とRepeatedPtrField<>か?
setterはadd_phones()のような気がするのだが、引数を取らずに戻り値がある。
ああ、説明がちゃんと書かれていた。add_phones()で追加して、戻り値に自分で設定してやるようだ。
indexを指定して取得してupdateできるようなことが書かれているが、constになってるのは何でだろう?


mutable、というAPIもある。
単語としては「変わりやすい」なのだが、C++のmutableとも別なのだろうか?
"direct pointer"と書いてあるから、左辺値にもなることができるのだろう。
repeatedのupdateできるというやつは、このmutable APIを使えばよいのか。


あとは、message全体向けのAPIや、シリアライズのAPIが説明されているが、まあ、あとでいいや。


まずは、動かそう。

https://developers.google.com/protocol-buffers/docs/cpptutorial#writing-a-message

main()があるので、コンパイルできれば動きそうだが、その前にC++のprotobufライブラリを用意せねばなるまい。

$ cd protobuf-3.5.1
$ mkdir install
$ ./configure --prefix=`pwd`/install
$ make

怒られた。

protobuf-3.5.1/missing: line 81: aclocal-1.14: command not found
WARNING: 'aclocal-1.14' is missing on your system.
          You should only need it if you modified 'acinclude.m4' or
          'configure.ac' or m4 files included by 'configure.ac'.
          The 'aclocal' program is part of the GNU Automake package:
          <http://www.gnu.org/software/automake>
          It also requires GNU Autoconf, GNU m4 and Perl in order to run:
          <http://www.gnu.org/software/autoconf>
          <http://www.gnu.org/software/m4/>
          <http://www.perl.org/>
Makefile:1488: recipe for target 'aclocal.m4' failed
make: *** [aclocal.m4] Error 127

aclocal --versionすると、1.15だということがわかった。
検索すると、バージョンは違うもののautoreconf -f -iすればなんとかなりそうな雰囲気だ。
configure前と書いているが、気にせずにやってmakeすると、進んだ。
WSLのUbuntuだからか、ビルドにかなり時間がかかった。
Releaseのlinux.zipにライブラリも入っていればよいのだが、そうはなってないのだ。


ライブラリは、-liteが付いたものと、そうでないものがある。
.aファイルだと、8.2MBと100MBというかなりの差だ。

$ g++ -o tst_w write.cc addressbook.pb.cc -I. -Iinstall/include -Linstall/lib -lprotobuf -pthread -static
$ ./tst_w abc.book

これでビルドできた。
installディレクトリは、make後にmake installしてインストールしたディレクトリだ。
addressbookたちはカレントディレクトリにある前提である。
protobuf-liteだとリンクでエラーになったので、限定的なのだろう。

-staticにしたのは、共有ライブラリにパスを通すのが面倒だったからだ。
これでもよい。

$ g++ -o tst_w write.cc addressbook.pb.cc -I. -Iinstall/include -Linstall/lib -lprotobuf
$ LD_LIBRARY_PATH=./install/lib ./tst_w abc.book

実行すると、新規ファイルを作り、パラメータの入力を求められた。

abc.book: File not found.  Creating a new file.
Enter person ID number: 1
Enter name: yoshio
Enter email address (blank for none): yoshio@mail.com
Enter a phone number (or leave blank to finish):

これで、29byteのファイルが生成された。
もう1回、同じことをやる。

Enter person ID number: 1
Enter name: yoshida
Enter email address (blank for none): yoshida@mail.com
Enter a phone number (or leave blank to finish):

IDは同じにしたのだが、追加になって、ファイルサイズは60byteになった。

同じ要領で、read側もソースファイルを持ってきて、ビルドする。

$ g++ -o tst_r read.cc addressbook.pb.cc -I. -Iinstall/include -Linstall/lib -lprotobuf
$ LD_LIBRARY_PATH=./install/lib ./tst_r abc.book
Person ID: 1
   Name: yoshio
   E-mail address: yoshio@mail.com
Person ID: 1
   Name: yoshida
   E-mail address: yoshida@mail.com

まあ、Person IDが重複するかどうかというのはアプリの作りであって、protobufとは関係ないな。


今回はここまで。

次回は、動かしたソースファイルを見ていこう。
そこら辺までやれば、C版も分かるようになるんじゃなかろうかね。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。