Let's say I have a class in C++, like the following:
struct Point {
int x, y, z;
};
I want to use Cereal to serialize that struct to JSON. So I added a serialize function like this:
struct Point {
int x, y, z;
template<class Archive>
void serialize(Archive& ar) {
ar(CEREAL_NVP(x),
CEREAL_NVP(y),
CEREAL_NVP(z));
}
};
This works fine when the Point is a member of another object or an element of an array. But if I want to make the Point be the primary object of an entire JSON file, it doesn't work properly. For example, with the following code:
Point p { 1, 2, 3 };
cereal::JSONOutputArchive ar(std::cout);
ar(p);
I get the following output:
{
"value0": {
"x": 1,
"y": 2,
"z": 3
}
}
I'd like to remove the "value0"
key and elevate the object to occupy the entire file, like this:
{
"x": 1,
"y": 2,
"z": 3
}
The only way I can seem to do that, is to basically re-implement the serialization function, manually adding the key names.
Point p {1, 2, 3};
cereal::JSONOutputArchive ar(std::cout);
ar(cereal::make_nvp("x", p.x),
cereal::make_nvp("y", p.y),
cereal::make_nvp("z", p.z));
Is there any way to do it utilizing the serialize function that I already implemented for the class?
Okay, figured it out. Pretty simple, just needed to call the serialize function directly from the object, passing the archive, instead of passing the object to the archive.
Point p {1, 2, 3};
cereal::JSONOutputArchive ar(std::cout);
p.serialize(ar);
Benjamin's answer is perfect solution if you know upfront that the class to be serialized has a serialize()
method. Since Cereal supports in-class/out-of-class serialize()
, split load()/save()
, explicit versioning; that's not always the case. Cereal's internal cereal::InputArchive
and cereal::OutputArchive
classes both have a bunch of SFINAE template methods to detect the right serialization method to use during compile time. The type-traits there can be used to roll our own template switch:
template< typename Class, typename Archive,
typename std::enable_if< cereal::traits::has_member_serialize<Class, Archive>::value>::type* = nullptr>
inline static void serializeHelper(Class& cl, Archive& ar)
{
cl.serialize(ar);
}
template< typename Class, typename Archive,
typename std::enable_if< cereal::traits::has_member_save<Class, Archive>::value>::type* = nullptr>
inline static void serializeHelper(Class& cl, Archive& ar)
{
cl.save(ar);
}
// More version could follow for remaining serialization types (external, versioned...)
template< typename Class, typename Archive,
typename std::enable_if< cereal::traits::has_member_serialize<Class, Archive>::value>::type* = nullptr>
inline static void deserializeHelper(Class& cl, Archive& ar)
{
cl.serialize(ar);
}
template< typename Class, typename Archive,
typename std::enable_if< cereal::traits::has_member_load<Class, Archive>::value>::type* = nullptr>
inline static void deserializeHelper(Class& cl, Archive& ar)
{
cl.load(ar);
}
// More version could follow for remaining deserialization types (external, versioned...)
Calling serializeHelper(p, ar);
will automatically select the serialization method provided by Point
, the same way as Cereal does internally.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With