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();

и так далее. Такой подход не удобен в случае, когда есть большое количество полей.

могу ли я сделать это с помощью итерации и получить имя и тип поля? Я знаю, что есть дескрипторов типа и несколько называется отражение, но у меня не было успеха в моих попытках. Может кто-нибудь дать мне пример кода, если это возможно?

спасибо!

2 ответов


посмотрите, как библиотека Protobuf реализует TextFormat::Printer класс, который использует дескрипторы и отражение для итерации по полям и преобразования их в текст:

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


это старый, но, возможно, кто-то выиграет. Вот метод, который печатает содержимое сообщения 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 возможные значения, которые вы получаете от field->type. Например, для типа сообщения вам нужно будет проверить, равен ли type FieldDescriptor::TYPE_MESSAGE.

эта функция печатает все "метаданные" сообщения protobuf. Однако вам нужно проверить отдельно для каждого значения, что такое тип, а затем вызовите соответствующую функцию 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());
      }

однако поля могут повторяться или не повторяться, и для обоих типов полей используются разные методы. Поэтому здесь используется проверка, чтобы убедиться, что мы используем правильный метод. Для повторяющихся полей у нас есть, например, этот метод для строк:

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

таким образом, он принимает индекс повторное поле в рассмотрение.

в случае 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 метод на отражение и повторить все повторяющиеся поля.