123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930 |
- <!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <!--
- (C) Copyright 2002-4 Robert Ramey - http://www.rrsd.com .
- Use, modification and distribution is subject to 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)
- -->
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <link rel="stylesheet" type="text/css" href="../../../boost.css">
- <link rel="stylesheet" type="text/css" href="style.css">
- <title>Serialization - Serialization of Classes</title>
- </head>
- <body link="#0000ff" vlink="#800080">
- <table border="0" cellpadding="7" cellspacing="0" width="100%" summary="header">
- <tr>
- <td valign="top" width="300">
- <h3><a href="../../../index.htm"><img height="86" width="277" alt="C++ Boost" src="../../../boost.png" border="0"></a></h3>
- </td>
- <td valign="top">
- <h1 align="center">Serialization</h1>
- <h2 align="center">Serializable Concept</h2>
- </td>
- </tr>
- </table>
- <hr>
- <dl class="page-index">
- <dt><a href="#primitiveoperators">Primitive Types</a>
- <dt><a href="#classoperators">Class Types</a>
- <dl class="page-index">
- <dt><a href="#member">Member Function</a>
- <dt><a href="#free">Free Function</a>
- <dl class="page-index">
- <dt><a href="#namespaces">Namespaces for Free Function Overrides</a>
- </dl>
- <dt><a href="#classmembers">Class Members</a>
- <dl class="page-index">
- <dt><a href="#base">Base Classes</a>
- <dt><a href="#const"><code style="white-space: normal">const</code> Members</a>
- <dt><a href="#templates">Templates</a>
- </dl>
- <dt><a href="#versioning">Versioning</a>
- <dt><a href="#splitting">Splitting <code style="white-space: normal">serialize</code> into
- <code style="white-space: normal">save/load</code></a>
- <dl class="page-index">
- <dt><a href="#splittingmemberfunctions">Member Functions</a>
- <dt><a href="#splittingfreefunctions">Free Functions</a>
- </dl>
- </dl>
- <dt><a href="#pointeroperators">Pointers</a>
- <dl class="page-index">
- <dt><a href="#constructors">Non-Default Constructors</a>
- <dt><a href="#derivedpointers">Pointers to Objects of Derived Classes</a>
- <dl class="page-index">
- <dt><a href="#registration">Registration</a>
- <dt><a href="#export">Export</a>
- <dt><a href="#instantiation">Instantiation</a>
- <dt><a href="#selectivetracking">Selective Tracking</a>
- <dt><a href="#runtimecasting">Runtime Casting</a>
- </dl>
- </dl>
- <dt><a href="#references">References</a>
- <dt><a href="#arrays">Arrays</a>
- <dt><a href="traits.html">Class Serialization Traits</a>
- <dt><a href="wrappers.html">Serialization Wrappers</a>
- <dt><a href="#models">Models - Serialization Implementations Included in the Library</a>
- </dl>
- A type <code style="white-space: normal">T</code> is <strong>Serializable</strong>
- if and only if one of the following is true:
- <ul>
- <li>it is a primitive type.<br>
- By <i>primitive type</i> we mean a C++ built-in type and <i>ONLY</i>
- a C++ built-in type. Arithmetic (including characters), bool, enum are primitive types.
- Below in <a target="detail" href="traits.html#Traits">serialization traits</a>,
- we define a "primitive" implementation level in a different way for a
- different purpose. This can be a source of confusion.
- <li>It is a class type and one of the following has been declared according
- to the prototypes detailed below:
- <ul>
- <li>a class member function <code style="white-space: normal">serialize</code>
- <li>a global function <code style="white-space: normal">serialize</code>
- </ul>
- <li>it is a pointer to a <strong>Serializable</strong> type.
- <li>it is a reference to a <strong>Serializable</strong> type.
- <li>it is a native C++ Array of <strong>Serializable</strong> type.
- </ul>
- <h2><a name="primitiveoperators">Primitive Types</a></h2>
- The template operators &, <<, and >> of the archive classes
- described above will generate code to save/load all primitive types
- to/from an archive. This code will usually just add the
- data to the archive according to the archive format.
- For example, a four byte integer is appended to a binary archive
- as 4 binary bytes while a to a text archive it would be
- rendered as a space followed by a string representation.
- <h2><a name="classoperators">Class Types</a></h2>
- For class/struct types, the template operators &, <<, and >>
- will generate code that invokes the programmer's serialization code for the
- particular data type. There is no default. An attempt to serialize a
- class/struct for which no serialization has been explicitly specified
- will result in a compile time error. The serialiation of a class can
- be specified via either a class member function or a free funcation which
- takes a reference to an instance of the class as an argument.
- <h3><a name="member">Member Function</a></h3>
- The serialization library invokes the following code to save or load a class instance
- to/from and archive.
- <pre><code>
- template<class Archive, class T>
- inline void serialize(
- Archive & ar,
- T & t,
- const unsigned int file_version
- ){
- // invoke member function for class T
- t.serialize(ar, file_version);
- }
- </code></pre>
- That is, the default definition of template <code style="white-space: normal">serialize</code>
- presumes the existence of a class member function template of the following
- signature:
- <pre><code>
- template<class Archive>
- void serialize(Archive &ar, const unsigned int version){
- ...
- }
- </code></pre>
- If such a member function is not declared, a compile time error will occur. In order
- that the member function generated by this template can be called to
- append the data to an archive, it either must be public or the class must
- be made accessible to the serialization library by including:
- <pre><code>
- friend class boost::serialization::access;
- </code></pre>
- in the class definition. This latter method should be preferred over the option
- of making the member function public. This will prevent serialization functions from
- being called from outside the library. This is almost certainly an error. Unfortunately,
- it may appear to function but fail in a way that is very difficult to find.
- <p>
- It may not be immediately obvious how this one template serves for both
- saving data to an archive as well as loading data from the archive.
- The key is that the <code style="white-space: normal">&</code> operator is
- defined as <code style="white-space: normal"><<</code>
- for output archives and as <code style="white-space: normal">>></code> input archives. The
- "polymorphic" behavior of the <code style="white-space: normal">&</code> permits the same template
- to be used for both save and load operations. This is very convenient in that it
- saves a lot of typing and guarantees that the saving and loading of class
- data members are always in sync. This is the key to the whole serialization
- system.
- <h3><a name="free">Free Function</a></h3>
- Of course we're not restricted to using the default implementation described
- above. We can override the default one with our own. Doing this will
- permit us to implement serialization of a class without altering
- the class definition itself. We call this <strong>non-intrusive</strong>
- serialization. Suppose our class is named <code style="white-space: normal">my_class</code>, the
- override would be specified as:
- <pre><code>
- // namespace selection
- template<class Archive>
- inline void serialize(
- Archive & ar,
- my_class & t,
- const unsigned int file_version
- ){
- ...
- }
- </code></pre>
- Note that we have called this override "non-intrusive". This is slightly
- inaccurate. It does not require that the class have special functions, that
- it be derived from some common base class or any other fundamental design changes.
- However, it will require access to the class members that are to
- be saved and loaded. If these members are <code style="white-space: normal">private</code>, it won't be
- possible to serialize them. So in some instances, minor modifications to the
- class to be serialized will be necessary even when using this "non-intrusive"
- method. In practice this may not be such a problem as many libraries
- (E.G. STL) expose enough information to permit implementation of non-intrusive
- serialization with absolutly no changes to the library.
- <h4><a name="namespaces">Namespaces for Free Function Overrides</a></h4>
- For maximum portability, include any free functions templates and definitions in the
- namespace <code style="white-space: normal">boost::serialization</code>. If portability is not a concern and the
- compiler being used supports ADL (Argument Dependent Lookup) the free functions and
- templates can be in any of the following namespaces:
- <ul>
- <li><code style="white-space: normal">boost::serialization</code>
- <li>namespace of the archive class
- <li>namespace of the type being serialized
- </ul>
- <p>
- Note that, at first glance, this suggestion may seem to be wrong for compilers which implement
- two phase lookup. In fact, the serialization library used a perhaps overly clever
- method to support this rule even for such compilers. Those with an interest in studying
- this further will find more information in
- <a target=serialization_hpp href="../../../boost/serialization/serialization.hpp">serialization.hpp</a>
- <h3><a name="classmembers">Serialization of Class Members</a></h3>
- Regardless of which of the above methods is used, the body of the serialize function must
- specify the data to be saved/loaded by sequential application of the archive
- <code style="white-space: normal">operator &</code> to all the data members of the class.
- <pre><code>
- {
- // save/load class member variables
- ar & member1;
- ar & member2;
- }
- </code></pre>
- <h4><a name="base">Base Classes</a></h4>
- The header file
- <a href="../../../boost/serialization/base_object.hpp" target="base_object_hpp">
- base_object.hpp
- </a>
- includes the template:
- <pre><code>
- template<class Base, class Derived>
- Base & base_object(Derived &d);
- </code></pre>
- which should be used to create a reference to an object of the base
- which can be used as an argument to the archive serialization operators.
- So for a class of <strong>Serializable</strong> type
- <code style="white-space: normal">T</code> the base class state should be
- serialized like this:
- <pre><code>
- {
- // invoke serialization of the base class
- ar & boost::serialization::base_object<base_class_of_T>(*this);
- // save/load class member variables
- ar & member1;
- ar & member2;
- }
- </code></pre>
- Resist the temptation to just cast <code style="white-space: normal">*this</code> to the base class.
- This might seem to work but may fail to invoke code necessary for
- proper serialization.
- <p>
- Note that this is <strong>NOT</strong> the same as calling the <code style="white-space: normal">serialize</code>
- function of the base class. This might seem to work but will circumvent
- certain code used for tracking of objects, and registering base-derived
- relationships and other bookkeeping that is required for the serialization
- system to function as designed. For this reason, all <code style="white-space: normal">serialize</code>
- member functions should be <code style="white-space: normal">private</code>.
- <h4><a name="const"><code style="white-space: normal">const</code> Members</a></h4>
- Saving <code style="white-space: normal">const</code> members to an archive
- requires no special considerations.
- Loading <code style="white-space: normal">const</code> members can be addressed by using a
- <code style="white-space: normal">const_cast</code>:
- <pre><code>
- ar & const_cast<T &>(t);
- </code></pre>
- Note that this violates the spirit and intention of the <code style="white-space: normal">const</code>
- keyword. <code style="white-space: normal">const</code> members are intialized when a class instance
- is constructed and not changed thereafter. However, this may
- be most appropriate in many cases. Ultimately, it comes down to
- the question about what <code style="white-space: normal">const</code> means in the context
- of serialization.
- <h4><a name="templates"></a>Templates</h4>
- Implementation of serialization for templates is exactly the same process
- as for normal classes and requires no additional considerations. Among
- other things, this implies that serialization of compositions of templates
- are automatically generated when required if serialization of the
- component templates is defined. For example, this library includes
- definition of serialization for <code style="white-space: normal">boost::shared_ptr<T></code> and for
- <code style="white-space: normal">std::list<T></code>. If I have defined serialization for my own
- class <code style="white-space: normal">my_t</code>, then serialization for
- <code style="white-space: normal">std::list< boost::shared_ptr< my_t> ></code> is already available
- for use.
- <p>
- For an example that shows how this idea might be implemented for your own
- class templates, see
- <a href="../example/demo_auto_ptr.cpp" target="demo_auto_ptr.cpp">
- demo_auto_ptr.cpp</a>.
- This shows how non-intrusive serialization
- for the template <code style="white-space: normal">auto_ptr</code> from the standard library
- can be implemented.
- <p>
- A somewhat trickier addition of serialization to a standard template
- can be found in the example
- <a href="../../../boost/serialization/shared_ptr.hpp" target="shared_ptr_hpp">
- shared_ptr.hpp
- </a>
- <!--
- Only the most minimal change to
- <code>shared_count.hpp</code>
- (to gain access to some private members) was necessary to achieve this.
- This should demonstrate how easy it is to non-intrusively
- implement serialization to any data type or template.
- -->
- <p>
- In the specification of serialization for templates, its common
- to split <code style="white-space: normal">serialize</code>
- into a <code style="white-space: normal">load/save</code> pair.
- Note that the convenience macro described
- <a href="#BOOST_SERIALIZATION_SPLIT_FREE">above</a>
- isn't helpful in these cases as the number and kind of
- template class arguments won't match those used when splitting
- <code style="white-space: normal">serialize</code> for a simple class. Use the override
- syntax instead.
- <h3><a name="versioning">Versioning</a></h3>
- It will eventually occur that class definitions change after archives have
- been created. When a class instance is saved, the current version
- in included in the class information stored in the archive. When the class instance
- is loaded from the archive, the original version number is passed as an
- argument to the loading function. This permits the load function to include
- logic to accommodate older definitions for the class and reconcile them
- with latest version. Save functions always save the current version. So this
- results in automatically converting older format archives to the newest versions.
- Version numbers are maintained independently for each class. This results in
- a simple system for permitting access to older files and conversion of same.
- The current version of the class is assigned as a
- <a href="traits.html">Class Serialization Trait</a> described later in this manual.
- <pre><code>
- {
- // invoke serialization of the base class
- ar & boost::serialization::base_object<base_class_of_T>(*this);
- // save/load class member variables
- ar & member1;
- ar & member2;
- // if its a recent version of the class
- if(1 < file_version)
- // save load recently added class members
- ar & member3;
- }
- </code></pre>
- <h3><a name="splitting">Splitting <code style="white-space: normal">serialize</code> into Save/Load</a></h3>
- There are times when it is inconvenient to use the same
- template for both save and load functions. For example, this might occur if versioning
- gets complex.
- <h4><a name="splittingmemberfunctions">Splitting Member Functions</a></h4>
- For member functions this can be addressed by including
- the header file <a href="../../../boost/serialization/split_member.hpp" target="split_member_hpp">
- boost/serialization/split_member.hpp</a> including code like this in the class:
- <pre><code>
- template<class Archive>
- void save(Archive & ar, const unsigned int version) const
- {
- // invoke serialization of the base class
- ar << boost::serialization::base_object<const base_class_of_T>(*this);
- ar << member1;
- ar << member2;
- ar << member3;
- }
- template<class Archive>
- void load(Archive & ar, const unsigned int version)
- {
- // invoke serialization of the base class
- ar >> boost::serialization::base_object<base_class_of_T>(*this);
- ar >> member1;
- ar >> member2;
- if(version > 0)
- ar >> member3;
- }
- template<class Archive>
- void serialize(
- Archive & ar,
- const unsigned int file_version
- ){
- boost::serialization::split_member(ar, *this, file_version);
- }
- </code></pre>
- This splits the serialization into two separate functions <code style="white-space: normal">save</code>
- and <code style="white-space: normal">load</code>. Since the new <code style="white-space: normal">serialize</code> template
- is always the same it can be generated by invoking the macro
- BOOST_SERIALIZATION_SPLIT_MEMBER() defined in the header file
- <a href="../../../boost/serialization/split_member.hpp" target="split_member_hpp">
- boost/serialization/split_member.hpp
- </a>.
- So the entire <code style="white-space: normal">serialize</code> function above can be replaced with:
- <pre><code>
- BOOST_SERIALIZATION_SPLIT_MEMBER()
- </code></pre>
- <h4><a name="splittingfreefunctions">Splitting Free Functions</a></h4>
- The situation is same for non-intrusive serialization with the free
- <code style="white-space: normal">serialize</code> function template.
- <a name="BOOST_SERIALIZATION_SPLIT_FREE">
- To use <code style="white-space: normal">save</code> and
- <code style="white-space: normal">load</code> function templates rather than
- <code style="white-space: normal">serialize</code>:
- <pre><code>
- namespace boost { namespace serialization {
- template<class Archive>
- void save(Archive & ar, const my_class & t, unsigned int version)
- {
- ...
- }
- template<class Archive>
- void load(Archive & ar, my_class & t, unsigned int version)
- {
- ...
- }
- }}
- </code></pre>
- include the header file
- <a href="../../../boost/serialization/split_free.hpp" target="split_free_hpp">
- boost/serialization/split_free.hpp
- </a>.
- and override the free <code style="white-space: normal">serialize</code> function template:
- <pre><code>
- namespace boost { namespace serialization {
- template<class Archive>
- inline void serialize(
- Archive & ar,
- my_class & t,
- const unsigned int file_version
- ){
- split_free(ar, t, file_version);
- }
- }}
- </code></pre>
- To shorten typing, the above template can be replaced with
- the macro:
- <pre><code>
- BOOST_SERIALIZATION_SPLIT_FREE(my_class)
- </code></pre>
- Note that although the functionality to split the <code style="white-space: normal">
- serialize</code> function into <code style="white-space: normal">save/load</code>
- has been provided, the usage of the <code style="white-space: normal">serialize</code>
- function with the corresponding <code style="white-space: normal">&</code> operator
- is preferred. The key to the serialization implementation is that objects are saved
- and loaded in exactly the same sequence. Using the <code style="white-space: normal">&</code>
- operator and <code style="white-space: normal">serialize</code>
- function guarantees that this is always the case and will minimize the
- occurrence of hard to find errors related to synchronization of
- <code style="white-space: normal">save</code> and <code style="white-space: normal">load</code>
- functions.
- <p>
- Also note that <code style="white-space: normal">BOOST_SERIALIZATION_SPLIT_FREE</code>
- must be used outside of any namespace.
- <h2><a name="pointeroperators">Pointers</a></h2>
- A pointer to any class instance can be serialized with any of the archive
- save/load operators.
- <p>
- To properly save and restore an object through a pointer the
- following situations must be addressed:
- <ol>
- <li>If the same object is saved multiple times through different
- pointers, only one copy of the object need be saved.
- <li>If an object is loaded multiple times through different pointers,
- only one new object should be created and all returned pointers
- should point to it.
- <li>The system must detect the case where an object is first
- saved through a pointer then the object itself is saved.
- Without taking extra precautions, loading would result in the
- creation of multiple copies of the original object. This system detects
- this case when saving and throws an exception - see below.
- <li>An object of a derived class may be stored through a
- pointer to the base class. The true type of the object must
- be determined and saved. Upon restoration the correct type
- must be created and its address correctly cast to the base
- class. That is, polymorphic pointers have to be considered.
- <li>NULL pointers must be dectected when saved and restored
- to NULL when deserialized.
- </ol>
- This serialization library addresses all of the above
- considerations. The process of saving and loading an object
- through a pointer is non-trivial. It can be summarized as
- follows:
- <p>Saving a pointer:
- <ol>
- <li>determine the true type of the object being pointed to.
- <li>write a special tag to the archive
- <li>if the object pointed to has not already been written
- to the archive, do so now
- </ol>
- Loading a pointer:
- <ol>
- <li>read a tag from the archive.
- <li>determine the type of object to be created
- <li>if the object has already been loaded, return its address.
- <li>otherwise, create a new instance of the object
- <li>read the data back in using the operators described above
- <li>return the address of the newly created object.
- </ol>
- Given that class instances are saved/loaded to/from the archive
- only once, regardless of how many times they are serialized with
- the <code style="white-space: normal"><<</code>
- and <code style="white-space: normal">>></code> operators
- <ul>
- <li>Loading the same pointer object multiple times
- results in only one object being created, thereby replicating
- the original pointer configuration.
- <li>Structures, such as collections of polymorphic pointers,
- are handled with no special effort on the part of users of this library.
- </ul>
- Serialization of pointers of derived types through a pointer to the
- base class may require a little extra "help". Also, the programmer
- may desire to modify the process described above for his own reasons.
- For example, it might be desired to suppress the tracking of objects
- as it is known a priori that the application in question can never
- create duplicate objects. Serialization of pointers can be "fine tuned"
- via the specification of <a target="detail" href="traits.html#Traits">Class Serialization Traits</a>
- as described in
- <a target="detail" href="special.html#derivedpointers">
- another section of this manual
- </a>
- <h3><a name="constructors">Non-Default Constructors</a></h3>
- Serialization of pointers is implemented in the library with code
- similar to the following:
- <pre><code>
- // load data required for construction and invoke constructor in place
- template<class Archive, class T>
- inline void load_construct_data(
- Archive & ar, T * t, const unsigned int file_version
- ){
- // default just uses the default constructor to initialize
- // previously allocated memory.
- ::new(t)T();
- }
- </code></pre>
- The default <code style="white-space: normal">load_construct_data</code> invokes the
- default constructor "in-place" to initialize the memory.
- <p>
- If there is no such default constructor, the function templates
- <code style="white-space: normal">load_construct_data</code> and
- perhaps <code style="white-space: normal">save_construct_data</code>
- will have to be overridden. Here is a simple example:
- <pre><code>
- class my_class {
- private:
- friend class boost::serialization::access;
- const int m_attribute; // some immutable aspect of the instance
- int m_state; // mutable state of this instance
- template<class Archive>
- void serialize(Archive &ar, const unsigned int file_version){
- ar & m_state;
- }
- public:
- // no default construct guarentees that no invalid object
- // ever exists
- my_class(int attribute) :
- m_attribute(attribute),
- m_state(0)
- {}
- };
- </code></pre>
- the overrides would be:
- <pre><code>
- namespace boost { namespace serialization {
- template<class Archive>
- inline void save_construct_data(
- Archive & ar, const my_class * t, const unsigned int file_version
- ){
- // save data required to construct instance
- ar << t->m_attribute;
- }
- template<class Archive>
- inline void load_construct_data(
- Archive & ar, my_class * t, const unsigned int file_version
- ){
- // retrieve data from archive required to construct new instance
- int attribute;
- ar >> attribute;
- // invoke inplace constructor to initialize instance of my_class
- ::new(t)my_class(attribute);
- }
- }} // namespace ...
- </code></pre>
- In addition to the deserialization of pointers, these overrides are used
- in the deserialization of STL containers whose element type has no default
- constructor.
- <h3><a name="derivedpointers">Pointers to Objects of Derived Classes</a></h3>
- <h4><a name="registration">Registration</a></h4>
- Consider the following:
- <pre><code>
- class base {
- ...
- };
- class derived_one : public base {
- ...
- };
- class derived_two : public base {
- ...
- };
- main(){
- ...
- base *b;
- ...
- ar & b;
- }
- </code></pre>
- When saving <code style="white-space: normal">b</code> what kind of object should be saved?
- When loading <code style="white-space: normal">b</code> what kind of object should be created?
- Should it be an object of class <code style="white-space: normal">derived_one</code>,
- <code style="white-space: normal">derived_two</code>, or maybe <code style="white-space: normal">base</code>?
- <p>
- It turns out that the kind of object serialized depends upon whether the base class
- (<code style="white-space: normal">base</code> in this case) is polymophic or not.
- If <code style="white-space: normal">base</code> is not polymorphic, that is if it has no
- virtual functions, then an object of the type <code style="white-space: normal">base</code>
- will be serialized. Information in any derived classes will be lost. If this is what is desired
- (it usually isn't) then no other effort is required.
- <p>
- If the base class is polymorphic, an object of the most derived type
- (<code style="white-space: normal">derived_one</code>
- or <code style="white-space: normal">derived_two</code>
- in this case) will be serialized. The question of which type of object is to be
- serialized is (almost) automatically handled by the library.
- <p>
- The system "registers" each class in an archive the first time an object of that
- class it is serialized and assigns a sequential number to it. Next time an
- object of that class is serialized in that same archive, this number is written
- in the archive. So every class is identified uniquely within the archive.
- When the archive is read back in, each new sequence number is re-associated with
- the class being read. Note that this implies that "registration" has to occur
- during both save and load so that the class-integer table built on load
- is identical to the class-integer table built on save. In fact, the key to
- whole serialization system is that things are always saved and loaded in
- the same sequence. This includes "registration".
- <p>
- Expanding our previous example:
- <pre><code>
- main(){
- derived_one d1;
- derived_two d2:
- ...
- ar & d1;
- ar & d2;
- // A side effect of serialization of objects d1 and d2 is that
- // the classes derived_one and derived_two become known to the archive.
- // So subsequent serialization of those classes by base pointer works
- // without any special considerations.
- base *b;
- ...
- ar & b;
- }
- </code></pre>
- When <code style="white-space: normal">b</code> is read it is
- preceded by a unique (to the archive) class identifier which
- has previously been related to class <code style="white-space: normal">derived_one</code> or
- <code style="white-space: normal">derived_two</code>.
- <p>
- If a derived class has NOT been automatically "registered" as described
- above, an <a target="detail" href="exceptions.html#unregistered_class">
- <code style="white-space: normal">unregistered_class</code></a> exception
- will be thrown when serialization is invoked.
- <p>
- This can be addressed by registering the derived class explicitly. All archives are
- derived from a base class which implements the following template:
- <pre><code>
- template<class T>
- register_type();
- </code></pre>
- So our problem could just as well be addressed by writing:
- <pre><code>
- main(){
- ...
- ar.template register_type<derived_one>();
- ar.template register_type<derived_two>();
- base *b;
- ...
- ar & b;
- }
- </code></pre>
- Note that if the serialization function is split between save and load, both
- functions must include the registration. This is required to keep the save
- and corresponding load in syncronization.
- <h4><a name="export">Export</a></h4>
- The above will work but may be inconvenient. We don't always know which derived
- classes we are going to serialize when we write the code to serialize through
- a base class pointer. Every time a new derived class is written we have to
- go back to all the places where the base class is serialized and update the
- code.
- <p>
- So we have another method:
- <pre><code>
- #include <boost/serialization/export.hpp>
- ...
- BOOST_CLASS_EXPORT_GUID(derived_one, "derived_one")
- BOOST_CLASS_EXPORT_GUID(derived_two, "derived_two")
- main(){
- ...
- base *b;
- ...
- ar & b;
- }
- </code></pre>
- The macro <code style="white-space: normal">BOOST_CLASS_EXPORT_GUID</code> associates a string literal
- with a class. In the above example we've used a string rendering
- of the class name. If a object of such an "exported" class is serialized
- through a pointer and is otherwise unregistered, the "export" string is
- included in the archive. When the archive
- is later read, the string literal is used to find the class which
- should be created by the serialization library.
- This permits each class to be in a separate header file along with its
- string identifier. There is no need to maintain a separate "pre-registration"
- of derived classes that might be serialized. This method of
- registration is referred to as "key export". More information on this
- topic is found in the section Class Traits -
- <a target="detail" href="traits.html#export">Export Key</a>.
- <p>
- <h4><a name="instantiation">Instantiation</a></h4>
- Registration by means of any of the above methods fulfill another role
- whose importance might not be obvious. This system relies on templated
- functions of the form <code style="white-space: normal">template<class Archive, class T></code>.
- This means that serialization code must be instantiated for each
- combination of archive and data type that is serialized in the program.
- <p>
- Polymorphic pointers of derived classes may never be referred to
- explictly by the program so normally code to serialize such classes
- would never be instantiated. So in addition to including export key
- strings in an archive, <code style="white-space: normal">BOOST_CLASS_EXPORT_GUID</code> explicitly
- instantiates the class serialization code for all archive classes used
- by the program.
- <h4><a name="selectivetracking">Selective Tracking</a></h4>
- Whether or not an object is tracked is determined by its
- <a target="detail" href="traits.html#tracking">object tracking trait</a>.
- The default setting for user defined types is <code style="white-space: normal">track_selectively</code>.
- That is, track objects if and only if they are serialized through pointers anywhere
- in the program. Any objects that are "registered" by any of the above means are presumed
- to be serialized through pointers somewhere in the program and therefore
- would be tracked. In certain situations this could lead to an inefficiency.
- Suppose we have a class module used by multiple programs. Because
- some programs serializes polymorphic pointers to objects of this class, we
- <a target="detail" href="traits.html#export">export</a> a class
- identifier by specifying <code style="white-space: normal">BOOST_CLASS_EXPORT</code> in the
- class header. When this module is included by another program,
- objects of this class will always be tracked even though it
- may not be necessary. This situation could be addressed by using
- <a target="detail" href="traits.html#tracking"><code style="white-space: normal">track_never</code></a>
- in those programs.
- <p>
- It could also occur that even though a program serializes through
- a pointer, we are more concerned with efficiency than avoiding the
- the possibility of creating duplicate objects. It could be
- that we happen to know that there will be no duplicates. It could
- also be that the creation of a few duplicates is benign and not
- worth avoiding given the runtime cost of tracking duplicates.
- Again, <a target="detail" href="traits.html#tracking"><code style="white-space: normal">track_never</code></a>
- can be used.
- <h4><a name="runtimecasting">Runtime Casting</a></h4>
- In order to properly translate between base and derived pointers
- at runtime, the system requires each base/derived pair be found
- in a table. A side effect of serializing a base object with
- <code style="white-space: normal">boost::serialization::base_object<Base>(Derived &)</code>
- is to ensure that the base/derived pair is added to the table
- before the <code style="white-space: normal">main</code> function is entered.
- This is very convenient and results in a clean syntax. The only
- problem is that it can occur where a derived class serialized
- through a pointer has no need to invoke the serialization of
- its base class. In such a case, there are two choices. The obvious
- one is to invoke the base class serialization with <code style="white-space: normal">base_object</code>
- and specify an empty function for the base class serialization.
- The alternative is to "register" the Base/Derived relationship
- explicitly by invoking the template
- <code style="white-space: normal">void_cast_register<Derived, Base>();</code>.
- Note that this usage of the term "register" is not related
- to its usage in the previous section. Here is an example of how this is done:
- <pre><code>
- #include <sstream>
- #include <boost/serialization/serialization.hpp>
- #include <boost/archive/text_iarchive.hpp>
- #include <boost/serialization/export.hpp>
- class base {
- friend class boost::serialization::access;
- //...
- // only required when using method 1 below
- // no real serialization required - specify a vestigial one
- template<class Archive>
- void serialize(Archive & ar, const unsigned int file_version){}
- };
- class derived : public base {
- friend class boost::serialization::access;
- template<class Archive>
- void serialize(Archive & ar, const unsigned int file_version){
- // method 1 : invoke base class serialization
- ar & boost::serialization::base_object<base>(*this);
- // method 2 : explicitly register base/derived relationship
- boost::serialization::void_cast_register<derived, base>(
- static_cast<derived *>(NULL),
- static_cast<base *>(NULL)
- )
- }
- };
- BOOST_CLASS_EXPORT_GUID(derived, "derived")
- main(){
- //...
- std::stringstream ss;
- boost::archive::text_iarchive ar(ss);
- base *b;
- ar >> b;
- }
- </code></pre>
- <p>
- In order for this template to be invoked in code compiled by non-conforming
- compilers, the following syntax may be used:
- <pre><code>
- boost::serialization::void_cast_register(
- static_cast<Derived *>(NULL),
- static_cast<Base *>(NULL)
- );
- </code></pre>
- For more information, see <a target="detail" href="implementation.html#tempatesyntax">Template Invocation syntax</a>
- <h3><a name="references"></a>References</h3>
- Classes that contain reference members will generally require
- non-default constructors as references can only be set when
- an instance is constructed. The example of the previous section
- is slightly more complex if the class has reference members.
- This raises the question of how and where the objects being
- referred to are stored and how are they created. Also there is the question about
- references to polymorphic base classes. Basically, these
- are the same questions that arise regarding pointers. This is
- no surprise as references are really a special kind of pointer.
- We address these questions by serializing references as though
- they were pointers.
- <pre><code>
- class object;
- class my_class {
- private:
- friend class boost::serialization::access;
- int member1;
- object & member2;
- template<class Archive>
- void serialize(Archive &ar, const unsigned int file_version);
- public:
- my_class(int m, object & o) :
- member1(m),
- member2(o)
- {}
- };
- </code></pre>
- the overrides would be:
- <pre><code>
- namespace boost { namespace serialization {
- template<class Archive>
- inline void save_construct_data(
- Archive & ar, const my_class * t, const unsigned int file_version
- ){
- // save data required to construct instance
- ar << t.member1;
- // serialize reference to object as a pointer
- ar << & t.member2;
- }
- template<class Archive>
- inline void load_construct_data(
- Archive & ar, my_class * t, const unsigned int file_version
- ){
- // retrieve data from archive required to construct new instance
- int m;
- ar >> m;
- // create and load data through pointer to object
- // tracking handles issues of duplicates.
- object * optr;
- ar >> optr;
- // invoke inplace constructor to initialize instance of my_class
- ::new(t)my_class(m, *optr);
- }
- }} // namespace ...
- </code></pre>
- <h3><a name="arrays"></a>Arrays</h3>
- If <code style="white-space: normal">T</code> is a serializable type,
- then any native C++ array of type T is a serializable type.
- That is, if <code style="white-space: normal">T</code>
- is a serializable type, then the following
- is automatically available and will function as expected:
- <pre><code>
- T t[4];
- ar << t;
- ...
- ar >> t;
- </code></pre>
- <h2><a href="traits.html">Class Serialization Traits</a></h2>
- <h2><a href="wrappers.html">Serialization Wrappers</a></h2>
- <h2><a name="models"></a>Models - Serialization Implementations Included in the Library</h2>
- The facilities described above are sufficient to implement
- serialization for all STL containers. In fact, this has been done
- and has been included in the library. For example, in order to use
- the included serialization code for <code style="white-space: normal">std::list</code>, use:
- <pre><code>
- #include <boost/serialization/list.hpp>
- </code></pre>
- rather than
- <pre><code>
- #include <list>
- </code></pre>
- Since the former includes the latter, this is all that is necessary.
- The same holds true for all STL collections as well as templates
- required to support them (e.g. <code style="white-space: normal">std::pair</code>).
- <p>
- As of this writing, the library contains serialization of the following boost classes:
- <ul>
- <li>optional
- <li>variant
- <li>scoped_ptr
- <li>shared_ptr
- <li>auto_ptr (demo)
- </ul>
- Others are being added to the list so check the boost files section and headers for
- new implementations!
- <hr>
- <p><i>© Copyright <a href="http://www.rrsd.com">Robert Ramey</a> 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)
- </i></p>
- </body>
- </html>
|