Roy Huang
Roy Huang

Reputation: 629

How to cast protobuf Dynamic Message to declared Message in C++?

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

Upvotes: 2

Views: 2077

Answers (1)

Rafael Lerm
Rafael Lerm

Reputation: 1400

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.

Upvotes: 1

Related Questions