123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- <!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 - Class Serialization Traits</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">Class Serialization Traits</h2>
- </td>
- </tr>
- </table>
- <hr>
- <dl class="page-index">
- <dt><a href="#version">Version</a>
- <dt><a href="#level">Implementation Level</a>
- <dt><a href="#tracking">Object Tracking</a>
- <dt><a href="#export">Export Key</a>
- <dt><a href="#abstract">Abstract</a>
- <dt><a href="#typeinfo">Type Information Implementation</a>
- <dt><a href="#wrappers">Wrappers</a>
- <dt><a href="#bitwise">Bitwise Serialization</a>
- <dt><a href="#templates">Template Serialization Traits</a>
- <dt><a href="#compiletime_messages">Compile Time Warnings and Errors</a>
- </dl>
- Serialization of data depends on the type of the data. For example, for
- primitive types such as <code style="white-space: normal">int</code>, it wouldn't make sense to save
- a version number in the archive. Likewise, for a data type that is never
- serialized through a pointer, it would (almost) never make sense to track
- the address of objects saved to/loaded from the archive as it will never
- be saved/loaded more than once in any case. Details of
- serialization for a particular data type will vary depending on the
- type, the way it is used and specifications of the programmer.
- <p>
- One can alter the manner in which a particular data type is serialized
- by specifying one or more <strong>class serialization traits</strong>.
- It is not generally necessary for the programmer to explictly assign
- traits to his classes as there are default values for all traits.
- If the default values are not appropriate they can be assigned by the programmer.
- A template is used to associate a typename with a constant. For example
- see <a href="../../../boost/serialization/version.hpp" target="version_hpp">
- version.hpp</a>.
- <h3><a name="version">Version</a></h3>
- This header file includes the following code:
- <pre><code>
- namespace boost {
- namespace serialization {
- template<class T>
- struct version
- {
- BOOST_STATIC_CONSTANT(unsigned int, value = 0);
- };
- } // namespace serialization
- } // namespace boost
- </code></pre>
- For any class <code style="white-space: normal">T</code>, The default definition
- of <code style="white-space: normal">boost::serialization::version<T>::value</code> is 0.
- If we want to assign a value of 2 as the version for class <code style="white-space: normal">my_class</code>
- we specialize the version template:
- <pre><code>
- namespace boost {
- namespace serialization {
- struct version<my_class>
- {
- BOOST_STATIC_CONSTANT(unsigned int, value = 2);
- };
- } // namespace serialization
- } // namespace boost
- </code></pre>
- Now whenever the version number for class <code style="white-space: normal">my_class</code> is required,
- the value 2 will be returned rather than the default value of 0.
- <p>
- To diminish typing and enhance readability, a macro is defined
- so that instead of the above, we could write:
- <pre><code>
- BOOST_CLASS_VERSION(my_class, 2)
- </code></pre>
- which expands to the code above.
- <h3><a name="level">Implementation Level</a></h3>
- In the same manner as the above, the "level" of implementation of serialization is
- specified. The header file <a href="../../../boost/serialization/level.hpp"
- target="level_hpp">level.hpp</a> defines the following.
- <pre><code>
- // names for each level
- enum level_type
- {
- // Don't serialize this type. An attempt to do so should
- // invoke a compile time assertion.
- not_serializable = 0,
- // write/read this type directly to the archive. In this case
- // serialization code won't be called. This is the default
- // case for fundamental types. It presumes a member function or
- // template in the archive class that can handle this type.
- // there is no runtime overhead associated reading/writing
- // instances of this level
- primitive_type = 1,
- // Serialize the objects of this type using the objects "serialize"
- // function or template. This permits values to be written/read
- // to/from archives but includes no class or version information.
- object_serializable = 2,
- ///////////////////////////////////////////////////////////////////
- // once an object is serialized at one of the above levels, the
- // corresponding archives cannot be read if the implementation level
- // for the archive object is changed.
- ///////////////////////////////////////////////////////////////////
- // Add class information to the archive. Class information includes
- // implementation level, class version and class name if available.
- object_class_info = 3,
- };
- </code></pre>
- Using a macro defined in <code style="white-space: normal">level.hpp</code> we can specify
- that <code style="white-space: normal">my_class</code> should be serialized along with its version number:
- <pre><code>
- BOOST_CLASS_IMPLEMENTATION(my_class, boost::serialization::object_class_info)
- </code></pre>
- If implementation level is not explicitly assigned, the system uses
- a default according to the following rules.
- <ul>
- <li>if the data type is <code style="white-space: normal">volatile</code>
- assign <code style="white-space: normal">not_serializable</code>
- <li>else if it's an enum or fundamental type assign <code style="white-space: normal">primitive_type</code>
- <li>else assign <code style="white-space: normal">object_class_info</code>
- </ul>
- That is, for most user defined types, objects will be serialized along with
- class version information. This will permit one to maintain backward
- compatibility with archives which contain previous versions. However, with this
- ability comes a small runtime cost. For types whose definition will "never"
- change, efficiency can be gained by specifying <code style="white-space: normal">object_serializable</code>
- to override the default setting of <code style="white-space: normal">object_class_info</code>.
- For example,
- this has been done for the
- <a href="../../../boost/serialization/binary_object.hpp" target="binary_object_hpp">
- binary_object wrapper</a>
- <h3><a name="tracking">Object Tracking</a></h3>
- Depending on the way a type is used, it may be necessary or convenient to
- track the address of objects saved and loaded. For example, this is generally
- necessary while serializing objects through a pointer in order to be sure
- that multiple identical objects are not created when an archive is loaded.
- This "tracking behavior" is controlled by the type trait defined in the header
- file <a href="../../../boost/serialization/tracking.hpp" target="tracking_hpp">tracking.hpp</a>
- which defines the following:
- <pre><code>
- // names for each tracking level
- enum tracking_type
- {
- // never track this type
- track_never = 0,
- // track objects of this type if the object is serialized through a
- // pointer.
- track_selectively = 1,
- // always track this type
- track_always = 2
- };
- </code></pre>
- A corresponding macro is defined so that we can use:
- <pre><code>
- BOOST_CLASS_TRACKING(my_class, boost::serialization::track_never)
- </code></pre>
- Default tracking traits are:
- <ul>
- <li>For primitive, <code style="white-space: normal">track_never</code>.
- <li>For pointers, <code style="white-space: normal">track_never</code>.
- That is, addresses of addresses are not tracked by default.
- <li>All current serialization wrappers such as <code style="white-space: normal">boost::serialization::nvp</code>,
- <code style="white-space: normal">track_never</code>.
- <li>For all other types, <code style="white-space: normal">track_selectively</code>.
- That is addresses of serialized objects are tracked if and only if
- one or more of the following is true:
- <ul>
- <li>an object of this type is anywhere in the program serialized
- through a pointer.
- <li>the class is explicitly "exported" - see below.
- <li>the class is explicitly "registered" in the archive
- </ul>
- </ul>
- <p>
- The default behavior is almost always the most convenient one. However,
- there a few cases where it would be desirable to override the
- default. One case is that of a virtual base class. In a diamond
- heritance structure with a virtual base class, object tracking
- will prevent redundant save/load invocations. So here is one
- case where it might be convenient to override the default tracking
- trait. <i>(Note: in a future version the default will be reimplemented
- to automatically track classes used as virtual bases).</i> This
- situation is demonstrated by
- <a href="../test/test_diamond.cpp" target="test_diamond_cpp">test_diamond.cpp</a>
- included with the library.
- <h3><a name="export">Export Key</a></h3>
- When serializing a derived class through a virtual base class pointer,
- two issues may arise.
- <ul>
- <li> The code in the derived class might never be explicitly
- referred to. Such code will never be instantiated.
- <p>
- This is addressed by invoking
- <code style="white-space: normal">BOOST_CLASS_EXPORT_IMPLEMENT(T)</code>
- in the file which defines (implements) the class T.
- This ensures that code for the derived class T will
- be explicity instantiated.
- <li> There needs to be some sort of identifier which can
- be used to select the code to be invoked when the object
- is loaded.
- Standard C++ does implement <code style="white-space: normal">typeid()</code> which can be
- used to return a unique string for the class. This is not entirely
- statisfactory for our purposes for the following reasons:
- <ul>
- <li>There is no guarantee that the string is the same across platforms.
- This would then fail to support portable archives.
- <li>In using code modules from various sources, classes may have
- to be wrapped in different namespaces in different programs.
- <li>There might be classes locally defined in different code modules
- that have the same name.
- <li>There might be classes with different names that we want to
- consider equivalent for purposes of serialization.
- </ul>
- <p>
- So in the serialization library, this is addressed by invoking
- <code style="white-space: normal">BOOST_CLASS_EXPORT_KEY2(my_class, "my_class_external_identifier")</code>
- in the header file which declares the class.
- In a large majority of applications, the class name works just fine
- for the external identifier string so the following short cut is
- defined -
- <code style="white-space: normal">BOOST_CLASS_EXPORT_KEY(my_class)</code>.
- </ul>
- For programs which consist of only one module - that is
- programs which do not use DLLS, one can specify
- <code style="white-space: normal">BOOST_CLASS_EXPORT(my_class)</code>
- or
- <code style="white-space: normal">BOOST_CLASS_EXPORT_GUID(my_class, "my_class_external_identifier")</code>
- in either the declaration header or definition. These macros
- expand to invocation of both of the macros described above.
- <i>(<b>GUID</b> stands for <b>G</b>lobally <b>U</b>nique <b>ID</b>entfier.)</i>
- <p>
- <i>(<a target="detail" href="special.html#export">Elsewhere</a>
- in this manual, the serialization of derived classes is addressed in detail.)</i>
- <p>
- The header file
- <a href="../../../boost/serialization/export.hpp" target="export_hpp">export.hpp</a>
- contains all macro definitions described here.
- The library will throw a runtime exception if
- <ul>
- <li> A type not explicitly referred to is not exported.
- <li> Serialization code for the same type is instantiated
- in more than one module (or DLL).
- </ul>
- <h3><a name="abstract">Abstract</a></h3>
- When serializing an object through a pointer to its base class,
- the library needs to determine whether or not the base is abstract
- (i.e. has at least one virtual function). The library uses the
- type trait macro <code style="white-space: normal">BOOST_IS_ABSTRACT(T)</code>
- to do this. Not all compilers support this type trait and corresponding
- macro. To address this, the macro <code style="white-space: normal">
- BOOST_SERIALIZATION_ASSUME_ABSTRACT(T)</code> has been
- implemented to permit one to explicitly indicate that a specified
- type is in fact abstract. This will guarentee that
- <code style="white-space: normal">BOOST_IS_ABSTRACT</code>
- will return the correct value for all compilers.
- <h3><a name="typeinfo">Type Information Implementation</a></h3>
- This last trait is also related to the serialization of objects
- through a base class pointer. The implementation of this facility
- requires the ability to determine at run time the true type of the
- object that a base class pointer points to. Different serialization
- systems do this in different ways. In our system, the default method
- is to use the function <code style="white-space: normal">typeid(...)</code> which is available
- in systems which support <b>RTTI</b> (<b>R</b>un <b>T</b>ime
- <b>T</b>ype <b>I</b>nformation).
- This will be satisfactory in almost all cases and most users of this
- library will lose nothing in skipping this section of the manual.
- <p>
- However, there are some cases where the default type determination
- system is not convenient. Some platforms might not support
- RTTI or it may have been disabled in order to speed execution
- or for some other reason. Some applications, E.G. runtime linking
- of plug-in modules, can't depend on C++ RTTI to determine the
- true derived class. RTTI only returns the correct type for polymorphic
- classes - classes with at least one virtual function. If any of these
- situations applies, one may substitute his own implementation of
- <code style="white-space: normal">extended_type_info</code>
- <p>
- The interface to facilities required to implement serialization is defined in
- <a href="../../../boost/serialization/extended_type_info.hpp"
- target="extended_type_info_hpp">extended_type_info.hpp</a>.
- Default implementation of these facilities based on <code style="white-space: normal">typeid(...)</code>
- is defined in
- <a href="../../../boost/serialization/extended_type_info_typeid.hpp"
- target="extended_type_info_typeid_hpp">extended_type_info_typeid.hpp</a>.
- An alternative implementation based on exported class identifiers
- is defined in
- <a href="../../../boost/serialization/extended_type_info_no_rtti.hpp"
- target="extended_type_info_rtti_hpp">extended_type_info_no_rtti.hpp</a>.
- <p>
- By invoking the macro:
- <pre><code>
- BOOST_CLASS_TYPE_INFO(
- my_class,
- extended_type_info_no_rtti<my_class>
- )
- </code></pre>
- we can assign the type information implementation to each class on a case by
- case basis. There is no requirement that all classes in a program use the same
- implementation of <code style="white-space: normal">extended_type_info</code>. This supports the concept
- that serialization of each class is specified "once and for all" in a header
- file that can be included in any project without change.
- <p>
- This is illustrated by the test program
- <a href="../test/test_no_rtti.cpp" target="test_no_rtti_cpp">test_no_rtti.cpp</a>.
- Other implementations are possible and might be necessary for
- certain special cases.
- <h3><a name="wrappers">Wrappers</a></h3>
- Archives need to treat wrappers differently from other types since, for example,
- they usually are non-const objects while output archives require that any
- serialized object (with the exception of a wrapper) be const.
- This header file <a href="../../../boost/serialization/wrapper.hpp">wrapper.hpp</a>
- includes the following code:
- <pre><code>
- namespace boost {
- namespace serialization {
- template<class T>
- struct is_wrapper
- : public mpl::false_
- {};
- } // namespace serialization
- } // namespace boost
- </code></pre>
- For any class <code style="white-space: normal">T</code>, The default definition
- of <code style="white-space: normal">boost::serialization::is_wrapper<T>::value</code> is thus false.
-
- If we want to declare that a class <code style="white-space: normal">my_class</code>
- is a wrapper we specialize the version template:
- <pre><code>
- namespace boost {
- namespace serialization {
- struct is_wrapper<my_class>
- : mpl::true_
- {};
- } // namespace serialization
- } // namespace boost
- </code></pre>
- <p>
- To diminish typing and enhance readability, a macro is defined
- so that instead of the above, we could write:
- <pre><code>
- BOOST_CLASS_IS_WRAPPER(my_class)
- </code></pre>
- which expands to the code above.
- <h3><a name="bitwise">Bitwise Serialization</a></h3>
- Some simple classes could be serialized just by directly copying all bits
- of the class. This is, in particular, the case for POD data types containing
- no pointer members, and which are neither versioned nor tracked. Some archives,
- such as non-portable binary archives can make us of this information to
- substantially speed up serialization.
- To indicate the possibility of bitwise serialization the type trait defined
- in the header
- file <a href="../../../boost/serialization/is_bitwise_serializable.hpp" target="is_bitwise_serializable">is_bitwise_serializable.hpp</a>
- is used:
- <pre><code>
- namespace boost { namespace serialization {
- template<class T>
- struct is_bitwise_serializable
- : public is_arithmetic<T>
- {};
- } }
- </code></pre>
- is used, and can be specialized for other classes. The specialization
- is made easy by the corresponding macro:
- <pre><code>
- BOOST_IS_BITWISE_SERIALIZABLE(my_class)
- </code></pre>
- <h3><a name="templates">Template Serialization Traits</a></h3>
- In some instances it might be convenient to assign serialization traits
- to a whole group of classes at once. Consider, the name-value pair
- wrapper
- <pre><code>
- template<class T>
- struct nvp : public std::pair<const char *, T *>
- {
- ...
- };
- </code></pre>
- used by XML archives to associate a name with a data variable of type T.
- These data types are never tracked and never versioned. So one might
- want to specify:
- <pre><code>
- BOOST_CLASS_IMPLEMENTATION(nvp<T>, boost::serialization::level_type::object_serializable)
- BOOST_CLASS_TRACKING(nvp<T>, boost::serialization::track_never)
- </code></pre>
- Examination of the definition of these macros reveals that they won't expand
- to sensible code when used with a template argument. So rather than using the
- convenience macros, use the original definitions
- <pre><code>
- template<class T>
- struct implementation_level<nvp<T> >
- {
- typedef mpl::integral_c_tag tag;
- typedef mpl::int_<object_serializable> type;
- BOOST_STATIC_CONSTANT(
- int,
- value = implementation_level::type::value
- );
- };
- // nvp objects are generally created on the stack and are never tracked
- template<class T>
- struct tracking_level<nvp<T> >
- {
- typedef mpl::integral_c_tag tag;
- typedef mpl::int_<track_never> type;
- BOOST_STATIC_CONSTANT(
- int,
- value = tracking_level::type::value
- );
- };
- </code></pre>
- to assign serialization traits to all classes generated by the template
- <code style="white-space: normal">nvp<T></code>
- <p>
- Note that it is only possible to use the above method to assign traits to
- templates when using compilers which correctly support Partial Template Specialization.
- One's first impulse might be to do something like:
- <pre><code>
- #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
- template<class T>
- struct implementation_level<nvp<T> >
- {
- ... // see above
- };
- // nvp objects are generally created on the stack and are never tracked
- template<class T>
- struct tracking_level<nvp<T> >
- {
- ... // see above
- };
- #endif
- </code></pre>
- This can be problematic when one wants to make his code <strong>and archives</strong>
- portable to other platforms. It means the objects will be serialized differently
- depending on the platform used. This implies that objects saved from one platform
- won't be loaded properly on another. In other words, archives won't be portable.
- <p>
- This problem is addressed by creating another method of assigning serialization traits
- to user classes. This is illustrated by the serialization for a
- <a target="nvp" href="../../../boost/serialization/nvp.hpp"><strong>name-value</strong> pair</a>.
- <p>
- Specifically, this entails deriving the template from a special class
- <a target="traits" href="../../../boost/serialization/traits.hpp">
- <code style="white-space: normal">boost::serialization::traits</code></a> which is specialized for a specific
- combination of serialization traits.
- When looking up the serialization traits, the library first checks to see if this class has been
- used as a base class. If so, the corresponding traits are used. Otherwise, the standard defaults
- are used. By deriving from a serialization traits class rather than relying upon Partial Template
- Specializaton, one can a apply serialization traits to a template and those traits will be
- the same across all known platforms.
- <p>
- The signature for the traits template is:
- <pre><code>
- template<
- class T,
- int Level,
- int Tracking,
- unsigned int Version = 0,
- class ETII = BOOST_SERIALIZATION_DEFAULT_TYPE_INFO(T),
- class IsWrapper = mpl::false_
- >
- struct traits
- </code></pre>
- and template parameters should be assigned according to the following table:
- <p>
- <table border>
- <tr><th align=left>parameter</th><th align=left>description</th><th align=left>permitted values</th><th align=left>default value</th></tr>
- <tr><td><code>T</code></td><td>target class</td><td>class name<T></td><td>none</td></tr>
- <tr><td><code>Level</code></td><td>implementation level</td><td><code>not_serializable<br>primitive_type<br>object_serializable<br>object_class_info</code></td><td>none</td></tr>
- <tr><td><code>Tracking</code></td><td>tracking level</td><td><code>track_never<br>track_selectivly<br>track_always</code></td><td>none</td></tr>
- <tr><td><code>Version</code></td><td><code>class version</td><td>unsigned integer</td><td><code>0</code></td></tr>
- <tr><td><code>ETTI</code></td><td><code>type_info</code> implementation</td><td><code>extended_type_info_typeid<br>extended_type_info_no_rtti</code></td><td>default <code>type_info implementation</code></td></tr>
- <tr><td><code>IsWrapper</code></td><td><code></code>is the type a wrapper?</td><td><code>mpl::false_<br>mpl::true_</code></td><td><code>mpl::false_</code></td></tr>
- </table>
- <h3><a name="compiletime_messages">Compile Time Warnings and Errors</a></h3>
- Some serialization traits can conflict with other ones. Sometimes these conflicts
- will result in erroneous behavior (E.G. creating of archives which could not be read)
- and other times they represent a probable misconception on the part of the
- library user which could result in suprising behavior. To the extent possible,
- these conflicts are detected at compile time and errors (BOOST_STATIC_ASSERT)
- or warnings (BOOST_STATIC_WARNING) are generated. They are generated in a
- compiler dependent manner which should show a chain of instantiation
- to the point where the error/warning is detected. Without this capability,
- it would be very hard to track down errors or unexpected behavior in library
- usage. Here is a list of the conflicts trapped:
- <dl>
- <dt><h2><a name="object_level">object_level</a> - error</h2></dt>
- <dd>
- This error traps attempts to serialize types whose
- implentation level is set to non_serializable.
- </dd>
- <dt><h2><a name="object_versioning">object_versioning</a> - error</h2></dt>
- <dd>
- It's possible that for efficiency reasons, a type can be
- assigned a serialization level which doesn't include type information
- in the archive. This would preclude the assignment
- of a new version number to the type. This error
- traps attempts to assign a version number in this case.
- This has to be a user error.
- </dd>
- <dt><h2><a name="object_tracking">object_tracking</a> - warning</h2></dt>
- <dd>
- The following code will display a message when compiled:
- <code style="white-space: normal"><pre>
- T t;
- ar << t;
- </pre></code>
- unless the tracking_level serialization trait is set to "track_never". The following
- will compile without problem:
- <code style="white-space: normal"><pre>
- const T t
- ar << t;
- </pre></code>
- Likewise, the following code will trap at compile time:
- <code style="white-space: normal"><pre>
- T * t;
- ar >> t;
- </pre></code>
- if the tracking_level serialization trait is set to "track_never".
- <p>
- The following case illustrates the function of this message.
- It was originally used as an example in the
- mailing list by Peter Dimov.
- <code style="white-space: normal"><pre>
- class construct_from
- {
- ...
- };
- void main(){
- ...
- Y y;
- construct_from x(y);
- ar << x;
- }
- </pre></code>
- Suppose that the above message is not displayed and the code is used as is.
- <ol>
- <li>this example compiles and executes fine. No tracking is done because
- construct_from has never been serialized through a pointer. Now some time
- later, the next programmer(2) comes along and makes an enhancement. He
- wants the archive to be sort of a log.
- <code style="white-space: normal"><pre>
- void main(){
- ...
- Y y;
- construct_from x(y);
- ar << x;
- ...
- x.f(); // change x in some way
- ...
- ar << x
- }
- </pre></code>
- <p>
- Again no problem. He gets two different of copies in the archive, each one is different.
- That is he gets exactly what he expects and is naturally delighted.
- <p>
- <li>Now sometime later, a third programmer(3) sees construct_from and says -
- oh cool, just what I need. He writes a function in a totally disjoint
- module. (The project is so big, he doesn't even realize the existence of
- the original usage) and writes something like:
- <code style="white-space: normal"><pre>
- class K {
- shared_ptr <construct_from> z;
- template <class Archive>
- void serialize(Archive & ar, const unsigned version){
- ar << z;
- }
- };
- </pre></code>
- <p>
- He builds and runs the program and tests his new functionality. It works
- great and he's delighted.
- <p>
- <li>Things continue smoothly as before. A month goes by and it's
- discovered that when loading the archives made in the last month (reading the
- log). Things don't work. The second log entry is always the same as the
- first. After a series of very long and increasingly acrimonius email exchanges,
- it's discovered
- that programmer(3) accidently broke programmer(2)'s code .This is because by
- serializing via a pointer, the "log" object is now being tracked. This is because
- the default tracking behavior is "track_selectively". This means that class
- instances are tracked only if they are serialized through pointers anywhere in
- the program. Now multiple saves from the same address result in only the first one
- being written to the archive. Subsequent saves only add the address - even though the
- data might have been changed. When it comes time to load the data, all instances of the log record show the same data.
- In this way, the behavior of a functioning piece of code is changed due the side
- effect of a change in an otherwise disjoint module.
- Worse yet, the data has been lost and cannot be recovered from the archives.
- People are really upset and disappointed with boost (at least the serialization system).
- <p>
- <li>
- After a lot of investigation, it's discovered what the source of the problem is
- and class construct_from is marked "track_never" by including:
- <code style="white-space: normal"><pre>
- BOOST_CLASS_TRACKING(construct_from, track_never)
- </pre></code>
- <li>Now everything works again. Or - so it seems.
- <p>
- <li><code style="white-space: normal">shared_ptr<construct_from></code>
- is not going to have a single raw pointer shared amongst the instances. Each loaded
- <code style="white-space: normal">shared_ptr<construct_from></code> is going to
- have its own distinct raw pointer. This will break
- <code style="white-space: normal">shared_ptr</code> and cause a memory leak. Again,
- The cause of this problem is very far removed from the point of discovery. It could
- well be that the problem is not even discovered until after the archives are loaded.
- Now we not only have a difficult to find and fix program bug, but we have a bunch of
- invalid archives and lost data.
- </ol>
- <p>Now consider what happens when the message is displayed:
- <ol>
- <p>
- <li>Right away, the program traps at
- <code style="white-space: normal"><pre>
- ar << x;
- </pre></code>
- <p>
- <li>The programmer curses (another %^&*&* hoop to jump through). He's in a
- hurry (and who isn't) and would prefer not to <code style="white-space: normal">const_cast</code>
- - because it looks bad. So he'll just make the following change an move on.
- <code style="white-space: normal"><pre>
- Y y;
- const construct_from x(y);
- ar << x;
- </pre></code>
- <p>
- Things work fine and he moves on.
- <p>
- <li>Now programer (2) wants to make his change - and again another
- annoying const issue;
- <code style="white-space: normal"><pre>
- Y y;
- const construct_from x(y);
- ...
- x.f(); // change x in some way ; compile error f() is not const
- ...
- ar << x
- </pre></code>
- <p>
- He's mildly annoyed now he tries the following:
- <ul>
- <li>He considers making f() a const - but presumably that shifts the const
- error to somewhere else. And he doesn't want to fiddle with "his" code to
- work around a quirk in the serializaition system
- <p>
- <li>He removes the <code style="white-space: normal">const</code>
- from <code style="white-space: normal">const construct_from</code> above - damn now he
- gets the trap. If he looks at the comment code where the
- <code style="white-space: normal">BOOST_STATIC_ASSERT</code>
- occurs, he'll do one of two things
- <ol>
- <p>
- <li>This is just crazy. Its making my life needlessly difficult and flagging
- code that is just fine. So I'll fix this with a <code style="white-space: normal">const_cast</code>
- and fire off a complaint to the list and mabe they will fix it.
- In this case, the story branches off to the previous scenario.
- <p>
- <li>Oh, this trap is suggesting that the default serialization isn't really
- what I want. Of course in this particular program it doesn't matter. But
- then the code in the trap can't really evaluate code in other modules (which
- might not even be written yet). OK, I'll add the following to my
- construct_from.hpp to solve the problem.
- <code style="white-space: normal"><pre>
- BOOST_CLASS_TRACKING(construct_from, track_never)
- </pre></code>
- </ol>
- </ul>
- <p>
- <li>Now programmer (3) comes along and make his change. The behavior of the
- original (and distant module) remains unchanged because the
- <code style="white-space: normal">construct_from</code> trait has been set to
- "track_never" so he should always get copies and the log should be what we expect.
- <p>
- <li>But now he gets another trap - trying to save an object of a
- class marked "track_never" through a pointer. So he goes back to
- construct_from.hpp and comments out the
- <code style="white-space: normal">BOOST_CLASS_TRACKING</code> that
- was inserted. Now the second trap is avoided, But damn - the first trap is
- popping up again. Eventually, after some code restructuring, the differing
- requirements of serializating <code style="white-space: normal">construct_from</code>
- are reconciled.
- </ol>
- Note that in this second scenario
- <ul>
- <li>all errors are trapped at compile time.
- <li>no invalid archives are created.
- <li>no data is lost.
- <li>no runtime errors occur.
- </ul>
- It's true that these messages may sometimes flag code that is currently correct and
- that this may be annoying to some programmers. However, this example illustrates
- my view that these messages are useful and that any such annoyance is a small price to
- pay to avoid particularly vexing programming errors.
- </dd>
- <dt><h2><a name="pointer_level">pointer_level</a> - warning</h2></dt>
- <dd>
- This trap addresses the following situaion when serializing
- a pointer:
- <ul>
- <li>A type doesn't save class information in the
- archive. That is, the serialization trait implementation
- level <= object_serializable.
- <li>Tracking for this type is set to "track selectively"
- in this case, indication that an object is tracked is
- not stored in the archive itself - see level == object_serializable.
- Since class information is not saved in the archive, the existence
- or absence of the operation ar << T * anywhere else in the
- program is used to infer that an object of this type should be tracked.
- <p>
- A problem arises when a program which reads an archive
- includes the operation ar >> T * so that tracking information
- will be included in the archive. When a program which
- creates the archive doesn't include ar << T it is presumed
- that the archive doesn't include tracking information and
- the archive will fail to load. Also the reverse situation could
- trigger a similar problem.
- <p>
- Though this situation is unlikely for several reasones,
- it is possible - hence this warning.
- </ul>
- So if your program traps here, consider changing the
- tracking or implementation level traits - or not
- serializing via a pointer.
- </dd>
- <dt><h2><a name="pointer_tracking">pointer_tracking</a> - warning</h2></dt>
- <dd>
- Serializing an object of a type marked "track_never" through a pointer
- could result in creating more objects than were saved! There are cases
- in which a user might really want to do this so we leave it as a warning.
- </dd>
- <dt><h2><a name="const_loading">const_loading</a> - error</h2></dt>
- <dd>
- One cannot load data into a "const" object unless it's a
- wrapper around some other non-const object.
- </dd>
- </dl>
- <hr>
- <p><i>© Copyright <a href="http://www.rrsd.com">Robert Ramey</a> 2002-2004 and Matthias Troyer 2006.
- 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>
|