Protocol Buffers によるメッセージ定義

Protocol Buffers

Protocol Buffersは、Google 社が開発している直列化のための公開技術であり、nine の直列化に利用することができる。

nine では、C++ 生成コードのみの対応となっている。

文書構成

ここでは protobuf を用いて定義、生成したデータを nine から使う方法について説明する。 Protocol Buffers そのもの利用方法については、Protocol Buffers 翻訳文書を参照のこと。

nineメッセージから protobufメッセージを使う

nineメッセージから protobufメッセージを使う方法は二つある。
  • nineメッセージとは別の構造体として扱う
  • nineメッセージとして扱う
いずれにしても、nine/nine.h とは別に nine/ninepb.h をインクルードする必要がある。

nineメッセージとは別の構造体として扱う

protobuf で定義したメッセージには、対応する C++ クラスが定義される。 これをそのままデータ型として扱う。 たとえばメッセージクラスのメンバ変数にすることができる。

protobuf から生成したクラスは、nine とは無関係の C++ クラスでしかない。 これをMessage クラスの直書き方法に則って、nine のメッセージでラップする。 google::protobuf::Message を nine::Buffer へ読み書きする関数を提供しているので、これを使えばよい。

foo.proto:
  message Foo {
    required int32 id = 1;
  };

bar.cpp:
  #include <foo.pb.h>
  #include <nine/ninepb.h>

  class Bar: public nine::Message {
    Foo m_foo;
    public:
      virtual int getMarshalSize() const
        { return nine::PbGetMarshalSize(&m_foo); }
      virtual void marshal(Buffer* pBuf) const
        { nine::PbMarshal(pBuf, &m_foo); }
      virtual void unmarshal(Buffer* pBuf)
        { nine::PbUnmarshal(pBuf, &m_foo); }
  };

nineメッセージとして扱う

protobuf から生成されたメッセージクラスと nine::Message を多重継承した新たなクラスを定義するやり方である。

protobuf メッセージの操作インターフェースをそのまま使うことができ、さらに nine::Message としても扱うことができる。

この方法では、簡単な二つのコードを追加するを踏む必要がある。
  • nineメッセージの KIND と TYPE を proto 定義に追加する
  • テンプレートを用いてnineメッセージを実現する。

nineメッセージでは、メッセージID のユニークさをユーザーが担保する必要がある。 しかし、protobuf ではメッセージID というものは無いので、特別な決まり事を導入した。

protoファイルでのメッセージ定義の内部で、NINE_MSGKIND と NINE_MSGTYPE という enum定数を指定する。
foo.proto:
   message Foo {
     enum En { NINE_MSGKIND = 2; NINE_MSGTYPE = 0x011; }

     require int32 id = 1;
     ...
   }
KIND, TYPE の意味は nineメッセージのそれと同じである。
さて、protoc で生成したクラス Foo は nineメッセージではない。 nine メッセージにするために、nine::PbMessage クラステンプレートを使う。
foouser.cpp:
  #include <foo.pb.h>
  #include <nine/ninepb.h>

  class MyServer: public nine::MessageServer {
    typedef nine::PbMessage< Foo > PbFoo;
PbMessage< Foo > は普通の nine メッセージのように使うことができる。
    void handleFoo(Pbfoo* pMsg) { ... }

    bool handleMessage(nine::Message* pMsg) {
      if (tryDispatch(&MyServer::handleFoo, pMsg)) { return true; }
      else { ... }
    }