首页 > 解决方案 > 用于非 protobuf 类的 protobuf `oneof` 功能的 C++ 实现

问题描述

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

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

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

标签: c++protocol-buffers

解决方案


根据您能够使用的 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


推荐阅读