Boost logo

Boost :

From: troy d. straszheim (troy_at_[hidden])
Date: 2005-03-06 09:54:34


Here's a use case that has been discussed before, but to which I
couldn't seem to find any solid resolution the list archives:

class A;
some_oarchive oa;

void mutate(shared_ptr<A>& aptr_)
{
    aptr_ = shared_ptr<A>(new A);
};

shared_ptr<A> aptr;
                         
for (int i=0; i<LARGE_NUMBER; i++)
{
    mutate(aptr);
    oa << aptr;
}

The problem is that your oarchive ends up with somewhere between two
and LARGE_NUMBER distinct A's in it. Usually two. Presumably the
"two" is because in this simple example only A's are getting allocated
and deallocated, and therefore there are often free A-sized blocks
conveniently laying around for reuse. aptr gets assigned A's at two
different alternating addresses.

If it was just a plain A, you could turn off tracking by not
serializing through a pointer. You'd then get the right number of A's
in your archive. But if elsewhere you want to serialize A through a
pointer you can't do that just for this call to operator<<(). Also
you don't have a good way to see if A actually is serialized via
pointer elsewhere or not. (My use case is one where lots of
inexperienced programmers are modifying the data structures contained
in A, and they will surely screw up if the rules are more complicated
than "use BOOST_CLASS_EXPORT in your header file, and never use bald
pointers, always use shared_ptr".)

I looked through the archives a bunch and didn't come across anything
conclusive. It seemed that some thought this kind of use case was
pathological, but I'm not sure why. Of course, I'm not all that
experienced with serialization.

What I'd like to be able to do is to tell the archive, "The previous
calls to operator<<() represent a 'snapshot' of the state of some
group of objects, and now I want you to forget about existent objects
because I am going to rearrange them all. Continue to track object
types, but forget about the addresses." I realize that this creates
the possiblity for memory leaks, but if the serialization is done
through one toplevel call to operator<< on a shared_ptr whose pointee
contains pointers to a whole universe of home-cooked pointer
spaghetti, I don't see a better way to do this, and I don't see how to
clearly express what I intend via the export and tracking macro
mechanisms. You can't close and reopen the archive in the top loop, you
get duplicate headers. The list archives mention the use case of
serializing the state of some memory pool that is very likely to get
reused: I think the little example above is probably the simplest case
of this.

So without asking for a sanity-check, I implemented
basic_oarchive::flush(), and some tests. The changes to basic_oarchive
and basic_oarchive_impl are very small. basic_oarchive has an internal
object_set, which tracks object_ids and addresses. I add a
num_flushed_objects member, and flush() clears out the set and adds the
number of objects flushed to this counter. New tracked objects are
assigned object_ids starting at num_flushed_objects + object_set.size().
  In this way the class_id's are reused post-flush, but object id's are
not. The interface is simply this:

class A;
some_oarchive oa;

void mutate(shared_ptr<A>& aptr_)
{
    aptr_ = shared_ptr<A>(new A);
};

shared_ptr<A> aptr;
                         
for (int i=0; i<LARGE_NUMBER; i++)
{
    mutate(aptr);
    oa << aptr;
    oa.flush();
}

No modifications are required to the iarchive, but the user code of
course has to be written in such a way that objects aren't leaked.

though "oa << archive::endl;" would be a cute way to flush, too.
Dunno.

Is this unnecessary because I'm missing something obvious? At any rate
the modified files and two tests are attached. All tests pass with this
change...

troy d. straszheim


/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// test_flush_simple.cpp
//
// copyright (c) 2005
// troy d. straszheim <troy_at_[hidden]>
// http://www.resophonic.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)

// should pass compilation and execution

// test verifies that basic_oarchive::flush() "resets" all pointers to objects

#include <iostream>
#include <fstream>

#include <cstdio> // remove
#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
    using ::remove;
}
#endif

#include <boost/archive/archive_exception.hpp>
#include "test_tools.hpp"
#include <boost/preprocessor/stringize.hpp>
#include BOOST_PP_STRINGIZE(BOOST_ARCHIVE_TEST)

#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>

struct A {
  unsigned i;
  template <class Archive>
  void serialize(Archive &ar, unsigned)
  {
    ar & BOOST_SERIALIZATION_NVP(i);
  }
  static std::size_t num_objects_;
public:
  virtual ~A(){ num_objects_--; }
  A() { num_objects_++; }
  static std::size_t num_objects() { return num_objects_; }
};

std::size_t A::num_objects_ = 0;

const unsigned NUMOBJS = 4;

void do_save(const char *testfile)
{
    test_ostream os(testfile, TEST_STREAM_FLAGS);
    test_oarchive oa(os);
    oa.flush(); // be sure you can flush an empty archive

    A a;
    for (unsigned int i=0; i<NUMOBJS; i++)
      {
        a.i = i;
        oa << BOOST_SERIALIZATION_NVP(a);
        oa.flush();
        BOOST_CHECK(A::num_objects() == 1);
      }
    // do some more flushes for the sake of thoroughness;
    oa.flush(); oa.flush(); oa.flush();

    A* ap = &a;
    for (unsigned int i=0; i<NUMOBJS; i++)
      {
        ap->i = i;
        oa << BOOST_SERIALIZATION_NVP(ap);
        oa.flush();
        BOOST_CHECK(A::num_objects() == 1);
      }
    oa.flush(); oa.flush(); oa.flush();
}

// save exported polymorphic class
void do_load(const char *testfile)
{
    test_istream is(testfile, TEST_STREAM_FLAGS);
    test_iarchive ia(is);
    {
      A a;
      for (unsigned int i=0; i<NUMOBJS; i++)
        {
          ia >> BOOST_SERIALIZATION_NVP(a);
          BOOST_CHECK(a.i == i);
          BOOST_CHECK(A::num_objects() == 1);
        }
    } // a goes out of scope: no objects
    BOOST_CHECK(A::num_objects() == 0);
    A* ap;
    for (unsigned int i=0; i<NUMOBJS; i++)
      {
        ia >> BOOST_SERIALIZATION_NVP(ap);
        std::cout << "numobjs=" << A::num_objects() << std::endl;
        BOOST_CHECK(ap->i == i);
        BOOST_CHECK(A::num_objects() == (i+1));
        // we leak 'em on purpose, we need to count em.
      }
}

int
test_main( int /* argc */, char* /* argv */[] )
{
    const char * testfile = boost::archive::tmpnam(NULL);
    BOOST_REQUIRE(NULL != testfile);

    do_save(testfile);
    do_load(testfile);

    std::remove(testfile);

    return EXIT_SUCCESS;
}

// EOF


/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// test_flush.cpp
//
// copyright (c) 2005
// troy d. straszheim <troy_at_[hidden]>
// http://www.resophonic.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)

// should pass compilation and execution

// test verifies that basic_oarchive::flush() "resets" all pointers to objects

#include <iostream>
#include <fstream>

#include <cstdio> // remove
#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
    using ::remove;
}
#endif

#include <boost/archive/archive_exception.hpp>
#include "test_tools.hpp"
#include <boost/preprocessor/stringize.hpp>
#include BOOST_PP_STRINGIZE(BOOST_ARCHIVE_TEST)

#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>

class polymorphic_base
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & /* ar */, const unsigned int /* file_version */){
    }
    static std::size_t num_objects_;
public:
    virtual ~polymorphic_base(){ num_objects_--; }
    polymorphic_base() { num_objects_++; }
    static std::size_t num_objects() { return num_objects_; }
};

std::size_t polymorphic_base::num_objects_ = 0;
//BOOST_IS_ABSTRACT(polymorphic_base)

class polymorphic_derived1 : public polymorphic_base
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive &ar, const unsigned int /* file_version */){
        ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(polymorphic_base);
    }
public:
    virtual ~polymorphic_derived1(){}
};

BOOST_CLASS_EXPORT(polymorphic_derived1)

class polymorphic_derived2 : public polymorphic_base
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive &ar, const unsigned int /* file_version */){
        ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(polymorphic_base);
    }
public:
    virtual ~polymorphic_derived2(){}
};

BOOST_CLASS_EXPORT(polymorphic_derived2)

// save exported polymorphic class
void save_exported(const char *testfile)
{
    test_ostream os(testfile, TEST_STREAM_FLAGS);
    test_oarchive oa(os);

    polymorphic_base *rb1 = new polymorphic_derived1;
    polymorphic_base *rb2a = new polymorphic_derived2;
    polymorphic_base *rb2b = new polymorphic_derived2;

    oa << BOOST_SERIALIZATION_NVP(rb1);
    oa << BOOST_SERIALIZATION_NVP(rb1);
    oa << BOOST_SERIALIZATION_NVP(rb2a);
    oa << BOOST_SERIALIZATION_NVP(rb2b);
    BOOST_CHECK(polymorphic_base::num_objects()==3);
    oa.flush();
    oa << BOOST_SERIALIZATION_NVP(rb2b);
    oa << BOOST_SERIALIZATION_NVP(rb2a);
    oa << BOOST_SERIALIZATION_NVP(rb1);
    oa << BOOST_SERIALIZATION_NVP(rb1);
    BOOST_CHECK(polymorphic_base::num_objects()==3);
    oa.flush();
    oa << BOOST_SERIALIZATION_NVP(rb1);
    oa << BOOST_SERIALIZATION_NVP(rb1);
    oa << BOOST_SERIALIZATION_NVP(rb2a);
    oa << BOOST_SERIALIZATION_NVP(rb2b);
    BOOST_CHECK(polymorphic_base::num_objects()==3);
    oa.flush();

    delete rb1;
    delete rb2a;
    delete rb2b;

    BOOST_CHECK(polymorphic_base::num_objects()==0);
}

#define RETRIEVE_AND_CHECK(ARCHIVE, PTR, TYPE, N) \
  ARCHIVE >> BOOST_SERIALIZATION_NVP(PTR); \
  BOOST_CHECK_MESSAGE( \
    boost::serialization::type_info_implementation<TYPE> \
     ::type::get_instance() \
     == boost::serialization::type_info_implementation<polymorphic_base> \
       ::type::get_derived_extended_type_info(*PTR), \
        "restored pointer " BOOST_PP_STRINGIZE(PTR) " not of correct type" ); \
    BOOST_CHECK(polymorphic_base::num_objects()==N);

// save exported polymorphic class
void load_exported(const char *testfile)
{
    test_istream is(testfile, TEST_STREAM_FLAGS);
    test_iarchive ia(is);

    polymorphic_base *rb1a = NULL, *rb1b = NULL;
    polymorphic_base *rb2a = NULL, *rb2b = NULL;

    RETRIEVE_AND_CHECK(ia, rb1a, polymorphic_derived1, 1);
    RETRIEVE_AND_CHECK(ia, rb1b, polymorphic_derived1, 1);
    RETRIEVE_AND_CHECK(ia, rb2a, polymorphic_derived2, 2);
    RETRIEVE_AND_CHECK(ia, rb2b, polymorphic_derived2, 3);

    RETRIEVE_AND_CHECK(ia, rb2b, polymorphic_derived2, 4);
    RETRIEVE_AND_CHECK(ia, rb2a, polymorphic_derived2, 5);
    RETRIEVE_AND_CHECK(ia, rb1a, polymorphic_derived1, 6);
    RETRIEVE_AND_CHECK(ia, rb1b, polymorphic_derived1, 6);

    RETRIEVE_AND_CHECK(ia, rb1a, polymorphic_derived1, 7);
    RETRIEVE_AND_CHECK(ia, rb1b, polymorphic_derived1, 7);
    RETRIEVE_AND_CHECK(ia, rb2a, polymorphic_derived2, 8);
    RETRIEVE_AND_CHECK(ia, rb2b, polymorphic_derived2, 9);
}

int
test_main( int /* argc */, char* /* argv */[] )
{
    const char * testfile = boost::archive::tmpnam(NULL);
    BOOST_REQUIRE(NULL != testfile);

    save_exported(testfile);
    load_exported(testfile);

    std::remove(testfile);

    return EXIT_SUCCESS;
}

// EOF


/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// basic_oarchive.cpp:

// (C) Copyright 2002 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)

// See http://www.boost.org for updates, documentation, and revision history.

#include <boost/config.hpp> // msvc 6.0 needs this for warning suppression

#include <cassert>
#include <set>

#include <boost/limits.hpp>
#include <boost/state_saver.hpp>
#include <boost/throw_exception.hpp>

// including this here to work around an ICC in intel 7.0
// normally this would be part of basic_oarchive.hpp below.
#define BOOST_ARCHIVE
#include <boost/archive/basic_archive.hpp>

#include <boost/archive/detail/basic_oserializer.hpp>
#include <boost/archive/detail/basic_pointer_oserializer.hpp>
#include <boost/archive/detail/basic_oarchive.hpp>
#include <boost/archive/archive_exception.hpp>

#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4251 4231 4660 4275)
#endif

using namespace boost::serialization;

namespace boost {
namespace serialization {
    class extended_type_info;
}
namespace archive {
namespace detail {

class basic_oserializer;
class basic_pointer_oserializer;

class basic_oarchive_impl
{
    friend class basic_oarchive;
    //////////////////////////////////////////////////////////////////////
    // information about each serialized object saved
    // keyed on address, class_id
    struct aobject
    {
        const void * address;
        class_id_type class_id;
        object_id_type object_id;

        bool operator<(const aobject &rhs) const
        {
            assert(NULL != address);
            assert(NULL != rhs.address);
            if( address < rhs.address )
                return true;
            if( address > rhs.address )
                return false;
            return class_id < rhs.class_id;
        }
        aobject & operator=(const aobject & rhs)
        {
            address = rhs.address;
            class_id = rhs.class_id;
            object_id = rhs.object_id;
            return *this;
        }
        aobject(
            const void *a,
            class_id_type class_id_,
            object_id_type object_id_
        ) :
            address(a),
            class_id(class_id_),
            object_id(object_id_)
        {}
        aobject() : address(NULL){}
    };
    // keyed on class_id, address
    typedef std::set<aobject> object_set_type;
    object_set_type object_set;
    std::size_t num_flushed_objects;
    //////////////////////////////////////////////////////////////////////
    // information about each serialized class saved
    // keyed on type_info
    struct cobject_type
    {
        const basic_oserializer * bos_ptr;
        const class_id_type class_id;
        bool initialized;
        cobject_type(
            std::size_t class_id_,
            const basic_oserializer & bos_
        ) :
            bos_ptr(& bos_),
            class_id(class_id_),
            initialized(false)
        {}
        cobject_type(const basic_oserializer & bos_)
            : bos_ptr(& bos_)
        {}
        cobject_type(
            const cobject_type & rhs
        ) :
            bos_ptr(rhs.bos_ptr),
            class_id(rhs.class_id),
            initialized(rhs.initialized)
        {}
        // the following cannot be defined because of the const
        // member. This will generate a link error if an attempt
        // is made to assign. This should never be necessary
        // use this only for lookup argument
        cobject_type & operator=(const cobject_type &rhs);
        bool operator<(const cobject_type &rhs) const {
            return *bos_ptr < *(rhs.bos_ptr);
        }
    };
    // keyed on type_info
    typedef std::set<cobject_type> cobject_info_set_type;
    cobject_info_set_type cobject_info_set;

    // list of objects initially stored as pointers - used to detect errors
    // keyed on object id
    std::set<object_id_type> stored_pointers;

    // address of the most recent object serialized as a poiner
    // whose data itself is now pending serialization
    const void * pending_object;
    const basic_oserializer * pending_bos;
    basic_oarchive_impl() :
        num_flushed_objects(0),
        pending_object(NULL),
        pending_bos(NULL)
    {}

    const cobject_type &
    find(const basic_oserializer & bos);
    const basic_oserializer *
    find(const serialization::extended_type_info &ti) const;
public:
    const cobject_type &
    register_type(const basic_oserializer & bos);
    void save_object(
        basic_oarchive & ar,
        const void *t,
        const basic_oserializer & bos
    );
    void save_pointer(
        basic_oarchive & ar,
        const void * t,
        const basic_pointer_oserializer * bpos
    );
    void flush();
};

//////////////////////////////////////////////////////////////////////
// implementation of basic_oarchive implementation functions

// given a type_info - find its bos
// return NULL if not found
inline const basic_oserializer *
basic_oarchive_impl::find(const serialization::extended_type_info & ti) const {
    class bosarg : public basic_oserializer
    {
       bool class_info() const {
            assert(false);
            return false;
        }
        // returns true if objects should be tracked
        bool tracking() const {
            assert(false);
            return false;
        }
        // returns class version
        unsigned int version() const {
            assert(false);
            return 0;
        }
        // returns true if this class is polymorphic
        bool is_polymorphic() const{
            assert(false);
            return false;
        }
        void save_object_data(
            basic_oarchive & ar, const void * x
        ) const {
            assert(false);
        }
    public:
        bosarg(const serialization::extended_type_info & type_) :
          boost::archive::detail::basic_oserializer(type_)
        {}
    };
    bosarg bos(ti);
    cobject_info_set_type::const_iterator cit
        = cobject_info_set.find(cobject_type(bos));
    // it should already have been "registered" - see below
    if(cit == cobject_info_set.end()){
        // if an entry is not found in the table it is because a pointer
        // of a derived class has been serialized through its base class
        // but the derived class hasn't been "registered"
        return NULL;
    }
    // return pointer to the real class
    return cit->bos_ptr;
}

inline const basic_oarchive_impl::cobject_type &
basic_oarchive_impl::find(const basic_oserializer & bos)
{
    std::pair<cobject_info_set_type::iterator, bool> cresult =
        cobject_info_set.insert(cobject_type(cobject_info_set.size(), bos));
    return *(cresult.first);
}

inline const basic_oarchive_impl::cobject_type &
basic_oarchive_impl::register_type(
    const basic_oserializer & bos
){
    cobject_type co(cobject_info_set.size(), bos);
    std::pair<cobject_info_set_type::const_iterator, bool>
        result = cobject_info_set.insert(co);
    return *(result.first);
}

inline void
basic_oarchive_impl::save_object(
    basic_oarchive & ar,
    const void *t,
    const basic_oserializer & bos
){
    // if its been serialized through a pointer and the preamble's been done
    if(t == pending_object && pending_bos == & bos){
        // just save the object data
        ar.end_preamble();
        (bos.save_object_data)(ar, t);
        return;
    }

    // get class information for this object
    const cobject_type & co = register_type(bos);
    if(bos.class_info()){
        if( ! co.initialized){
            ar.vsave(class_id_optional_type(co.class_id));
            ar.vsave(tracking_type(bos.tracking()));
            ar.vsave(version_type(bos.version()));
            (const_cast<cobject_type &>(co)).initialized = true;
        }
    }

    // we're not tracking this type of object
    if(! bos.tracking()){
        // just windup the preamble - no object id to write
        ar.end_preamble();
        // and save the data
        (bos.save_object_data)(ar, t);
        return;
    }

    // look for an existing object id
    object_id_type oid(object_set.size() + num_flushed_objects);
    // lookup to see if this object has already been written to the archive
    basic_oarchive_impl::aobject ao(t, co.class_id, oid);
    std::pair<basic_oarchive_impl::object_set_type::const_iterator, bool>
        aresult = object_set.insert(ao);
    oid = aresult.first->object_id;

    // if its a new object
    if(aresult.second){
        // write out the object id
        ar.vsave(oid);
        ar.end_preamble();
        // and data
        (bos.save_object_data)(ar, t);
        return;
    }

    // check that it wasn't originally stored through a pointer
    if(stored_pointers.end() != stored_pointers.find(oid)){
        // this has to be a user error. loading such an archive
        // would create duplicate objects
        boost::throw_exception(
            archive_exception(archive_exception::pointer_conflict)
        );
    }
    // just save the object id
    ar.vsave(object_reference_type(oid));
    ar.end_preamble();
    return;
}

// save a pointer to an object instance
inline void
basic_oarchive_impl::save_pointer(
    basic_oarchive & ar,
    const void * t,
    const basic_pointer_oserializer * bpos_ptr
){
    const basic_oserializer & bos = bpos_ptr->get_basic_serializer();
    std::size_t original_count = cobject_info_set.size();
    const cobject_type & co = register_type(bos);
    if(! co.initialized){
        ar.vsave(co.class_id);
        // if its a previously unregistered class
        if((cobject_info_set.size() > original_count)){
            if(bos.is_polymorphic()){
                const serialization::extended_type_info *eti = & bos.type;
                const char * key = NULL;
                if(NULL != eti)
                    key = eti->key;
                if(NULL != key){
                    // the following is required by IBM C++ compiler which
                    // makes a copy when passing a non-const to a const. This
                    // is permitted by the standard but rarely seen in practice
                    const class_name_type cn(key);
                    // write out the external class identifier
                    ar.vsave(cn);
                }
                else
                    // without an external class name
                    // we won't be able to de-serialize it so bail now
                    boost::throw_exception(
                        archive_exception(archive_exception::unregistered_class)
                    );
            }
        }
        if(bos.class_info()){
            ar.vsave(tracking_type(bos.tracking()));
            ar.vsave(version_type(bos.version()));
        }
        (const_cast<cobject_type &>(co)).initialized = true;
    }
    else{
        ar.vsave(class_id_reference_type(co.class_id));
    }

    // if we're not tracking
    if(! bos.tracking()){
        // just save the data itself
        ar.end_preamble();
        state_saver<const void *> x(pending_object);
        state_saver<const basic_oserializer *> y(pending_bos);
        pending_object = t;
        pending_bos = & bpos_ptr->get_basic_serializer();
        bpos_ptr->save_object_ptr(ar, t);
        return;
    }

    object_id_type oid(object_set.size() + num_flushed_objects);
    // lookup to see if this object has already been written to the archive
    basic_oarchive_impl::aobject ao(t, co.class_id, oid);
    std::pair<basic_oarchive_impl::object_set_type::const_iterator, bool>
        aresult = object_set.insert(ao);
    oid = aresult.first->object_id;
    // if the saved object already exists
    if(! aresult.second){
        // append the object id to he preamble
        ar.vsave(object_reference_type(oid));
        // and windup.
        ar.end_preamble();
        return;
    }

    // append id of this object to preamble
    ar.vsave(oid);
    ar.end_preamble();

    // and save the object itself
    state_saver<const void *> x(pending_object);
    state_saver<const basic_oserializer *> y(pending_bos);
    pending_object = t;
    pending_bos = & bpos_ptr->get_basic_serializer();
    bpos_ptr->save_object_ptr(ar, t);
    // add to the set of object initially stored through pointers
    stored_pointers.insert(oid);
}

// replaces addresses of objects in the object_set with NULL so that
// future objects will be duplicated, but object_id's will be
// preserved.
inline void
basic_oarchive_impl::flush()
{
  num_flushed_objects += object_set.size();
  object_set.clear();
}

//////////////////////////////////////////////////////////////////////
// implementation of basic_oarchive functions

BOOST_DECL_ARCHIVE
basic_oarchive::basic_oarchive()
    : pimpl(new basic_oarchive_impl)
{}

BOOST_DECL_ARCHIVE
basic_oarchive::~basic_oarchive()
{
    delete pimpl;
}

void
BOOST_DECL_ARCHIVE
basic_oarchive::save_object(
    const void *x,
    const basic_oserializer & bos
){
    pimpl->save_object(*this, x, bos);
}

void
BOOST_DECL_ARCHIVE
basic_oarchive::save_pointer(
    const void * t,
    const basic_pointer_oserializer * bpos_ptr
){
    pimpl->save_pointer(*this, t, bpos_ptr);
}

void
BOOST_DECL_ARCHIVE
basic_oarchive::register_basic_serializer(const basic_oserializer & bos){
    pimpl->register_type(bos);
}

unsigned int
BOOST_DECL_ARCHIVE
basic_oarchive::library_version() const{
    return ARCHIVE_VERSION();
}

void
BOOST_DECL_ARCHIVE
basic_oarchive::end_preamble(){
}

void
BOOST_DECL_ARCHIVE
basic_oarchive::flush(){
  pimpl->flush();
}

} // namespace detail
} // namespace archive
} // namespace boost

#ifdef BOOST_MSVC
#pragma warning(pop)
#endif


#ifndef BOOST_ARCHIVE_BASIC_OARCHIVE_HPP
#define BOOST_ARCHIVE_BASIC_OARCHIVE_HPP

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// basic_oarchive.hpp:

// (C) Copyright 2002 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)

// See http://www.boost.org for updates, documentation, and revision history.

#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>

// can't use this - much as I'd like to as borland doesn't support it
// #include <boost/scoped_ptr.hpp>

#include <boost/archive/basic_archive.hpp>
#include <boost/serialization/tracking.hpp>

#include <boost/archive/detail/abi_prefix.hpp> // must be the last header

namespace boost {
namespace archive {
namespace detail {

class BOOST_DECL_ARCHIVE basic_oarchive_impl;
class BOOST_DECL_ARCHIVE basic_oserializer;
class BOOST_DECL_ARCHIVE basic_pointer_oserializer;
//////////////////////////////////////////////////////////////////////
// class basic_oarchive - write serialized objects to an output stream
class BOOST_DECL_ARCHIVE basic_oarchive
{
    friend class basic_oarchive_impl;
    // hide implementation of this class to minimize header conclusion
    // in client code. note: borland can't use scoped_ptr
    //boost::scoped_ptr<basic_oarchive_impl> pimpl;
    basic_oarchive_impl * pimpl;

    // overload these to bracket object attributes. Used to implement
    // xml archives
    virtual void vsave(const version_type t) = 0;
    virtual void vsave(const object_id_type t) = 0;
    virtual void vsave(const object_reference_type t) = 0;
    virtual void vsave(const class_id_type t) = 0;
    virtual void vsave(const class_id_optional_type t) = 0;
    virtual void vsave(const class_id_reference_type t) = 0;
    virtual void vsave(const class_name_type & t) = 0;
    virtual void vsave(const tracking_type t) = 0;

protected:
    basic_oarchive();
    virtual ~basic_oarchive();

public:
    unsigned int library_version() const;
    void save_object(
        const void *x,
        const basic_oserializer & bos
    );
    void save_pointer(
        const void * t,
        const basic_pointer_oserializer * bpos_ptr
    );
    void register_basic_serializer(const basic_oserializer & bos);
    void save_null_pointer(){
        vsave(NULL_POINTER_TAG);
    }
    void end_preamble(); // default implementation does nothing
    void flush();
};

} // namespace detail
} // namespace archive
} // namespace boost

// required by smart_cast for compilers not implementing
// partial template specialization
BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION(
    boost::archive::detail::basic_oarchive
)

#include <boost/archive/detail/abi_suffix.hpp> // pops abi_suffix.hpp pragmas

#endif //BOOST_ARCHIVE_BASIC_OARCHIVE_HPP


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk