Serializing classes

Support for serializing and deserializing objects is implemented by adding a serialize() template method to the class:

class hms {

public:

// ...

    template<typename iter_type>
    void serialize(iter_type &i)
    {
        i(h);
        i(m);
	i(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 a reference to one of two possible iterators. When serializing, serialize() receives an instance of a serializing iterator. When deserializing, serialize() receives an instance of a deserializing iterator. The unified serialize() template method iterates over the same class members, in the same order, using the iterator, automatically implementing identical serializing and deserializing logic.

Note

Although the serializing process does not modify the object, a single non-const serialize() template function gets invoked to serialize the object. However serializing a constant object works. This is because the template which implements serializing temporarily drops the const-ness of the object, during serializing, enabling the usage of the serialize() template function.

This is, unfortunately, a necessary evil. The benefits of defining a single physical block of code for serializing and deserializing outweighs the idealistic purity of implementing two template methods, one constant method that handles serializing, and a second non-constant method that handles deserializing.

Still, sometimes different logic is necessary when serializing and deserilizing. Here's the serialize() method from the x::tzfile class:

template<typename iter_type>
void serialize(iter_type &i)
{
    i(name, 255);

    if (!i.serializing)
    {
        std::string namecpy(name);

        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).