Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically build a new protobuf from a set of already defined descriptors?

At my server, we receive Self Described Messages (as defined here... which btw wasn't all that easy as there aren't any 'good' examples of this in c++).

At this point I am having no issue creating messages from these self-described ones. I can take the FileDescriptorSet, go through each FileDescriptorProto, adding each to a DescriptorPool (using BuildFile, which also gives me every defined FileDescriptor).

From here I can create any of the messages which were defined in the FileDescriptorSet with a DynamicMessageFactory instanced with the DP and calling GetPrototype (which is very easy to do as our SelfDescribedMessage required the messages full_name() and thus we can call the FindMessageTypeByName method of the DP, giving us the properly encoded Message Prototype).

The question is how can I take each already defined Descriptor or message and dynamically BUILD a 'master' message that contains all of the defined messages as nested messages. This would primarily be used for saving the current state of the messages. Currently we're handling this by just instancing a type of each message in the server(to keep a central state across different programs). But when we want to 'save off' the current state, we're forced to stream them to disk as defined here. They're streamed one message at a time (with a size prefix). We'd like to have ONE message (one to rule them all) instead of the steady stream of separate messages. This can be used for other things once it is worked out (network based shared state with optimized and easy serialization)

Since we already have the cross-linked and defined Descriptors, one would think there would be an easy way to build 'new' messages from those already defined ones. So far the solution has alluded us. We've tried creating our own DescriptorProto and adding new fields of the type from our already defined Descriptors but got lost (haven't deep dived into this one yet). We've also looked at possibly adding them as extensions (unknown at this time how to do so). Do we need to create our own DescriptorDatabase (also unknown at this time how to do so)?

Any insights?


Linked example source on BitBucket.


Hopefully this explanation will help.

I am attempting to dynamically build a Message from a set of already defined Messages. The set of already defined messages are created by using the "self-described" method explained(briefly) in the official c++ protobuf tutorial (i.e. these messages not available in compiled form). This newly defined message will need to be created at runtime.

Have tried using the straight Descriptors for each message and attempted to build a FileDescriptorProto. Have tried looking at the DatabaseDescriptor methods. Both with no luck. Currently attempting to add these defined messages as an extension to another message (even tho at compile time those defined messages, and their 'descriptor-set' were not classified as extending anything) which is where the example code starts.

like image 609
g19fanatic Avatar asked Aug 16 '12 22:08

g19fanatic


People also ask

Is Protobuf extensible?

Objects in protobuf-net are not extensible by default, since they are just regular . NET classes; this means that any unexpected fields will be silently dropped during deserialization, and will be lost during serialization. It is, however, trivial to support extensions.

Is Protobuf self describing?

Protobuf's wire format is not self-describing, but because of Confluent Schema Registry, your DynamicMessages will always have the schema needed to be able to parse the binary payload.


3 Answers

you need a protobuf::DynamicMessageFactory:

{   using namespace google;    protobuf::DynamicMessageFactory dmf;   protobuf::Message* actual_msg = dmf.GetPrototype(some_desc)->New();    const protobuf::Reflection* refl = actual_msg->GetReflection();    const protobuf::FieldDescriptor* fd = trip_desc->FindFieldByName("someField");   refl->SetString(actual_msg, fd, "whee");    ...     cout << actual_msg->DebugString() << endl; } 
like image 92
hoppy Avatar answered Sep 28 '22 23:09

hoppy


I was able to solve this problem by dynamically creating a .proto file and loading it with an Importer.

The only requirement is for each client to either send across its proto file (only needed at init... not during full execution). The server then saves each proto file to a temp directory. An alternative if possible is to just point the server to a central location that holds all of the needed proto files.

This was done by first using a DiskSourceTree to map actual path locations to in program virtual ones. Then building the .proto file to import every proto file that was sent across AND define an optional field in a 'master message'.

After the master.proto has been saved to disk, i Import it with the Importer. Now using the Importers DescriptorPool and a DynamicMessageFactory, I'm able to reliably generate the whole message under one message. I will be putting an example of what I am describing up later on tonight or tomorrow.

If anyone has any suggestions on how to make this process better or how to do it different, please say so.

I will be leaving this question unanswered up until the bounty is about to expire just in case someone else has a better solution.

like image 41
g19fanatic Avatar answered Sep 28 '22 21:09

g19fanatic


What about serializing all the messages into strings, and making the master message a sequence of (byte) strings, a la

message MessageSet
{
  required FileDescriptorSet proto_files = 1;
  repeated bytes serialized_sub_message = 2;
}
like image 32
Managu Avatar answered Sep 28 '22 22:09

Managu