用于非 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 消息时使用。如果我有两个类 A
和 B
,它们是由 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-net RuntimeTypeModel 不序列化基类的成员