C++ Boost

Serialization

Template serialization - shared_ptr<class T>


All the code snippets included below are defined within the 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 serialization process proceeds down the tree above.

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
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
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);
}
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.

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 shared_count and related classes we added the statement:


// grant access to serialization code
friend class boost::serialization::shared_ptr_access;
and defined an "accessor class":

class shared_ptr_access {
public:
    // functions that need to access the private variables of shared_cnt
    ...
};
The serialization of shared_countuses this "accessor class" to perform the normal serialization operations on the private member variables of the shared_count class.

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
inline void save_construct_data(
    Archive & ar,
    const boost::detail::sp_counted_base_impl * t, 
    const unsigned int file_version
){
    // variables used for construction
//  ar << t->ptr;
    ar << shared_ptr_access::ptr(*t);
    ar << *t;
}

template
inline void load_construct_data(
    Archive & ar,
    boost::detail::sp_counted_base_impl * t, 
    const unsigned int file_version
){
    P ptr_;
    ar >> ptr_;
    // placement new
    ::new(t)boost::detail::sp_counted_base_impl(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;
}
The statement ar >> ptr_ 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 shared_ptr/shared_count corresponding to a particular object are created, the 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, 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 incrememented. Code must be added to the serialiation 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:

In this case we need only the latter function so we can replace the base object serialization with:

// register the relationship between each derived class
// its polymorphic base
void_cast_register<
    boost::detail::sp_counted_base, 
    boost::detail::sp_counted_base_impl<P, D>
>();
and we don't have to include a trival 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:

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.
Revised 15 September, 2003

© Copyright Robert Ramey 2002-2004. All Rights Reserved.