2008/08/05

perl版もあったんですね。
Perl App Engine状況報告、Protocol BufferのPerl対応 | エンタープライズ | マイコミジャーナル

Perl App Engineに関連したコードベースにはおもに次の3つがある。

  • Perl App Engine
  • Perl XS module Sys::Protect
  • Protocol Buffers for Perl
http://journal.mycom.co.jp/news/2008/07/29/042/index.html

protobuf-perl - Google Code

Protocol Buffers for Perl.

http://code.google.com/p/protobuf-perl/
さっそく遊んでみました。
まず、protobuf-perlはオリジナルの改造として作られており、スケルトンクラス生成ツール「protoc」をビルドする所からの導入となります。
通常はprotobufディレクトリで # make -j2 protoc
とすれば出来上がります。ただVisual Studioの場合はlibprotocプロジェクトのソース一覧からperlのgeneratorソースである「perl_generator.cc」が外れてしまっているので、追加してからビルドする必要があります。
出来上がれば、「libprotobuf.dll」と「libprotoc.dll」、「protoc.exe」をパスの通った位置にコピーします。
"main.cc"のソースを見て頂ければ分かりますが、protocol buffersのgeneratorはソースが分離されており、main実行時にプラグインを読み込む様な形で登録されています。 int main(int argc, char* argv[]) {

  google::protobuf::compiler::CommandLineInterface cli;

  // Proto2 C++
  google::protobuf::compiler::cpp::CppGenerator cpp_generator;
  cli.RegisterGenerator("--cpp_out", &cpp_generator,
                        "Generate C++ header and source.");

  // Proto2 Java
  google::protobuf::compiler::java::JavaGenerator java_generator;
  cli.RegisterGenerator("--java_out", &java_generator,
                        "Generate Java source file.");


  // Proto2 Python
  google::protobuf::compiler::python::Generator py_generator;
  cli.RegisterGenerator("--python_out", &py_generator,
                        "Generate Python source file.");


  // Proto2 Perl
  google::protobuf::compiler::perl::Generator perl_generator;
  cli.RegisterGenerator("--perl_out", &perl_generator,
                        "Generate Perl source file.");

  return cli.Run(argc, argv);
}
別の言語でgeneratorを作る場合、"XXX_generator.h"と"XXX_generator.cc"を作り、"main.cc"でincludeとRegisterGeneratorを実行すれば動き出す仕組みになっています。キレイな作りですね。

さて出来上がった"protoc"でperlのラッパクラスを作ります。
先日ご紹介した際に使った"person.proto"を使います。ここで注意しなければならないのが、"--perl_out"オプションを使用する際の引数で指定するファイル名がパッケージ名として使われてしまう点です。
perlパッケージ名称がそれっぽくなる様に以下の様に実行します。
# protoc --perl_out=. Person.proto UNIX系の方は"person.proto"から"Person.proto"にリネームしてから実行して下さい。Windowsならばそのまま実行出来ます。
出来上がった"ProtoBuf::Person"は以下の様になりました。
## Boilerplate:
# Auto-generated code from the protocol buffer compiler.  DO NOT EDIT!

use strict;
use warnings;
#use 5.6.1;
use Protobuf;
package ProtoBuf::Person;


use constant TRUE => 1;
use constant FALSE => 0;
## Top-level enums:

## Top-level extensions:

## All nested enums:
## Message descriptors:

our $_PERSON = Protobuf::Descriptor->new(
  name => 'Person',
  full_name => 'protocol.Person',
  containing_type => undef,
  fields => [
    Protobuf::FieldDescriptor->new(
      name => 'name', index => 0, number => 1,
      type => 9, cpp_type => 9, label => 2,
      default_value => "",
      message_type => undef, enum_type => undef, containing_type => undef,
      is_extension => FALSE, extension_scope => undef),
    Protobuf::FieldDescriptor->new(
      name => 'age', index => 1, number => 2,
      type => 5, cpp_type => 1, label => 2,
      default_value => 0,
      message_type => undef, enum_type => undef, containing_type => undef,
      is_extension => FALSE, extension_scope => undef),
  ],
  extensions => [
  ],
  nested_types => [],  # TODO(bradfitz): Implement.
  enum_types => [
  ],
  options => Protobuf::MessageOptions->new(
  ),
);

## Imports:

## Fix foreign fields:

## Messages:
Protobuf::Message->GenerateClass(__PACKAGE__ . '::Person', $_PERSON);

## Fix foreign fields in extensions:
## Services:
実装はMooseを使っており、アクセサに対する制御も行っています。
次にサンプルコード
use strict;
use warnings;
no warnings qw(deprecated);
use Protobuf::Decoder;
use ProtoBuf::Person;

my $person = ProtoBuf::Person::Person->new;
$person->set_name('mattn');
$person->set_age(18);

open my $out, '>', 'person_perl.txt';
print $out $person->serialize_to_string();
close $out;

my $other = ProtoBuf::Person::Person->new;
open my $in, '<', 'person_perl.txt';
$other->parse_from_string( do { local $/;<$in> } );
close $in;

printf "name:%s\nage:%d\n", $other->name(), $other->age();
前回と同様にファイルに書き出し、ファイルから読み込んでparse_from_stringで実体化します。結果は前回と同様です。

cppは別として、pythonの場合はpickleが、javaの場合はSerializableがあり、Google Protocol Buffersの使い所が難しい気もしましたが、色々な言語がサポートされてくると言語をまたいだデータ共有という形で使えそうな気がして来ました。特にmemcachedを使ったダイナミックインスタンスエクスポートとか面白いかもしれませんね。
またperl版がMooseと絡んでいる所がとても通ぽくて良いですね。:-)
どうせなら、PerlIOでシリアライズ出来たらな...と思ってしまいましたが、もしかしたら今後サポートされるかもしれませんね。

他にもprotocol buffersはいろんな言語でインプリメンツされているので、皆さんもお好きな言語で遊んでみてはいかがでしょうか。
Posted at 14:04 | WriteBacks () | Edit
Edit this entry...

wikieditish message: Ready to edit this entry.






















A quick preview will be rendered here when you click "Preview" button.