web-dev-qa-db-ja.com

c ++ protobuf:メッセージのフィールドを反復する方法?

私はprotobufを使い始めたばかりで、単純なタスクに悩まされています。メッセージのフィールドを反復処理して、そのタイプを確認する必要があります。タイプがメッセージの場合、このメッセージに対して同じことを再帰的に行います。

たとえば、次のようなメッセージがあります。

package MyTool;

message Configuration {
    required GloablSettings         globalSettings  = 1;
    optional string                 option1         = 2;
    optional int32                  option2         = 3;
    optional bool                   option3         = 4;

}

message GloablSettings {
    required bool                   option1         = 1;
    required bool                   option2         = 2;
    required bool                   option3         = 3;
}

C++でフィールド値に明示的にアクセスするには、次のようにします。

MyTool::Configuration config;
fstream input("config", ios::in | ios::binary);
config.ParseFromIstream(&input);

bool option1val = config.globalSettings().option1();
bool option2val = config.globalSettings().option2();

等々。このアプローチは、フィールドが大量にある場合には便利ではありません。

これを繰り返しで実行して、フィールドの名前とタイプを取得できますか?タイプのdescriptorsと呼ばれ--reflectionと呼ばれることは知っていますが、私の試みは成功しませんでした。可能であれば、誰かがコードの例を教えてもらえますか?

ありがとう!

16
Dania

ProtobufライブラリがTextFormat::Printerクラスを実装する方法を見てください。これは、記述子とリフレクションを使用してフィールドを反復処理し、それらをテキストに変換します。

https://github.com/google/protobuf/blob/master/src/google/protobuf/text_format.cc#L147

9
Kenton Varda

これは古いですが、多分誰かが恩恵を受けるでしょう。次に、protobufメッセージの内容を出力するメソッドを示します。

 void Example::printMessageContents(std::shared_ptr<google::protobuf::Message> m)
 {
      const Descriptor *desc       = m->GetDescriptor();
      const Reflection *refl       = m->GetReflection();   
      int fieldCount= desc->field_count();
      fprintf(stderr, "The fullname of the message is %s \n", desc->full_name().c_str());
      for(int i=0;i<fieldCount;i++)
      {
        const FieldDescriptor *field = desc->field(i);
        fprintf(stderr, "The name of the %i th element is %s and the type is  %s \n",i,field->name().c_str(),field->type_name());
      }
 } 

FieldDescriptor Enum Values で、field->typeから取得できる値を見つけることができます。たとえば、メッセージタイプの場合、タイプがFieldDescriptor::TYPE_MESSAGEに等しいかどうかを確認する必要があります。

この関数は、protobufメッセージのすべての「メタデータ」を出力します。ただし、型が何であるかを値ごとに個別にチェックしてから、対応するゲッター関数を Reflection を使用して呼び出す必要があります。

したがって、この条件を使用して、文字列を抽出できます。

 if(field->type() == FieldDescriptor::TYPE_STRING  && !field->is_repeated())
      {
            std::string g= refl->GetString(*m, field);
            fprintf(stderr, "The value is %s ",g.c_str());
      }

ただし、フィールドは繰り返すことも繰り返さないこともでき、両方のフィールドタイプに異なる方法が使用されます。したがって、ここではチェックを使用して、正しい方法を使用していることを確認します。繰り返しフィールドの場合、たとえば次のような文字列のメソッドがあります。

GetRepeatedString(const Message & message, const FieldDescriptor * field, int index

したがって、繰り返しフィールドのインデックスが考慮されます。

MessageタイプのFieldDescriptorの場合、提供される関数はメッセージの名前のみを出力します。その内容も出力する方がよいでしょう。

      if(field->type()==FieldDescriptor::TYPE_MESSAGE)
       {
         if(!field->is_repeated())  
         {
           const Message &mfield = refl->GetMessage(*m, field);      
           Message *mcopy = mfield.New();
           mcopy->CopyFrom(mfield);
           void *ptr = new std::shared_ptr<Message>(mcopy);
           std::shared_ptr<Message> *m =
           static_cast<std::shared_ptr<Message> *>(ptr);
           printMessageContents(*m);
          }
       }

そして最後に、フィールドが繰り返される場合、リフレクションでFieldSizeメソッドを呼び出し、すべての繰り返されたフィールドを繰り返す必要があります。

22
M.C.