简体   繁体   English

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

[英]C++ implementation of protobuf `oneof` feature for non-protobuf class

The protobuf oneof feature is great. protobuf oneof功能很棒。 But it only can be used when the fields in oneof are either a primitive type or a protobuf message. 但它只能当在领域中使用oneof是原始类型或protobuf的消息。 What if I have two classes A and B , which are defined by C++ code instead of protobuf messages, and I want to implement a class AorB which is like: 如果我有两个类AB ,它们是由C ++代码而不是protobuf消息定义的,并且我想实现一个类AorB ,例如:

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

I tried to read the generated C++ code of a oneof field to see how that is implemented. 我尝试阅读oneof字段的生成的C ++代码,以了解如何实现。 But it's pretty complicated. 但这很复杂。 Is there any concise way to implement this? 有没有简洁的方法来实现这一目标? Or any template which I can use directly? 还是我可以直接使用的任何模板?

Depending on which version of C++ you're able to use, your options are std::variant , roll your own with variadic templates, or roll your own with union . 根据您可以使用的C ++版本,您的选择为std::variant ,使用可变参数模板滚动自己或使用union滚动自己。 std::variant was added to the language in C++17 and will definitely be the easiest to manage. std::variant已添加到C ++ 17语言中,并且肯定是最容易管理的。 The variadic template version is tricky. 可变参数模板版本比较棘手。

union works to the beginning of the language and would look something like. 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;
    }
  }
};

at a baseline that kinda maybe works. 在某种程度上可能起作用。 There's a pile of details to get it right, though. 不过,还有很多细节可以解决。 The basics of it are that a union puts the values in overlapping memory, only one is valid at a time, and it's undefined behavior to access the wrong one. 它的基本原理是,联合将值放入重叠的内存中,一次仅一个有效,并且访问错误的值是不确定的行为。 You also need to manually destruct before re-assigning the held value. 重新分配保留值之前,您还需要手动销毁。

I probably missed a detail somewhere in there. 我可能错过了某个地方的细节。 I'd rather leave it to std::variant but if you need to write your own discriminated union, it would start something like the code above. 我宁愿将其留给std::variant但是如果您需要编写自己的有区别的联合,它将开始类似于上面的代码。

More details on variant here: https://en.cppreference.com/w/cpp/utility/variant 有关变体的更多详细信息,请访问: https : //en.cppreference.com/w/cpp/utility/variant

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

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