Google Protocol Bufferの学習要点

7287 ワード

基礎知識

  • protobufの用語では、構造化データをMessage
  • と呼ぶ.
  • a

  • きほんこうぞう


    リスト1.protoファイル

     package lm; 
     message helloworld 
     { 
        required int32     id = 1;  // ID 
        required string    str = 2;  // str 
        optional int32     opt = 3;  //optional field 
     }

    コンパイルprotoファイル


    Protoファイルを作成すると、Protobufコンパイラでターゲット言語にコンパイルできます.この例では、C++を使用します.protoファイルが$SRCに保存されているとします.DIRの下で、生成されたファイルを同じディレクトリに配置したい場合は、次のコマンドを使用します.
     protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto

    コマンドを使用すると、2つのファイルが生成されます.
    lm.helloworld.pb.h,C++クラスを定義したヘッダファイル
    lm.helloworld.pb.cc,C++クラスの実装ファイル
    生成されたヘッダファイルには、C++クラスhelloworldが定義され、後のWriterとReaderはこのクラスを使用してメッセージを操作します.メッセージのメンバーを割り当てたり、メッセージをシーケンス化したりする方法があります.

    writerとReaderの作成


    必要:
    Writerは、他の人が読み取るために構造化されたデータをディスクに書き込みます.Protobufを使わなければ、実は多くの選択肢があります.可能な方法は、データを文字列に変換し、文字列をディスクに書き込むことです.
    文字列の欠点:
    しかしよく考えてみると、このようなやり方はReaderを書いた人に対する要求が高く、Readerの作者はWriterの細部を必要としていることがわかります.例えば、「123」は、単一の数字123であってもよいし、3つの数字1,2および3であってもよい.
    Protobufの利点を使用するには、次の手順に従います.
    Protobufを使用すると、これらの詳細をアプリケーションで考慮する必要がなくなります.1.処理すべき構造化データは.protoファイルの説明;2.Writerはヘッダファイルをincludeする必要があり、このクラスを使用することができます.

    使用


    前節のコンパイル過程を経て、このデータ化構造はC++のクラスに対応し、lmに定義.helloworld.pb.h中.この例では、クラス名はlm::helloworldです.Writerコードでは、ディスクに格納する構造化データはlm::helloworldクラスのオブジェクトによって表され、構造化データのデータメンバー、またはfieldと呼ばれる一連のget/set関数を提供します.

    プロセス


    構造化されたデータをディスクに保存する必要がある場合、クラスlm::helloworldは複雑なデータをバイトシーケンスにする方法を提供しており、このバイトシーケンスをディスクに書き込むことができます.
    このデータを読み出すプログラムにとっても、クラスlm::helloworldの対応する逆シーケンス化方法を使用して、このバイトシーケンスを構造化データに再変換する必要があります.これは私たちが始めたときの「123」の考えと似ていますが、Protobufが考えていたのは私たちの粗い文字列の変換よりはるかに全面的なので、安心してProtobufに任せたほうがいいでしょう.

    リスト2.Writerの主なコード

    #include "lm.helloworld.pb.h"
    …
    
     int main(void) 
     { 
    
      lm::helloworld msg1; 
      msg1.set_id(101); 
      msg1.set_str(“hello”); 
    
      // Write the new address book back to disk. 
      fstream output("./log", ios::out | ios::trunc | ios::binary); 
    
      if (!msg1.SerializeToOstream(&output)) { 
          cerr << "Failed to write msg." << endl; 
          return -1; 
      }         
      return 0; 
     }

    Msg 1はhelloworldクラスのオブジェクトで、set_id()はidの値を設定するために使用されます.SerializeToOstreamオブジェクトをシーケンス化してfstreamストリームに書き込みます.

    リスト3.Reader

     #include "lm.helloworld.pb.h" void ListMsg(const lm::helloworld & msg) { 
      cout << msg.id() << endl; 
      cout << msg.str() << endl; 
     } 
    
     int main(int argc, char* argv[]) { 
    
      lm::helloworld msg1; 
    
      { 
        fstream input("./log", ios::in | ios::binary); 
        if (!msg1.ParseFromIstream(&input)) { 
          cerr << "Failed to parse address book." << endl; 
          return -1; 
        } 
      } 
    
      ListMsg(msg1); 
      … 
     }
    

    同様に、Readerはクラスhelloworldのオブジェクトmsg 1を宣言し、ParseFromIstreamを使用してfstreamストリームから情報を読み出し、逆シーケンス化します.その後、ListMsgではget方式でメッセージの内部情報を読み出し、印刷出力操作を行う.

    実行結果


    WriterとReaderを実行した結果は次のとおりです.
     >writer >reader 101 Hello 

    Readerは、ファイルlogのシーケンス化情報を読み込み、画面に印刷します.

    リスト4.ネストMessageの例

    message Person { 
      required string name = 1; 
      required int32 id = 2;        // Unique ID number for this person. 
      optional string email = 3; 
    
      enum PhoneType { 
        MOBILE = 0; 
        HOME = 1; 
        WORK = 2; 
      } 
    
      message PhoneNumber { 
        required string number = 1; 
        optional PhoneType type = 2 [default = HOME]; 
      } 
      repeated PhoneNumber phone = 4; 
     }

    Message Personでは、ネストされたメッセージPhoneNumberが定義され、Personメッセージのphon eドメインを定義するために使用されます.これにより、より複雑なデータ構造を定義することができます.

    Import Message


    一つです.protoファイルには、Importキーで他に導入することもできる.protoファイルで定義されたメッセージは、Import Message、またはDependency Messageと呼ぶことができます.
    たとえば、次の例です.
    リスト5.コード#コード#
     import common.header; 
    
     message youMsg{ 
      required common.info_header header = 1; 
      required string youPrivateData = 2; 
     }