Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ protobuf: how to iterate through fields of message?

Tags:

I'm new to protobuf and I'm stuck with simple task: I need to iterate through fields of message and check it's type. If type is message I will do same recursively for this message.

For example, I have such messages:

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;
}

Now, to explicitly access a field value in C++ I can do this:

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

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

and so on. This approach is not convenient in case when have big amount of fields.

Can I do this with iteration and get field's name and type? I know there are descriptors of type and somewhat called reflection, but I didn't have success in my attempts. Can some one give me example of code if it's possible?

Thanks!

like image 340
Dania Avatar asked May 30 '14 22:05

Dania


People also ask

What is repeated field in Protobuf?

repeated : this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.

What is field number in Protobuf?

Field numbers are an important part of Protobuf. They're used to identify fields in the binary encoded data, which means they can't change from version to version of your service. The advantage is that backward compatibility and forward compatibility are possible.

Is Protobuf faster than JSON?

In one – protobufjs was faster, and in the second — JSON was faster. Looking at the schemas, the immediate suspect was the number of strings. We ran the benchmark with this payload (10,000 strings, of length 10 each).

Can you remove field from Protobuf?

Removing fields is fine, although you might want to mark it reserved so that nobody reuses it in an incompatible way. New code with old data (with the field) will silently ignore it; old code with new data will just load without the field populated, since everything in proto3 is implicitly optional .

What is a Protobuf repeated field?

Protobuf. Collections. RepeatedField< T > The contents of a repeated field: essentially, a collection with some extra restrictions (no null values) and capabilities (deep cloning).

How does a protocol buffer compiler work?

From that, the protocol buffer compiler creates a class that implements automatic encoding and parsing of the protocol buffer data with an efficient binary format. The generated class provides getters and setters for the fields that make up a protocol buffer and takes care of the details of reading and writing the protocol buffer as a unit.

Why does libprotobuf fail to parse uninitialized messages?

If libprotobuf is compiled in debug mode, serializing an uninitialized message will cause an assertion failure. In optimized builds, the check is skipped and the message will be written anyway. However, parsing an uninitialized message will always fail (by returning false from the parse method).

Can I use a type which is not supported by Protocol Buffers?

This implementation does not generally prohibit the use of types which are not supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases. The element type of the repeated field.


2 Answers

This is old but maybe someone will benefit. Here is a method that prints the contents of a protobuf message:

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

You can find in FieldDescriptor Enum Values the possible values you get from field->type. For example for the message type you would have to check if type is equal to FieldDescriptor::TYPE_MESSAGE.

This function prints all the "metadata" of the protobuf message. However you need to check separately for each value what the type is and then call the corresponding getter function using Reflection.

So using this condition we could extract the strings :

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

However fields can be either repeated or not repeated and different methods are used for both field types. So a check is used here to assure that we are using the right method. For repeated fields we have for example this method for strings :

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

So it takes the index of the repeated field into consideration.

In the case of FieldDescriptor of type Message, the function provided will only print the name of the message, we better print its contents too.

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

And finally if the field is repeated you will have to call the FieldSize method on the reflection and iterate all repeated fields.

like image 88
M.C. Avatar answered Sep 27 '22 16:09

M.C.


Take a look at how the Protobuf library implements the TextFormat::Printer class, which uses descriptors and reflection to iterate over fields and convert them to text:

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

like image 42
Kenton Varda Avatar answered Sep 27 '22 16:09

Kenton Varda