Support for serializing and deserializing objects is implemented
by adding a serialize
() template method to the
class:
class hms { public: // ... template<typename ptr_type, typename iter_type> static void serialize(ptr_type ptr, iter_type &i) { i(ptr->h); i(ptr->m); i(ptr->s); } };
This is an abbreviated excerpt from the actual definition of the
x::hms
class
that holds a interval of time expressed as hours, minutes, and seconds.
Defining the serialize
() template function makes
this class serializable.
To support serialization of containers of the serializable class,
the class must also implement a default constructor and a copy
constructor (explicitly or implicitly).
The serialize
() template method receives
two parameters:
A mutable or a constant pointer to the object getting deserialized
or serialized. A mutable pointer gets passed in when deserialization
the object. A const
pointer gets passed on when
serializing the object.
The above serialize
()'s
ptr_type
is always either
hms *
or const hms *
.
When serializing, serialize
() receives
an instance of a
serializing
iterator
.
When deserializing, serialize
() receives an
instance of a
deserializing
iterator
.
The serialize
() template method typically
iterates
over the same class members, in the same order, using the iterator,
automatically implementing identical serializing and deserializing
logic.
Still,
sometimes different logic is necessary when serializing and
deserilizing. Here's the serialize
()
method from the
x::tzfile
class:
template<typename ptr_type, typename iter_type> static void serialize(ptr_type, iter_type &i) { i(name, 255); if constexpr (!i.serializing) { std::string namecpy=ptr->name; ptr->load_file(namecpy); } }
x::tzfile
contains detailed data structures
that implement the mapping between epoch time and local time.
This mapping is loaded from the system timezone database, and
consists of a collection of arrays of various objects, and other
boring data.
It's possible to simply serialize each one of the internal class
members. However, since a serialized object may only be
deserialized into a structurally compatible object, changing anything
about the internal x::tzfile
class members
ends up invalidating all the previously serialized objects.
An arbitrary timezone is defined solely by the timezone's name,
which is a single string. Therefore,
x::tzfile
is serialized
simply by serializing its name.
That means only the timezone's name gets deserialized
(enforcing the maximum timezone name size of 255 characters), so
the timezone definition must be loaded from the system timezone
database also. This is what happens in the given example.
“iterator
.serializing” is
a constant expression that evaluates
to true
if the parameter to
serialize
() is the serializing iterator, and
false
if it's the deserializing iterator.
First, the timezone mapping name gets either serialized or deserialized.
When serializing, the process is finished at this point.
When deserializing the object, load_file
() gets
invoked. This is an internal class method that loads the given
timezone mapping from the system database.
The tradeoff here is that a serializing timezone mapping can only be
truly deserialized if an identical
timezone mapping exists at the time and place
when deserializing occurs. This is a reasonable assumption.
Although serializing the internal structure of
x::tzfile
is possible, as previously mentioned,
the added imposition of prohibiting any future changes to the inner
structure of this class is weighed to be too high of a price to pay for
the ability to deserialize a timezone mapping in an environment that
does not have an identical mapping available.
This is a rare possibility, so the value added is very small.
(The reason for making a copy of the timezone mapping when deserializing
is because load_file
takes a reference to the
timezone mapping as a parameter, and assigns the referenced string
object to the “name” class member).