Serialization - Derivation from an Existing Archive

###  | # Serialization ## Derivation from an Existing Archive

Portable Binary Archives
Fast Binary Archives

### Portable Binary Archives

It may happen that one wants to create a new archive class by derivation from one of the included ones. Included is a sample program that shows how to derive a new archive from one of the ones included with the library. The first example is demo_portable_archive.cpp. This binary archive save/loads integers in a portable format. To this end it is derived from the native binary archive and the save/load functions for integers are overridden with special versions which convert to big endian format if necessary. It also implements an exception for the case where an integer saved on one platform is too large for the platform which is loading the archive. This example doesn't address floating point types. This examples illustrates several issues that have to be addressed when doing something like this. The discussion below refers to the output archive only but it applies equally to input archives as well.

1. It is derived from `binary_oarchive_impl` NOT `binary_oarchive`
   As described in the comments in binary_oarchive.hpp. `binary_oarchive` really a shorthand name for `binary_oarchive_impl<binary_oarchive>`. So we should derive from `binary_oarchive_impl<portable_binary_oarchive>` rather than `binary_oarchive`.

       class portable_binary_oarchive :
           // don't derive from binary_oarchive !!!
           public binary_oarchive_impl<portable_binary_oarchive>
       {
       ...

2. Note the `portable_binary_oarchive` between the <> This is required so that base classes can downcast their `this` pointer to the most derived class. This is referred to as Curiously Recurring Template Pattern (CRTP) [11]. It is used to implement static polymorphism.
3. Base classes need to be explicitly given access to the derived class. This can be done by making members public or by including friend declarations for the base classes.

           typedef portable_binary_oarchive derived_t;
           friend class detail::common_oarchive<derived_t>;
           friend class basic_binary_oarchive<derived_t>;
           friend class basic_binary_oprimitive<derived_t, std::ostream>;
           friend class boost::serialization::save_access;

4. Base class functions will usually need to be explicitly invoked We commonly overload the function name `save` for saving primitives. This is very convenient. Usage of a function name in a derived class "hides" similarly named functions of the base class. That is, function name overloading doesn't automatically include base classes. To address this, we can use:

           using binary_oarchive_impl<derived_t>::save;
           void save(const unsigned int t);
           ...

   which should work on conforming compilers. However, I have found that the following equivalent works on more compilers.

           // default fall through for any types not specified here
           template<class T>
           void save(const T & t){
               binary_oarchive_impl<derived_t>::save(t);
           }
           void save(const unsigned int t);
           ...

   so it's what I use.
5. Template definitions of base classes may have to be included. The demo includes

       // explicitly instantiate for this type of binary stream
       #include <boost/archive/basic_binary_oprimitive.ipp>

   for just this purpose. Failure to include required template definitions will result in undefined symbol errors when the program is linked.
6. Without alteration, this class cannot be further derived from
   Base classes using CRTP must be templates with a parameter corresponding to the most derived class. As presented here, this class doesn't qualify, so it cannot be used as a base class. In order to derive further from this class, it would have to be reorganized along the lines of the original `binary_oarchive`. Specifically, it would look something like:

       template<class Archive>
       class portable_binary_oarchive_impl :
           // don't derive from binary_oarchive !!!
           public binary_oarchive_impl<Archive>
       {
           ...
       );

       // do not derived from this class !!!
       class portable_binary_oarchive :
           public portable_binary_oarchive_impl<portable_binary_oarchive>
       {
       public:
           portable_binary_oarchive(std::ostream & os, unsigned int flags = 0) :
               portable_binary_oarchive_impl<binary_oarchive>(os, flags)
           {}
       };

### Fast Binary Archives

The second example demo_fast_archive.cpp. is similar to the first one. The difference is that it intercepts the serialization before the default serialization is invoked. In this case we want to replace the default serialization of C arrays of integers with a faster one. The default version will invoke serialization of each element of the array. If its an array of integers, and we're not concerned with the archive being portable to another platform we can just save/load the whole array as a binary string of bytes. This should be faster than the default element by element method.

The same considerations that applied when overriding the the save/load of primitives above apply here, and the code is very similar.

© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)