Hello all
The 'demo_shared_ptr.cpp' example currently bundled
with the Boost documentation has some problems. It
would not compile without modification on my system
(g++ 4.1/Ubuntu 6.10). And it took quite some work to
make XML the archive format if one is not familiar with
the Boost.Serialization library.
The code below, I hope, represents an improvement.
I will happily take on-board any suggestions and fixes
(descriptive or diffs as suits) and will resubmit a
modified example back to this list in due course.
Furthermore, it may be appropriate to add the final
version of this file to the official Boost
documentation. Alternatively, I can put it on the
Boost Wiki as an unofficial contribution. (Perhaps
Robert can advise on which approach would be better?)
Finally, some search terms which might help locate this
posting: unregistered void cast sp_counted_base_impl
oa.register_type
with best wishes
Robbie
---
Robbie Morrison
PhD student -- policy-oriented energy system simulation
Institute for Energy Engineering (IET)
Technical University of Berlin (TU-Berlin), Germany
8<---------------------------------------------------------------
// demo_shared_ptr_xml.cpp : demos polymorphic pointer XML serialization
// *******************************************************************
// MODIFICATION WARNING: this file has been modified by a Boost user
// and is not part of the official Boost distribution.
// *******************************************************************
// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . Polymorphic
// derived pointer example by David Tonge. XML archive format and bug
// fixes added by Robbie Morrison on 10-Sep-2007.
// 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.
//
// Purpose
//
// This file demonstrates how base class pointers holding derived
// class objects can be serialized and deserialized in one of
// three archive formats: text, XML, and platform-specific binary.
//
// Important
//
// Note carefully that, in this example, the class hierarchy is
// "polymorphic", which means that each parent class definition
// contains at least one virtual function -- in this case, its
// destructor.
//
// Base classes can also be abstract. Users may wish to
// experiment by making class A abstract, in which case, the
// following macro should be placed after the duly revised class
// definition: BOOST_IS_ABSTRACT(A)
//
// Note also that a non-polymorphic class hierarchy will not
// serialize derived instances without extra effort. In most
// cases, it is simpler and less error-prone to declare all
// relevant destructors as virtual.
//
// Text and binary archive formats can be selected by
// redefining the ARCHIVE_FORMAT preprocessor macro in the
// code below. The default format is XML.
//
// XML is a little more demanding code-wise because both the
// object itself and a legal XML tag name need to be
// specified. Often the simplest way to do this is to use a
// Boost macro which automatically generates the tag name
// from the C++ identifier, such as BOOST_SERIALIZATION_NVP(x)
// and BOOST_SERIALIZATION_BASE_OBJECT_NVP(A).
//
// Testing
//
// This file has been tested with Boost release 1.34.1 dated
// 24-Jul-2007 (source build) and also release 1.33.1 (Ubuntu
// package) using the GNU GCC 4.1.2 compiler (with -Wall and
// -pedantic), valgrind version "valgrind-3.2.0-Debian" memory
// checker, and Linux Ubuntu 6.10 operating system (now superseded
// by 7.04). The file builds and runs cleanly under the
// conditions just listed.
#include <iostream> // standard io
#include <fstream> // file-based io
#include <string> // C++ strings
#include // locate a temporary directory
#include // save text archive
#include // load text archive
#include // save XML archive
#include // load XML archive
#include // save non-portable binary archive
#include // load non-portable binary archive
#include // friendship
#include // shared_ptr serialization
#include // base class serialization
#include // BOOST_IS_ABSTRACT macro
#include // BOOST_CLASS_EXPORT macro (place last)
// set some typedefs to enable the client code to shift between
// archive formats without further effort (if you don't need this
// flexibility, omit these typedefs and just use the original
// type names, also remove any library headers that are now
// surplus to requirements)
//
// 1 = text format, 2 = XML format, 3 = binary format
#define ARCHIVE_FORMAT 2
#if (ARCHIVE_FORMAT == 1)
typedef boost::archive::text_oarchive output_archive_type;
typedef boost::archive::text_iarchive input_archive_type;
const std::string archive_ext = "txt";
#elif (ARCHIVE_FORMAT == 2)
typedef boost::archive::xml_oarchive output_archive_type;
typedef boost::archive::xml_iarchive input_archive_type;
const std::string archive_ext = "xml";
#elif (ARCHIVE_FORMAT == 3)
typedef boost::archive::binary_oarchive output_archive_type;
typedef boost::archive::binary_iarchive input_archive_type;
const std::string archive_ext = "dat";
#endif
// class A is a polymorphic base class, due to its virtual destructor
class A
{
friend class boost::serialization::access; // intrusive serialization
private:
template <class Archive>
void serialize
(Archive& ar, const unsigned int class_version) {
ar & BOOST_SERIALIZATION_NVP(x); // macro form
//ar & boost::serialization::make_nvp("x", x); // statement form
}
int x;
public:
A(): x(42) { ++count; } // default constructor, the 42 is arbitrary
virtual ~A() { --count; }
static int count; // public to simplify test code (although not used)
};
// the following macro is needed for derived classes, but does no harm for base classes
// BOOST_CLASS_EXPORT(A) // requires
// uncomment if making class A abstract (left as an exercise for users)
// BOOST_IS_ABSTRACT(A) // requires
// uncomment if using particular Metrowerks or Borland compilers
// BOOST_SERIALIZATION_SHARED_PTR(A)
int A::count = 0;
// derived class B was originally added by David Tonge
class B : public A
{
friend class boost::serialization::access;
private:
template<class Archive>
void serialize(Archive& ar, const unsigned int class_version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(A); // this works fine in all cases
//ar & boost::serialization::base_object<A>(*this); // CAUTION: problematic for XML
ar & BOOST_SERIALIZATION_NVP(x);
}
int x;
public:
B() : x(84) { ++count; } // default constructor, the 84 is arbitrary
virtual ~B() { --count; }
static int count;
};
// uncomment if using particular Metrowerks or Borland compilers
// BOOST_SERIALIZATION_SHARED_PTR(B)
// if omitted, an "unregistered class" boost::archive::archive_exception is thrown
BOOST_CLASS_EXPORT(B) // strictly necessary as B is derived
int B::count = 0;
// display details for two supposedly common shared pointers
void display(std::string tag, boost::shared_ptr<A>& a0, boost::shared_ptr<A>& a1)
{
std::cout << tag << "0: address = 0x" << std::hex << a0.get() << std::dec;
if (a0.get()) std::cout << ", underlying typeid = " << typeid(*(a0.get())).name();
std::cout << ", use_count = " << a0.use_count() << std::endl;
std::cout << tag << "1: address = 0x" << std::hex << a1.get() << std::dec;
if (a1.get()) std::cout << ", underlying typeid = " << typeid(*(a1.get())).name();
std::cout << ", use_count = " << a1.use_count() << std::endl;
std::cout << std::endl;
}
// main code
int main(int argc, char* argv[])
{
// name the archive file
std::string filename(boost::archive::tmpdir()); //locate a temporary directory
filename += "/demo_shared_ptr_xml."; // filename stub
filename += archive_ext; // "txt", "xml", "dat"
const char* cfilename = filename.c_str(); // convert to C-stye string
// part one
std::cout << std::endl;
std::cout << "original pointers" << "\n" << std::endl;
// create two new type A shared pointers to hold a single type A object
boost::shared_ptr<A> spa0(new A()); // spa0(new A) would also be acceptable
boost::shared_ptr<A> spa1;
spa1 = spa0; // both now own the same type A object
display("spa", spa0, spa1);
// new code by David Tonge starts here
// create two new type A shared pointers to hold a single type B object
boost::shared_ptr<A> spb0(new B()); // a derived and also polymorphic pointer
boost::shared_ptr<A> spb1;
spb1 = spb0;
display("spb", spb0, spb1);
// serialize the four pointers
//
// it would be better practice to make the following block a
// try block and then catch exceptions (but this code has
// been omitted for reasons of simplicity)
{
std::ofstream ofs(cfilename, std::ios::binary);
output_archive_type oa(ofs); // see earlier typedefs
oa << BOOST_SERIALIZATION_NVP(spa0);
oa << BOOST_SERIALIZATION_NVP(spa1);
oa << BOOST_SERIALIZATION_NVP(spb0);
oa << BOOST_SERIALIZATION_NVP(spb1);
// 'ofs' and 'oa' are automatically destructed on exiting this block
}
// part two
std::cout << "reset pointers" << "\n" << std::endl;
// reset each shared pointer to hold a type A* null pointer
spa0.reset();
spa1.reset(); // last shared pointer, held object duly deallocated
spb0.reset();
spb1.reset(); // last shared pointer, held object duly deallocated
display("spa", spa0, spa1);
display("spb", spb0, spb1);
// part three
std::cout << "reinstated pointers" << "\n" << std::endl;
// restore program state to one equivalent to the original
{
std::ifstream ifs(cfilename, std::ios::binary);
input_archive_type ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(spa0);
ia >> BOOST_SERIALIZATION_NVP(spa1);
ia >> BOOST_SERIALIZATION_NVP(spb0);
ia >> BOOST_SERIALIZATION_NVP(spb1);
}
display("spa", spa0, spa1);
display("spb", spb0, spb1);
// report archive name
std::cout << "archive name: " << filename << "\n" << std::endl;
return 0;
} // main()
// ---------------------------------
// Output (typeid is compiler-dependent)
// ---------------------------------
//
// original pointers
//
// spa0: address = 0x0x4e719e0, underlying typeid = 1A, use_count = 2
// spa1: address = 0x0x4e719e0, underlying typeid = 1A, use_count = 2
//
// spb0: address = 0x0x4e71a98, underlying typeid = 1B, use_count = 2
// spb1: address = 0x0x4e71a98, underlying typeid = 1B, use_count = 2
//
// reset pointers
//
// spa0: address = 0x0, use_count = 0
// spa1: address = 0x0, use_count = 0
//
// spb0: address = 0x0, use_count = 0
// spb1: address = 0x0, use_count = 0
//
// reinstated pointers
//
// spa0: address = 0x0x4e7b308, underlying typeid = 1A, use_count = 2
// spa1: address = 0x0x4e7b308, underlying typeid = 1A, use_count = 2
//
// spb0: address = 0x0x4e7c600, underlying typeid = 1B, use_count = 2
// spb1: address = 0x0x4e7c600, underlying typeid = 1B, use_count = 2
//
// archive name: ./demo_shared_ptr_xml.xml
// end-of-file demo_shared_ptr_xml.cpp
--------------------------------------------------------------->8
[from IMAP client]