123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- <!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <!--
- (C) Copyright 2002-10 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; UTF-8">
- <link rel="stylesheet" type="text/css" href="../../../boost.css">
- <link rel="stylesheet" type="text/css" href="style.css">
- <title>Serialization - More on Archives</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">Archive Class Reference</h2>
- </td>
- </tr>
- </table>
- <hr>
- <dl class="page-index">
- <dt><a href="#trivial">Trivial Archive</a>
- <dt><a href="#implementation">More Useful Archive Classes</a>
- <dt><a href="#usage">Usage</a>
- <dt><a href="#testing">Testing</a>
- <dt><a href="#polymorphic">Polymorphic Archives</a>
- </dl>
- <h3><a name="trivial">Trivial Archive</a></h3>
- The <a href="archives.html"><strong>Archive</strong></a> concept specifies the functions that a
- class must implement in order to be used to serialize
- <a href="serialization.html"><strong>Serializable</strong></a> types.
- Our discussion will focus on archives used for saving as the hierarchy is exactly analogous
- for archives used for loading data.
- <h4>Minimum Requirments</h4>
- The simplest class which will model the <a href="archives.html"><strong>Archive</strong></a> concept specifies the functions that a
- class will look like:
- <pre><code>
- #include <cstddef> // std::size_t
- //////////////////////////////////////////////////////////////
- // class trivial_oarchive
- class trivial_oarchive {
- public:
- //////////////////////////////////////////////////////////
- // public interface used by programs that use the
- // serialization library
- typedef boost::mpl::bool_<true> is_saving;
- typedef boost::mpl::bool_<false> is_loading;
- template<class T> void register_type(){}
- template<class T> trivial_oarchive & operator<<(const T & t){
- return *this;
- }
- template<class T> trivial_oarchive & operator&(const T & t){
- return *this << t;
- }
- void save_binary(void *address, std::size_t count){};
- };
- </code></pre>
- The simplest possible input archive class is analogous to the above.
- In the following discussion, only output archives will be addressed.
- Input archives are exactly symmetrical to output archives.
- <p>
- This archive will compile and execute with any types which implement the
- <a href="serialization.html"><strong>Serializable</strong></a> concept.
- For an example see
- <a href="../example/demo_trivial_archive.cpp" target="demo_trivial_archive">
- <code style="white-space: normal">demo_trivial_archive.cpp</code></a>.
- Of course this program won't produce any output as it is. But it provides
- the starting point for a simple class which can be used to log formatted
- output. See the implementation of a <a href="simple_log.html">simple
- log archive</a> to how this has been done.
- <h3><a name="implementation">More Useful Archive Classes</a></h3>
- The above example is fine as far as it goes. But it doesn't implement
- useful features such as serialization of pointers, class versioning
- and others. This library implements a family of full featured archive
- classes appropriate for a variety of purposes.
- <p>
- Our archives have been factored into a tree of classes in order to minimize
- repetition of code. This is shown in the accompanying
- <a target="class_diagram" href="class_diagram.html">class diagram</a>.
- Any class which fulfills the following requirements will fit into
- this hierarchy and implement all the features we require. Deriving from
- the base class <a href="../../../boost/archive/detail/common_oarchive.hpp" target="common_oarchive_hpp">
- common_oarchive.hpp</a> provides all features we desire which
- are missing from trivial_oarchive above.
- <pre><code>
- <a href="../../../boost/archive/detail/common_oarchive.hpp" target="common_oarchive_hpp">
- #include <cstddef> // std::size_t
- #include <boost/archive/detail/common_oarchive.hpp>
- </a>
- /////////////////////////////////////////////////////////////////////////
- // class complete_oarchive
- class complete_oarchive :
- public boost::archive::detail::common_oarchive<complete_oarchive>
- {
- // permit serialization system privileged access to permit
- // implementation of inline templates for maximum speed.
- friend class boost::archive::save_access;
- // member template for saving primitive types.
- // Specialize for any types/templates that require special treatment
- template<class T>
- void save(T & t);
- public:
- //////////////////////////////////////////////////////////
- // public interface used by programs that use the
- // serialization library
- // archives are expected to support this function
- void save_binary(void *address, std::size_t count);
- };
- </code></pre>
- Given a suitable definitions of <code style="white-space: normal">save</code>
- and <code style="white-space: normal">save_binary</code>,
- any program using serialization with a conforming C++ compiler should compile
- and run with this archive class.
- <h4>Optional Overrides</h4>
- The <code style="white-space: normal">detail::common_oarchive</code> class contains
- a number of functions that are used by various parts of the serialization library
- to help render the archive in a particular form.
- <dl>
- <dt><h4><code>void save_start(char const *)</code></h4></dt>
- <dd>
- <strong>Default</strong>:Does nothing.<br>
- <strong>Purpose</strong>:To inject/retrieve an object name into the archive. Used
- by XML archive to inject "<name>" before data.
- </dd>
- <p>
- <dt><h4><code>void save_end(char const *)</code></h4></dt>
- <dd>
- <strong>Default</strong>:Does nothing.<br>
- <strong>Purpose</strong>:To inject/retrieve an object name into the archive. Used
- by XML archive to inject "</name>" after data.
- <dd>
- </dd>
- <p>
- <dt><h4><code>void end_preamble()</code></h4></dt>
- <dd>
- <strong>Default</strong>:Does nothing.<br>
- <strong>Purpose</strong>:Called <strong>each time</strong> user data is saved.
- It's not called when archive bookkeeping data is saved. This is used by XML archives
- to determine when to inject a ">" character at the end of an XML header. XML output archives
- keep their own internal flag indicating that data being written is header data. This
- internal flag is reset when an object start tag is written. When
- <code style="white-space: normal">void end_preamble()</code> is invoked and this internal flag is set
- a ">" character is appended to the output and the internal flag is reset. The default
- implementation for <code style="white-space: normal">void end_preamble()</code> is a no-op thereby permitting it
- to be optimised away for archive classes that don't use it.
- </dd>
- <p>
- <dt><h4><code>
- template<class T>
- void save_override(T & t, int);
- </code></h4></dt>
- <dd>
- <strong>Default</strong>:Invokes <code style="white-space: normal">archive::save(Archive & ar, t)</code><br>
- This is the main entry into the serialization library.<br>
- <strong>Purpose</strong>:This can be specialized in cases where the data is to be written
- to the archive in some special way. For example, XML archives implement special handling for
- name-value pairs by overriding this function template for name-value pairs.
- This replaces the default name-value pair handling, which is just to throw away the name,
- with one appropriate for XML which writes out the start of an XML tag with the correct object name.
- </dd>
- </dl>
- <h4>Types used by the serialization library</h4>
- The serialization library injects bookkeeping data into the serialization archive.
- This data includes things like object ids, version numbers, class names etc. Each
- of these objects is included in a wrapper so that the archive class can override the
- implementation of <code style="white-space: normal">void save_override(T & t, int);</code>.
- For example, in the XML archive, the override for this type renders an object_id equal to 23 as
- "object_id=_23". The following table lists the types defined in the
- <code style="white-space: normal">boost::archive namespace</code>
- used internally by the serialization library:
- <p>
- <table border>
- <tr><th align=left>type</th><th align=left><code style="white-space: normal">default<br>serialized as</code></th>
- <tr><td><code style="white-space: normal">version_type</code></td><td><code style="white-space: normal">unsigned int</code></td>
- <tr><td><code style="white-space: normal">object_id_type</code></td><td><code style="white-space: normal">unsigned int</code></td>
- <tr><td><code style="white-space: normal">object_id_reference_type</code></td><td><code style="white-space: normal">unsigned int</code></td>
- <tr><td><code style="white-space: normal">class_id_type</code></td><td><code style="white-space: normal">int</code></td>
- <tr><td><code style="white-space: normal">class_id_optional_type</code></td><td><code style="white-space: normal">nothing</code></td>
- <tr><td><code style="white-space: normal">class_id_reference_type</code></td><td><code style="white-space: normal">int</code></td>
- <tr><td><code style="white-space: normal">tracking_type</code></td><td><code style="white-space: normal">bool</code></td>
- <tr><td><code style="white-space: normal">classname_type</code></td><td><code style="white-space: normal">string</code></td>
- </table>
- <p>
- All of these are associated with a default serialization defined in terms of primitive types
- so it isn't a requirement to define <code style="white-space: normal">save_override</code>
- for these types.
- <p>
- These are defined in
- <a href="../../../boost/archive/basic_archive.hpp" target="basic_archive_hpp"><code style="white-space: normal">basic_archive.hpp</code></a>.
- All of these types have been assigned an
- <a target="detail" href="traits.html#level">implementation level</a> of
- <code style="white-space: normal">primitive</code> and are convertible to types such as int, unsigned int, etc.
- so that they have default implementations. This is illustrated by
- <a href="../../../boost/archive/basic_text_iarchive.hpp" target="basic_text_iarchive_hpp"><code style="white-space: normal">basic_text_iarchive.hpp</code></a>.
- which relies upon the default. However, in some cases, overrides will have to be
- explicitly provided for these types. For an example see
- <a href="../../../boost/archive/basic_xml_iarchive.hpp" target="basic_xml_iarchive_hpp"><code style="white-space: normal">basic_xml_iarchive.hpp</code></a>.
- <p>
- In real practice, we probably won't be quite done.
- One or more of the following issues may need to be addressed:
- <ul>
- <li>Even if we are using a conforming compiler, we might want our new archive class
- to be portable to non-conforming compilers.
- <li>Our archive format might require extra information inserted into it. For
- example, XML archives need <name ... >...</name> surrounding
- all data objects.
- <li>Addressing any of the above may generate more issues to be addressed.
- <li>The archives included with the library are all templates which use a
- <code style="white-space: normal">stream</code> or
- <code style="white-space: normal">streambuf</code>
- as a template parameter rather than simple classes.
- Combined with the above, even more issues arise with non-conforming compilers.
- </ul>
- The attached <a target="class_diagram" href="class_diagram.html">class diagram</a>
- shows the relationships between classes used to implement the serialization library.
- <p>
- A close examination of the archives included with the library illustrate
- what it takes to make a portable archive that covers all data types.
- <h3><a name="usage">Usage</a></h3>
- The newly created archive will usually be stored in its own header module. All
- that is necessary is to include the header and construct an instance of the new archive.
- EXCEPT for one special case.
- <ul>
- <li>Instances of a derived class are serialized through a base class pointer.
- <li>Such instances are not "registered" neither implicitly nor explicitly. That
- is, the macro <code style="white-space: normal">BOOT_CLASS_EXPORT</code> is used
- to instantiate the serialization code for the included archives.
- </ul>
- To make this work, the following should be included after the archive
- class definition.
- <pre><code>
- BOOST_SERIALIZATION_REGISTER_ARCHIVE(Archive)
- </code></pre>
- Failure to do this will not inhibit the program from compiling, linking
- and executing properly - except in one case. If an instance of a derived
- class is serialized through a pointer to its base class, the program
- will throw an
- <a href="exceptions.html#unregistered_class"><code style="white-space: normal">unregistered_class</code></a>
- exception.
- <p>
- <h4><a name="testing">Testing</h4>
- Exhaustive testing of the library requires testing the different aspects of object
- serialization with each archive. There are 46 different tests that can run with any archive.
- There are 5 "standard archives" included with the system.
- (3 in systems that don't support wide charactor i/o).
- <p>
- In addition, there are 28 other tests which aren't related to any particular archive class.
- <p>
- The default <code style="white-space: normal">bjam</code> testing setup will run all
- the above described tests. This will result in as many as 46 archive tests * 5
- standard archives + 28 general tests = 258 tests. Note that a complete test of the
- library would include DLL vs static library, release vs debug so the actual total
- would be closer to 1032 tests.
- <p>
- For each archive there is a header file in the test directory similar to the one below.
- The name of this archive is passed to the test program by setting the
- environmental variable <code style="white-space: normal">BOOST_ARCHIVE_TEST</code>
- to the name of the header. Here is the header file
- <code style="white-space: normal">test_archive.hpp</code> . Test header files for
- other archives are similar.
- <pre><code>
- // text_archive test header
- // include output archive header
- #include <boost/archive/text_oarchive.hpp>
- // set name of test output archive
- typedef boost::archive::text_oarchive test_oarchive;
- // set name of test output stream
- typedef std::ofstream test_ostream;
- // repeat the above for input archive
- #include <boost/archive/text_iarchive.hpp>
- typedef boost::archive::text_iarchive test_iarchive;
- typedef std::ifstream test_istream;
- // define open mode for streams
- // binary archives should use std::ios_base::binary
- #define TEST_STREAM_FLAGS (std::ios_base::openmode)0
- </code></pre>
- To test a new archive, for example, portable binary archives, with the gcc compiler,
- make a header file <code style="white-space: normal">portable_binary_archive.hpp</code>
- and invoke <code style="white-space: normal">bjam</code> with
- <pre><code>
- -sBOOST_ARCHIVE_LIST=portable_binary_archive.hpp
- </code></pre>
- This process in encapsulated in the shell or cmd script
- <code style="white-space: normal">library_test</code> whose command line is
- <pre><code>
- library_test --toolset=gcc -sBOOST_ARCHIVE_LIST=portable_binary_archive.hpp
- </code></pre>
- <h3><a name="polymorphic">Polymorphic Archives</a></h3>
- <h4>Motivation</h4>
- All archives described so far are implemented as templates. Code to save and load
- data to archives is regenerated for each combination of archive class and data type.
- Under these cirumstances, a good optimizing compiler that can expand
- <code>inline</code> functions to enough depth will generate fast code.
- However:
- <ul>
- <li>Much inline code may be replicated.
- <li>If there are several archive classes, code will be regenerated for each archive class.
- <li>If serialization code is placed in a library, that library must be rebuilt
- each time a new archive class is created.
- <li>If serialization code is placed in a DLL,
- <ul>
- <li>The DLL will contain versions of code for each known archive type.
- This would result in loading of DLLs which contain
- much code that is not used - basically defeating one of the main motivations
- for choosing to use a DLL in the first place.
- <li>If a new archive is created and an application shipped, all DLLs have to be
- rebuilt, and reshipped along with the application which uses the new archive. Thus
- the other main motivation for using a DLL is defeated.
- </ul>
- </ul>
- <h4>Implementation</h4>
- The solution is the pair <code>polymorphic_oarchive</code>
- and <code>polymorphic_iarchive</code>. They present a common interface of virtual
- functions - no templates - that is equivalent to the standard templated one.
- This is shown in the accompanying
- <a target="class_diagram" href="class_diagram.html">class diagram</a>
- <p>
- The accompanying demo program in files
- <a target=demo_polymorphic_cp href="../example/demo_polymorphic.cpp"><code style="white-space: normal">demo_polymorphic.cpp</code></a>,
- <a target=demo_polymorphic_A_hpp href="../example/demo_polymorphic_A.hpp"><code style="white-space: normal">demo_polymorphic_A.hpp</code></a>, and
- <a target=demo_polymorphic_A_cpp href="../example/demo_polymorphic_A.cpp"><code style="white-space: normal">demo_polymorphic_A</code></a>
- show how polymorphic archives are to be used. Note the following:
- <ul>
- <li><a target=demo_polymorphic_A_hpp href="../example/demo_polymorphic_A.hpp"><code style="white-space: normal">demo_polymorphic_A.hpp</code></a> and
- <a target=demo_polymorphic_A_cpp href="../example/demo_polymorphic_A.cpp"><code style="white-space: normal">demo_polymorphic_A.cpp</code></a>
- contain no templates and no reference to any specific archive implementation. That is, they will
- only have to be compiled once for all archive implementations. This even applies to archives classes
- created in the future.
- <li>The main program <a target=demo_polymorphic_cp href="../example/demo_polymorphic.cpp"><code style="white-space: normal">demo_polymorphic.cpp</code></a>
- specifies a specific archive implementation.
- </ul>
- As can be seen in the
- <a target="class_diagram" href="class_diagram.html">class diagram</a>
- and the header files, this implementation is just a composition of the polymorphic
- interface and the standard template driven implementation. This composition is
- accomplished by the templates
- <a target=polymorphic_iarchive_route_hpp href="../../../boost/archive/detail/polymorphic_iarchive_route.hpp"><code style="white-space: normal">polymorphic_iarchive_route.hpp</code></a>
- and
- <a target=polymorphic_oarchive_route_hpp href="../../../boost/archive/detail/polymorphic_oarchive_route.hpp"><code style="white-space: normal">polymorphic_oarchive_route.hpp</code></a>
- which redirect calls to the polymorphic archives to the specific archive.
- As these contain no code specific to the particular implementation archive, they can be used to create
- a polymorphic archive implementation from any functioning templated archive implementation.
- <p>
- As a convenience, small header files have been included which contain
- a <code style="white-space: normal">typedef</code> for a polymorphic implementation for each corresponding
- templated one. For example, the headers
- <a target=polymorphic_text_iarchive_hpp href="../../../boost/archive/polymorphic_text_iarchive.hpp"><code style="white-space: normal">polymorphic_text_iarchive.hpp</code></a>
- and
- <a target=polymorphic_text_oarchive_hpp href="../../../boost/archive/polymorphic_text_oarchive.hpp"><code style="white-space: normal">polymorphic_text_oarchive.hpp</code></a>.
- contain the <code style="white-space: normal">typedef</code> for the polymorphic implementation
- of the standard text archive classes
- <a target=text_iarchive_hpp href="../../../boost/archive/text_iarchive.hpp"><code style="white-space: normal">text_iarchive.hpp</code></a>
- and
- <a target=text_oarchive_hpp href="../../../boost/archive/text_oarchive.hpp"><code style="white-space: normal">text_oarchive.hpp</code></a>
- respectively. All included polymorphic archives use the same naming scheme.
- <h4>Usage</h4>
- Polymorphic archives address the issues raised above regarding templated implementation.
- That is, there is no replicated code, and no recompilation for new archives. This will
- result in smaller executables for program which use more than one type of archive, and
- smaller DLLS. There is a penalty for calling archive functions through a virtual function
- dispatch table and there is no possibility for a compiler to <code style="white-space: normal">inline</code>
- archive functions. This will result in a detectable degradation in performance for
- saving and loading archives.
- <p>
- Note that the concept of polymophic archives is fundamentally incompatible with the
- serialization of new types that are marked "primitive" by the user with:
- <pre><code>
- BOOST_CLASS_IMPLEMENTATION(my_primitive_type, boost::serialization::primitive_type)
- </code></pre>
- Code to implement serialization for these types is instantiated "on the fly" in the user's program.
- But this conflicts with the whole purpose of the polymorphic archive. An attempt to
- serialize such a primitive type will result in a compilation error since the common polymorhic
- interface is static and cannot instantiate code for a new type.
- <p>
- The main utility of polymorphic archives will be to permit the building of class DLLs that will
- include serialization code for all present and future archives with no redundant code.
- <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>
|