5.5 KiB
Serialization Functions
Since C++ lacks reflection, implementing serialization requires you to specify which data members should be serialized. cereal provides a number of ways to specify such serialization functions for your classes, even if you don't have access to their internals.
TLDR Version
cereal supports single serializaton functions (serialize) or split load/save pairs (load and save) either inside
or outside of classes. Internal serialization functions can be kept private so long as cereal is given access by
befriending cereal::access. cereal will tell you if you've made a mistake at compile time, if possible.
Types of Serialization Functions
Serialization functions can either be internal or external. Functionality can
either be in a single serialize function, or a split load and save
functions. When possible, it is preferred to use a single internal serialize
method, though split methods can be used when it is necessary (e.g. to
dynamically allocate memory upon loading a class). Unlike boost, there is no need to explicitly tell cereal that it
needs to use the split load-save pair; cereal will pick whichever is present and give a compile time error if it cannot
disambiguate a single serialization method.
{% capture class_begin %}struct MyClass
{
int x, y, z;{% endcapture %}
Internal serialize function
{% highlight cpp %} {{ class_begin }}
template void serialize(Archive & archive) { archive( x, y, z ); } };
{% endhighlight %}
Internal split/load functions
{% highlight cpp %} {{ class_begin }}
template void save(Archive & archive) const { archive( x, y, z ); }
template void load(Archive & archive) { archive( x, y, z ); } };{% endhighlight %}
External serialize function
{% highlight cpp %} {{ class_begin }} };
template void serialize(Archive & archive, MyClass & m) { archive( m.x, m.y, m.z ); }
{% endhighlight %}
External split/load functions
{% highlight cpp %} {{ class_begin }} };
template void save(Archive & archive, MyClass const & m) { archive( m.x, m.y, m.z ); }
template void load(Archive & archive) MyClass & m) { archive( m.x, m.y, m.z ); } {% endhighlight %}
Important! Save functions are const (or take a const reference to your class). cereal will throw a static assertion if it detects a non const save function.
External serialization functions should be placed either in the same namespace as the types they serialize or in the
cereal namespace so that the compiler can find them properly.
Non-public serialization
Serialization functions can be placed under access control to be protected or private. cereal will need access to them,
and can be given access by befriending cereal::access, defined in <cereal/access.hpp>:
#include <cereal/access.hpp>
class MyCoolClass
{
private:
int secretX;
friend class cereal::access;
template <class Archive>
void serialize( Archive & ar )
{
ar( secretX );
}
};
This also works with split save/load functions.
Inheritance
Serialization functions, like any other function, will be inherited by derived classes. Depending on the serialization method you have chosen to employ, this can cause some ambiguities that cereal is not happy with:
#include <iostream>
#include <cereal/archives/binary.hpp>
struct MyBase
{
template <class Archive>
void serialize( Archive & )
{ }
};
struct MyDerived : MyBase
{
template <class Archive>
void load( Archive & )
{ }
template <class Archive>
void save( Archive & ) const
{ }
};
int main()
{
MyDerived d;
cereal::BinaryOutputArchive ar( std::cout );
ar( d ); // static assertion failure: xCEREAL detected both a serialize and save/load pair for MyDerived
}
In the above example, MyDerived inherits the public serialize function from MyBase, thus giving it both a
serialize and load-save pair. cereal is unable to disambiguate which it should call and thus gives a compile time
static assertion.
Even if serialize was made private in the base class, cereal may still have access to it from
the derived class because of a friend declaration to cereal::access, resulting in the same error. The solution to this
error is to provide an explicit disambiguation for cereal:
#include <cereal/access.hpp>
namespace cereal
{
template <class Archive> struct specialize<Archive, MyDerived, cereal::specialization::member_load_save> {};
// xCEREAL no longer has any ambiguity when serializing MyDerived
}
More information can be found by reading the doxygen documentation for <cereal/access.hpp> [here]({{ site.baseurl }}/assets/doxygen/structcereal_1_1specialize.html).
Default construction
cereal requires access to a default constructor for types it serializes. If you don't want to provide a default constructor but do want to serialize smart pointers to it, you can get around this restriction using a special overload, detailed in the pointers section of the documentation.