|
SerializationTemplate serialization -
|
boost::serialization
namespace.
shared_ptr<T>
is defined in
shared_ptr.hpp.
The general class outline for a shared_ptr<T>
is:
shared_ptr<T>
contains:
T *px;
shared_count pn;
which contains a pointer to:
sp_counted_base_impl<T, ...>
which is
derived from the polymorphic abstract class
sp_counted_base
The first cut at implementing serialization for shared_ptr
just serializes the relevant members of shared_ptr
.
It's almost trivial:
template<class Archive, class T>
inline void serialize(
Archive & ar,
shared_ptr<T> & t,
const unsigned int file_version,
int
){
ar & t.px; // save the raw pointer
ar & t.pn; // save the shared reference count
}
So far so good. Now for the serialization of shared_count
:
template<class Archive>
inline void save(
Archive & ar,
const boost::detail::shared_count & t,
const unsigned int file_version
){
ar << t.pi_;
}
template<class Archive>
inline void load(
Archive & ar,
boost::detail::shared_count & t,
const unsigned int file_version
){
ar >> t.pi_;
}
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 non-intrusive serialization.
The pi_
member of shared count is a pointer to an
instance of sp_counted_base_impl
. Since this class
doesn't have a default constructor, serialization requires
specification of the following overload:
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 << *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;
}
The statement ar >> ptr_
is key. This deserializes
the same pointer deserialized 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 shared_ptr/shared_count
corresponding to a particular object are created, they will all point
to the same object.
Since sp_counted_base_impl<P, D>
is derived from
sp_counted_base
, the following is needed:
template<class Archive, class P, class D>
inline void serialize(
Archive & ar,
boost::detail::sp_counted_base_impl<P, D> & t,
const unsigned int file_version,
int
){
ar & boost::serialization::base_object<
boost::detail::sp_counted_base
>(*this);
}
which will in turn require serialization of its base class:
inline void serialize(
Archive & ar,
boost::detail::sp_counted & t,
const unsigned int file_version,
int
){
}
It would seem we're done, but running the test program,
demo_shared_ptr.cpp
,
with this code produces the following output.
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
This indicates that we're not quite done. Due to default object
tracking, sp_counted_base_impl<P, D>
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 incremented. Code must be added
to the serialization functions to maintain the proper reference
count.
The process of serialization of an empty base class -
sp_counted_base
- seems like additional overhead.
Examination of code in
base_object.hpp
reveals that base_object.hpp
provides two functions:
// register the relationship between each derived class
// and its polymorphic base
void_cast_register<
boost::detail::sp_counted_base_impl<P, D>
boost::detail::sp_counted_base,
>();
and we don't have to include a trivial serializer for sp_counted_base
.
Finally we need to specify name-value pair wrappers if we want to be able to use this serialization with XML archives.
Actually, even this is really just a start. Among the issues not addressed in this implementation are:
weak_ptr
is not addressed. I haven't even looked into this.
shared_ptr
haven't been addressed at all. To be confident that the implementation is
complete and correct, all these should be addressed as well.
BOOST_SHARED_POINTER_EXPORT(T)
BOOST_SHARED_POINTER_EXPORT_GUID(T, K)
These are specialized versions of the macros used for exporting classes serialized through raw pointers.
Clear, 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.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)