I am able to write code to deserialize a proto message that contains a oneof section without having to know beforehand what the oneof section contains. I can not figure out how to write a similar set of struct definitions that I can deserialize the same way using Cereal.
I was using Protobufs to serialize/deserialize some data, but I ran into the same problem as piaoxu. So I switched to using Cereal.
I have been able to convert all of the original proto definitions to C++ struct definitions that can be serialized using Cereal, except the proto definitions that use the oneof functionality.
Here is an example set of a proto definitions that I'd like to convert to structs:
syntax = "proto3";
package Messages;
message A {}
message B {}
message Message {
oneof contents {
A a = 1;
B b = 2;
}
}
Here is the corresponding C++ code I had written to deserialize and parse a received Message
. Using the generated protobuf code, I was able to deserialize a Message
without first knowing if it contains an A
or a B
:
void ParseData(const string& data) {
{
auto message = new Messages::Message();
message->ParseFromString(data);
switch (message->contents_case()) {
case Messages::Message::kA:
std::cout << "I got an A!" << std::endl;
break;
case Messages::Message::kB:
std::cout << "I got a B!" << std::endl;
break;
default:
break;
}
}
And here is my attempt at making an equivalent set of struct definitions:
struct A {};
struct B {};
template <class Contents>
struct Message {
enum Type {
A,
B,
};
Type type;
Contents contents;
template <class Archive>
void serialize(Archive& archive) {
archive(type, contents);
}
};
And I'm using these structs to serialize and send a message like so:
bool SendA() {
Message<A> message{};
ostringstream stream;
message.type = Message::Type::A;
message.contents = new A{};
{
cereal::PortableBinaryOutputArchive archive(stream);
archive(message);
}
return SendData(stream.str());
}
This approach works until I try to deserialize a received Message
. I would like to be able to deserialize a Message
without first knowing if it contains an A
or a B
, but AFAIK this is not possible.
Is this indeed not possible using my current approach? And if so, is there another approach that will allow me to deserialize a similar set of structs without first knowing what it contains?
With Igor's comment, I was able to alter the Message
struct to be deserializable without first knowing what it contained:
struct A {};
struct B {};
struct Message {
enum Type {
A,
B,
};
Type type;
unique_ptr<::A> a = nullptr;
unique_ptr<::B> b = nullptr;
template <class Archive>
void serialize(Archive& archive) {
archive(type, contents);
}
};
The trick was to not make the Message
struct a template.
My first approach at writing the Message
struct was to included the templeted member variable contents
for storing each possible inner struct (an A
or a B
). This requires you to know what a serialized Message
structure contains before you can deserialize it, because you'd have to first create the specialized Message
variable to deserialize the data into.
Instead, you can create a unique_ptr
for each possible inner data struct. This allows you to create a Message
struct that isn't templated, and you don't have to create a specialized Message
variable to deserialize the data into.