c ++ protobuf:如何遍历消息字段?

Dan*_*nia 15 c++ iteration protocols protocol-buffers

我是protobuf的新手,我遇到了简单的任务:我需要遍历消息字段并检查它的类型.如果type是message,我将以递归方式为此消息执行相同操作.

例如,我有这样的消息:

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;
}
Run Code Online (Sandbox Code Playgroud)

现在,要在C++中显式访问字段值,我可以这样做:

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

bool option1val = config.globalSettings().option1();
bool option2val = config.globalSettings().option2();
Run Code Online (Sandbox Code Playgroud)

等等.当有大量字段时,这种方法不方便.

我可以通过迭代执行此操作并获取字段的名称和类型吗?我知道有类型的描述符,有些叫反射,但我的尝试没有成功.有人可以给我一些代码示例吗?

谢谢!

M.C*_*.C. 20

这是旧的,但也许有人会受益.这是一个打印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());
      }
 } 
Run Code Online (Sandbox Code Playgroud)

您可以在FieldDescriptor Enum Values中找到您可能获得的值field->type.例如,对于消息类型,您必须检查类型是否等于FieldDescriptor::TYPE_MESSAGE.

此功能打印protobuf消息的所有"元数据".但是,您需要分别检查每个值的类型,然后使用Reflection调用相应的getter函数.

所以使用这个条件我们可以提取字符串:

 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());
      }
Run Code Online (Sandbox Code Playgroud)

但是,可以重复或不重复字段,并且对两种字段类型使用不同的方法.因此,这里使用检查来确保我们使用正确的方法.对于重复的字段,我们有例如字符串的这种方法:

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);
          }
       }
Run Code Online (Sandbox Code Playgroud)

最后,如果重复该字段,则必须FieldSize在反射上调用该方法并迭代所有重复的字段.


Ken*_*rda 9

看一下Protobuf库如何实现TextFormat::Printer该类,该类使用描述符和反射来迭代字段并将它们转换为文本:

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