【问题标题】:c++ protobuf oneof deserializing genericallyc++ protobuf oneof 反序列化
【发布时间】:2020-08-03 22:12:02
【问题描述】:

我想知道是否有一种通用的方法来反序列化具有oneof 子消息的消息。 oneof 消息将包含许多其他消息。现在,我可以在case 号码上使用switch 声明并调用适当的getter 来让它工作。但是,如果有一个通用的方法,那么将来我就不必继续添加新的案例了。

我想消除这些情况并有一些通用的东西,我可以在其中获取消息类型,即 Exit 并调用正确的 getter。有没有办法通过反射来做到这一点?我在网上找了一些例子,但找不到太多。

原型文件:

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

代码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.
    }
}

【问题讨论】:

  • 顺便说一句,您应该在继续使用解析的消息之前测试ParseFromString() 的返回值(布尔值),即if ( !clientMsg.ParseFromString(buf) ) { /* handle_error_here */ }
  • 另一个关于 ParseFromString(buf) 的问题。 :它还会填充 oneof 消息吗?还是我也需要在 oneof 对象上调用 ParseFromString 。我正在获取默认值。
  • 二进制消息将被反序列化,结果将是填充的消息对象。如果它是消息的一部分,它应该被自动反序列化。 问题:您为什么使用oneof?看起来应该是enum。示例:godbolt.org/z/rnnx8x.
  • 知道了。我看到oneof 为您提供了某种自动化枚举和对象处理设施。建议:您应该使用Generated Code 文章中描述的默认值(索引零),以便在未正确设置消息的情况下使错误处理更加稳健。

标签: c++ generics reflection protocol-buffers


【解决方案1】:

这里有一些代码应该适用于此。

在初始化的某个时候,您可以在其中一个字段编号和索引之间构建一个映射。

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

稍后您可以使用该映射访问 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;

如果不对 one-of 的名称进行硬编码,我一直无法找到一种方法来获取 one-of 描述符,这感觉有点蹩脚。直接从 AllClientMessages.client_message() 获取它会很好。

【讨论】:

    猜你喜欢
    • 2016-07-19
    • 1970-01-01
    • 2014-12-30
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    相关资源
    最近更新 更多