简体   繁体   English

c++ protobuf 一般反序列化之一

[英]c++ protobuf oneof deserializing generically

I am wondering if there is a generic way to deserialize a message that has a oneof sub-message.我想知道是否有一种通用的方法来反序列化具有oneof子消息的消息。 The oneof message will contain many other messages. oneof消息将包含许多其他消息。 Right now, I can get it working using a switch statement on the case number and call the appropriate getter.现在,我可以在case编号上使用switch语句并调用适当的 getter 来让它工作。 But, if there is a generic way so that in the future I don't have to keep adding new cases.但是,如果有一种通用的方法,那么将来我就不必继续添加新的案例了。

I would like to eliminate the cases and have something generic where I could get the message type ie Exit and call the correct getter.我想消除这些情况并拥有一些通用的东西,我可以在其中获取消息类型,即Exit并调用正确的 getter。 Is there a way to do it with reflection?有没有办法用反射来做到这一点? I looked for examples online but couldn't find much.我在网上寻找示例,但找不到太多。

proto file:原型文件:

message AllClientMessages {
    oneof client_message {
        util.client.command Exit exit = 1
        util.client.command SendNew sendnew = 2
        util.client.command CancelAll cancelAll = 3
        // ... (more than 60 messages)
    }
}

Code ( buf contains the serialized message):代码buf包含序列化消息):

const std::unique_ptr<google::protobuf::Message> ProtoBuilder::deserialize(char* buf)
{
    AllClientMessages clientMsg;
    clientMsg.ParseFromString(buf);

    switch(clientMsg.client_message_case())
    {
    case AllClientMessages::kExitFieldNumber: //generate enums
        return make_unique<Exit>(clientMsg.exit());

    case AllClientMessages::kSendNewFieldNumber:
        return make_unique<SendNew>(clientMsg.sendnew());

    // I access the getter and return unique_ptr to base protobuf Message.
    // ... I do this for each case.
    }
}

Here's some code that should work for this.这是一些适用于此的代码。

Some time at initialization you can construct a map between one-of field number and index.在初始化的某个时候,您可以在其中一个字段号和索引之间构造一个 map。

auto descriptor = clientMsg.GetDescriptor();
auto oneofDescriptor = descriptor->FindOneofByName("client_message");

// Map from oneof field number to field index
std::unordered_map<int, int> numberToIndexMap;

for (int idx = 0; idx < oneofDescriptor->field_count(); idx++)
{
    auto fieldDescriptor = oneofDescriptor->field(idx);
    numberToIndexMap[fieldDescriptor->number()] = idx;
}

Then later on you can use that map to access the correct field indicated by the type_case .然后稍后您可以使用 map 访问type_case指示的正确字段。

auto reflection = clientMsg.GetReflection();
auto descriptor = clientMsg.GetDescriptor();
auto oneofDescriptor = descriptor->FindOneofByName("client_message");
const auto & message = reflection->GetMessage(clientMsg, oneofDescriptor->field(numberToIndexMap.at(int(event.type_case()))));

// If we want a new copy to return as unique ptr
auto copy = std::make_unique<google::protobuf::Message>(message.New());
copy->CopyFrom(message);

return copy;

I haven't been able to find a way to get the one-of descriptor without hardcoding the name of the one-of, which feels kind of lame.我一直无法找到一种方法来获取其中一个描述符而不对其中之一的名称进行硬编码,这感觉有点蹩脚。 It would be nice to be about to get it directly from AllClientMessages.client_message().直接从 AllClientMessages.client_message() 获取它会很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM