Files
serialization/doc/shared_ptr.html
Robert Ramey 9e6d12881d tabs and licence
[SVN r23760]
2004-07-19 07:00:01 +00:00

262 lines
9.8 KiB
HTML

<!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=iso-8859-1">
<link rel="stylesheet" type="text/css" href="../../../boost.css">
<link rel="stylesheet" type="text/css" href="style.css">
<title>Template serialization - shared_ptr</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="../../../c++boost.gif" border="0"></a></h3>
</td>
<td valign="top">
<h1 align="center">Serialization</h1>
<h2 align="center">Template serialization - <code style="white-space: normal">shared_ptr&lt;class T&gt;</code></h2>
</td>
</tr>
</table>
<hr>
All the code snippets included below are defined within the
<code style="white-space: normal">boost::serialization</code> namespace.
<p>
<code style="white-space: normal">shared_ptr&lt;T&gt;</code> is defined in
<a href="../../../boost/shared_ptr.hpp" target=shared_ptr.hpp>shared_ptr.hpp</a>.
<p>
The general class outline for a <code style="white-space: normal">shared_ptr&lt;T&gt;</code> is:
<dl>
<dt><code style="white-space: normal">shared_ptr&lt;T&gt;</code> contains:
<dl>
<dt><code style="white-space: normal">T *px;</code>
<dt><code style="white-space: normal">shared_count pn;</code> which contains a pointer to:
<dl>
<dt><code style="white-space: normal">sp_counted_base_impl&lt;T, ...&gt;</code> which is
derived from the polymorphic abstract class
<dl>
<dt><code style="white-space: normal">sp_counted_base</code>
</dl>
</dl>
</dl>
</dl>
The serialization process proceeds down the tree above.
<p>
The first cut at implementing serialization for <code style="white-space: normal">shared_ptr</code>
just serializes the relevant members of <code style="white-space: normal">shared_ptr</code>.
It's almost trivial:
<pre><code>
template&lt;class Archive, class T&gt;
inline void serialize(
Archive & ar,
shared_ptr&lt;T&gt; &t,
const unsigned int file_version,
int
){
ar & t.px; // save the raw pointer
ar & t.pn; // save the shared reference count
}
</code></pre>
So far so good. Now for the serialization of <code style="white-space: normal">shared_count</code>:
<pre><code>
template<class Archive>
inline void save(
Archive & ar,
const boost::detail::shared_count &t,
const unsigned int file_version
){
// the following will fail because pi_ is private variable
// of shared_count
// ar << t.pi_;
// so employ the accessor class
ar << shared_ptr_access::pi(t);
}
template<class Archive>
inline void load(
Archive & ar,
boost::detail::shared_count &t,
const unsigned int file_version
){
// see above
// ar >> t.pi_;
ar >> shared_ptr_access::pi(t);
}
</code></pre>
A key feature of this library is the ability to specify serialization
of a class or template without changing the class or template declaration
or definition. This is referred to as <i>non-intrusive</i> serialization.
<p>
In some cases, serialization of a class or template requires access
to one or more of its private members. In this cases, there will be
no alternative but to alter the class or template to permit access.
The simplest way to do to this is just to make the required members
public. For <code style="white-space: normal">shared_count</code> and related classes we added
the statement:
<pre><code>
// grant access to serialization code
friend class boost::serialization::shared_ptr_access;
</code></pre>
and defined an "accessor class":
<pre><code>
class shared_ptr_access {
public:
// functions that need to access the private variables of shared_cnt
...
};
</code></pre>
The serialization of <code style="white-space: normal">shared_count</code>uses this "accessor class"
to perform the normal serialization operations on the private member
variables of the <code style="white-space: normal">shared_count</code> class.
<p>
The <code style="white-space: normal">pi_</code>member of shared count is a pointer to an
instance of <code style="white-space: normal">sp_counted_base_impl<T, ...></code>. Since this class
doesn't have a default constructor, serialization requires
specification of the following overload:
<pre><code>
template<class Archive, class P, class D>
inline void save_construct_data(
Archive & ar,
const boost::detail::sp_counted_base_impl<P, D> * t,
const unsigned int file_version
){
// variables used for construction
// ar << t->ptr;
ar << shared_ptr_access::ptr(*t);
ar << *t;
}
template<class Archive, class P, class D>
inline void load_construct_data(
Archive & ar,
boost::detail::sp_counted_base_impl<P, D> * t,
const unsigned int file_version
){
P ptr_;
ar >> ptr_;
// placement new
::new(t)boost::detail::sp_counted_base_impl<P, D>(ptr_, D());
ar >> *t;
// compensate for that fact that a new shared count always is
// initialized with one. the add_ref below will increment it
// every time its serialized so without this adjustment
// the use counts will be off by one.
shared_ptr_access::use_count(*t) = 0;
}
</code></pre>
The statement <code style="white-space: normal">ar &gt;&gt; ptr_</code> is key. This deserializes
the same pointer deserialzed above. Default object tracking will ensure
that no more than one instance of the object is created and that the
pointer returned by multiple deserializations are all the same. Hence,
regardless of how many instances of <code style="white-space: normal">shared_ptr/shared_count</code>
corresponding to a particular object are created, the will all point
to the same object.
<p>
Since <code style="white-space: normal">sp_counted_base_impl&lt;P, D&gt;</code> is derived from
<code style="white-space: normal">sp_counted_base</code>, the following is needed:
<pre><code>
template&lt;class Archive, class P, class D&gt;
inline void serialize(
Archive & ar,
boost::detail::sp_counted_base_impl&lt;P, D&gt; &t,
const unsigned int file_version,
int
){
ar & boost::serialization::base_object&lt;
boost::detail::sp_counted_base
&gt;(*this);
}
</code></pre>
which will in turn require serialization of its base class:
<pre><code>
inline void serialize(
Archive & ar,
boost::detail::sp_counted &t,
const unsigned int file_version,
int
){
}
</code></pre>
It would seem we're done, running the test program,
<a href="../example/demo_shared_ptr.cpp" target="demo_shared_ptr_cpp">
demo_shared_ptr.cpp
</a>,
with this code produces the following output.
<pre><code>
a = 0x003017A0 use count = 2
a1 = 0x003017A0 use count = 2
unique element count = 1
a = 0x00000000 use count = 0
a1 = 0x00000000 use count = 0
unique element count = 0
a = 0x00303060 use count = 1
a1 = 0x00303060 use count = 1
unique element count = 1
</code></pre>
This indicates that we're not quite done. Due to default object
tracking, <code style="white-space: normal">sp_counted_base_impl&lt;P, D&gt;</code> is only
created once regardless of how many shared pointers point to the
same object. Of course, it has to be this way. The reference
count starts at 1 and is never incrememented. Code must be added
to the serialiation functions to maintain the proper reference
count.
<p>
The process of serialization of an empty base class -
<code style="white-space: normal">sp_counted_base</code> - seems like additional overhead.
Examination of code in
<a href="../../../boost/serialization/base_object.hpp" target="base_object_hpp">
base_object.hpp
</a>
reveals that <code style="white-space: normal">base_object.hpp</code> provides two functions:
<ul>
<li>invokes serialization of the base class data
<li>as a side effect, "registers" the fact base/derived relationship
so that conversions of pointers between base and derived classes can be
made at runtime.
</ul>
In this case we need only the latter function so we can replace the
base object serialization with:
<pre><code>
// register the relationship between each derived class
// its polymorphic base
void_cast_register&lt;
boost::detail::sp_counted_base,
boost::detail::sp_counted_base_impl&lt;P, D&gt;
&gt;();
</code></pre>
and we don't have to include a trival serializer for <code style="white-space: normal">sp_counted_base</code>
<p>
Finally we need to specify name-value pair wrappers if we want to be able
to use this serialization with XML archives.
<p>
Actually, even this is really just a start. Among the issues not addressed in
this implementation are:
<ul>
<li><code style="white-space: normal">weak_ptr</code> is not addressed. I haven't even looked into this.
<li>Other smart pointers that might interact with <code style="white-space: normal">shared_ptr</code>
haven't been addressed at all. To be confident that the implementation is
complete and correct, all these should be addressed as well.
<li>Exception handling hasn't been exhaustively considered.
<li>Other issues yet to be discovered.
</ul>
Clearly, complete, correct and exception safe serialization of smart pointers is going to
be a challenge. I hope that this implementation provides a useful
starting point for such an effort.
<hr>
Revised
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
15 September, 2003
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
</p>
<p><i>&copy; Copyright <a href="http://www.rrsd.com">Robert Ramey</a>
2002-2004. All Rights Reserved.</i></p>
</body>
</html>