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

时间:2019-06-18 07:28:16

标签: c++ protocol-buffers

protobuf oneof功能很棒。但是,仅当oneof中的字段是原始类型或protobuf消息时才可以使用它。如果我有两个类AB,它们是由C ++代码而不是protobuf消息定义的,并且我想实现一个AorB类,例如:

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

我试图读取oneof字段的生成的C ++代码,以了解其实现方式。但这很复杂。有没有简洁的方法来实现这一目标?还是我可以直接使用的任何模板?

1 个答案:

答案 0 :(得分: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