The protobuf oneof
feature is great. But it only can be used when the fields in oneof
are either a primitive type or a protobuf message. 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:
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. 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
. std::variant
was added to the language in C++17 and will definitely be the easiest to manage. The variadic template version is tricky.
union
works to the beginning of the language and would look something like.
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.
More details on variant here: https://en.cppreference.com/w/cpp/utility/variant
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.