用于非 protobuf 类的 protobuf `oneof` 功能的 C++ 实现

Posted

技术标签:

【中文标题】用于非 protobuf 类的 protobuf `oneof` 功能的 C++ 实现【英文标题】:C++ implementation of protobuf `oneof` feature for non-protobuf class 【发布时间】:2019-06-18 07:28:16 【问题描述】:

protobuf oneof 功能很棒。但它只能在oneof 中的字段是原始类型或protobuf 消息时使用。如果我有两个类 AB,它们是由 C++ 代码而不是 protobuf 消息定义的,我想实现一个类 AorB,如下所示:

message AorB 
    oneof oneof_name 
        A a = 1;
        B b = 2;
    

我尝试阅读生成的 oneof 字段的 C++ 代码,以了解它是如何实现的。但这很复杂。有什么简洁的方法来实现这个吗?或者我可以直接使用的任何模板?

【问题讨论】:

我不知道 protobufs,但听起来像 std::variant? In fact, oneof was created to answer the same question in reverse! 【参考方案1】:

根据您可以使用的 C++ 版本,您可以选择std::variant、使用可变参数模板自行开发,或使用union 自行开发。 std::variant 在 C++17 中被添加到语言中,肯定是最容易管理的。可变参数模板版本很棘手。

union 工作到语言的开头,看起来像。

struct MyAorB 
  union 
    A a;
    B b;
  ;
  ~MyAorB()  destruct(); 
  MyAorB& operator=(const MyAorB&) = delete;
  MyAorB& operator=(MyAorB&&) = delete;
  MyAorB(const MyAorB&) = delete;
  MyAorB(const MyAorB&&) = delete;
  enum  HOLDS_NONE, HOLDS_A, HOLDS_B  which_one = HOLDS_NONE;
  A& get_A()  assert(which_one == HOLDS_A); return a; 
  B& get_B()  assert(which_one == HOLDS_B); return b; 
  void set_A(A new_a)  which_one = HOLDS_A; destruct(); a = std::move(new_a); 
  void set_B(B new_b)  which_one = HOLDS_B; destruct(); b = std::move(new_b); 
  void destruct() 
    switch (which_one) 
      case HOLDS_A: a.~A(); break;
      case HOLDS_B: b.~B(); break;
      default: break;
    
  
;

在一个可能有效的基线上。不过,有很多细节可以让它正确。它的基础是联合将值放在重叠的内存中,一次只有一个有效,访问错误的值是未定义的行为。您还需要在重新分配保存的值之前手动销毁。

我可能错过了那里的某个细节。我宁愿把它留给std::variant,但如果你需要编写自己的可区分联合,它会像上面的代码一样开始。

这里有更多关于变体的详细信息:https://en.cppreference.com/w/cpp/utility/variant

【讨论】:

以上是关于用于非 protobuf 类的 protobuf `oneof` 功能的 C++ 实现的主要内容,如果未能解决你的问题,请参考以下文章

使用 ProtoBuf 序列化动态 JSON - Java

Protobuf-net 包括不可序列化基类的特定成员

Protobuf-net 创建具有接口和抽象基类的类型模型

Protobuf-net RuntimeTypeModel 不序列化基类的成员

使用非标准构造函数序列化和反序列化对象 - protobuf-net

ProtoBuf系列protobuf的介绍与安装