0

I'm trying to construct parser by the proto at runtime. I create dynamic message pointer by DynamicMessageFactory, and I want to cast the pointer to the exact type so that I can get access to some fields directly (instead of by reflection).

Here is my code, the ShortDebugString() output seems fine. But I didn't get the right value of field id. Why?

#include "MyData.pb.h" // I use generated pb codes at the same time.
/*
file: MyData.proto
message MyData {
    int32 id = 1;
};
*/

static const std::string message_type = "MyData";
std::ifstream proto_ifs("MyData.proto");
std::string proto_content((std::istreambuf_iterator<char>(proto_ifs)),
                           std::istreambuf_iterator<char>());
ArrayInputStream raw_input(proto_content.c_str(), proto_content.size());
Tokenizer input(&raw_input, NULL);

FileDescriptorProto file_desc_proto;
Parser parser;
if (!parser.Parse(&input, &file_desc_proto)) {
    std::cerr << "Failed to parse .proto definition:" << proto_content;
    return -1;
}

if (!file_desc_proto.has_name()) {
    file_desc_proto.set_name(message_type);
}

google::protobuf::DescriptorPool pool;
const google::protobuf::FileDescriptor* file_desc = pool.BuildFile(file_desc_proto);

const auto* message_desc = file_desc->FindMessageTypeByName(message_type);
google::protobuf::DynamicMessageFactory factory;
factory.SetDelegateToGeneratedFactory(true);
const google::protobuf::Message* prototype_msg;
prototype_msg = factory.GetPrototype(message_desc);
google::protobuf::Message* mutable_msg = prototype_msg->New();

MyData tmp;
tmp.set_id(111);

int32_t msg_len;
const char* msg_data = tmp.SerializeToArray(msg_data, msg_len);

mutable_msg->ParseFromArray(msg_data, msg_len);
const MyData& my_data = static_cast<const MyData&>(*mutable_msg);

std::cout << mutable_msg->ShortDebugString() << "\n";
std::cout << my_data.ShortDebugString() << "\n";
std::cout << my_data.id() << "\n";

outputs:

id: 111
id: 111
0
Roy Huang
  • 579
  • 3
  • 23

1 Answers1

1

Basically, you can't. There isn't an inheritance dependency between MyData and the generic Message that you have.

What you can do, though, is serialize the message and deserialize it again:

MyData my_data;
my_data.ParseFromString(mutable_msg->SerializeAsString());

Of course, this will copy all the data over, and the parsed message wouldn't benefit from any newer versions of the schema, such as new enum values.

Rafael Lerm
  • 1,340
  • 7
  • 10
  • I just edited: `#include "MyData.pb.h" // I use generated pb codes at the same time.` I think in this case, `class MyData` is the extended class of `google::protobuf::Message`. Is that right? :) – Roy Huang Aug 27 '21 at 06:56
  • 1
    That's right. `MyData` extends `Message`. However, the `Message*` you get is not a `MyData`, it's some other subclass of `Message` (namely, [`DynamicMessage`](https://github.com/protocolbuffers/protobuf/blob/8cf990fc9f3651502f42de8c14cb06d865f8a06b/src/google/protobuf/dynamic_message.cc#L835)). Casting to a pointer of a subclass is only valid when the object really is of that subclass. Otherwise, it's [undefined behavior](https://stackoverflow.com/questions/49399783/is-it-undefined-behavior-to-static-cast-down-a-type-that-isnt-actually-the-type). It's just not a valid code. – Rafael Lerm Aug 27 '21 at 17:04
  • 1
    I realize you're calling `SetDelegateToGeneratedFactory` and expecting to get an instance of the generated code? That's not really [what it does](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.dynamic_message#DynamicMessageFactory.SetDelegateToGeneratedFactory.details); that would only happen if you passed it the *generated* descriptor (e.g. `MyData::descriptor()`). – Rafael Lerm Aug 27 '21 at 17:06
  • Since static cast doesn't work, I tried to parse data by reflection. But I met another problem https://stackoverflow.com/questions/69113912/protobuf-dynamicmessage-reflection-addmessage-pure-virtual-method-called . Any help pls? – Roy Huang Sep 09 '21 at 09:39