Boost logo

Boost Users :

From: Robbie Morrison (robbie_at_[hidden])
Date: 2007-08-22 12:55:12


Hello again

After posting my earlier message
<mailman.2403.1187719362.16337.boost-users_at_[hidden]>,
I thought some more about 'demo_shared_ptr.cpp' and
tried new fixes (isn't that always the way).

My modified file 'demo_shared_ptr.mod.cpp' now builds
and runs cleanly in both text and XML archive modes.

The revised file is appended here. It contains some
documentation at the end which may be worth reading.

Note too that the data members named 'x' are now member
initialized and this duly silences (Linux system) g++
compilier warnings and a raft of valgrind (memory
checker) errors.

I would now hope that a modified form of
'demo_shared_ptr.cpp' drawing on my experiences can be
checked into the Boost SVN repository. I don't think I
should be the person who does this, as I may have
introduced subtle bugs or bad practice.

Neither I have filed a bug report but would do so if
asked.

Finally, the presence of a buggy 'demo_shared_ptr.cpp'
in both 1.33.1 and 1.34.1 almost caused me to give up
on using Boost.Serialization. By all accounts, I am
not the first to struggle with this demo file and
fixing it should be accorded some priority.

Many thanks, as always, to the developers and
maintainers of Boost.Serialization!

best regards to all
Robbie

---
Robbie Morrison
PhD student -- policy-oriented energy system simulation
Institute for Energy Engineering (IET)
Technical University of Berlin (TU-Berlin), Germany
[from IMAP client]
// demo_shared_ptr.cpp : shows 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 variant and some bug
// fixes added by Robbie Morrison <robbie_at_[hidden]> on 22-Aug-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.
//
// This file has been tested with Boost release 1.34.1 dated
// 24-Jul-2007 (source build) and release 1.33.1 (Ubuntu package)
// using the GNU GCC 4.1.2 compiler (with -Wall -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.
//
// Unlike the 1.34.1 version of this demo, the archive file is not
// deleted and can be subsequently examined using any pager utility or
// text editor.
//
// Jump to the end of this file for a discussion on some of the coding
// practices employed.
// ---------------------------------------------------------
//  Preprocessor directives controlling archive type
// ---------------------------------------------------------
// these can be reset lower values to aid troubleshooting
// 0 = straight text, 1 = text with NVP (name-value-pair) statements, 2 = xml
#define ARCHIVE_MODE 2
// 0 = omit David Tonge derived pointer code, 1 = use derived pointer code
#define POLYMORPHIC_CODE 1
// ---------------------------------------------------------
#include <iomanip>            // setw() and family
#include <iostream>           // standard io
#include <fstream>            // file-based io
#include <string>             // C++ strings
#include <boost/archive/tmpdir.hpp>            // locate a temporary directory
#if (ARCHIVE_MODE >= 2)
# include <boost/archive/xml_iarchive.hpp>     // archive for loading XML
# include <boost/archive/xml_oarchive.hpp>     // archive for saving XML
#else
# include <boost/archive/text_iarchive.hpp>    // archive for loading text
# include <boost/archive/text_oarchive.hpp>    // archive for saving text
#endif
#include <boost/serialization/shared_ptr.hpp>  // shared_ptr serialization
#include <boost/serialization/base_object.hpp> // base class serialization
#include <boost/serialization/export.hpp>      // explicit code for exports (place last)
class A
{
    friend class boost::serialization::access;
private:
    template <class Archive>
    void serialize
    (Archive& ar, const unsigned int class_version) {
#if (ARCHIVE_MODE >= 1)
        ar & BOOST_SERIALIZATION_NVP(x);     // boost::serialization::make_nvp("x", x)
#else
        ar & x;                              // simple form for text archives
#endif
    }
    int x;
public:
    A(): x(42)   { ++count; }                // default constructor, the 42 is arbitrary
    virtual ~A() { --count; }
    static int count;                        // public in order to simplify test code
};
BOOST_CLASS_EXPORT(A)                        // requires <boost/serialization/export.hpp>
// if using particular Metrowerks or Borland compilers, uncomment the following:
// BOOST_SERIALIZATION_SHARED_PTR(B)
int A::count = 0;
class B : public A                           // class B added by David Tonge
{
    friend class boost::serialization::access;
private:
    int x;
    template<class Archive>
    void serialize(Archive& ar, const unsigned int class_version) {
#if (ARCHIVE_MODE >= 1)
      //ar & boost::serialization::base_object<A>(*this);  // CAUTION: problematic for XML
        ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(A);       // ..  but this works fine
        ar & BOOST_SERIALIZATION_NVP(x);
#else
        ar & boost::serialization::base_object<A>(*this);  // this is fine for text
      //ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(A);       // ..  and this also works
        ar & x;                                            // x now saved (unlike 1.34.1)
#endif
    }
public:
    B() : A(), x(84) { ++count; }            // default constructor, the 84 is arbitrary
    virtual ~B()     { --count; }
    static int count;
};
// if using particular Metrowerks or Borland compilers, uncomment the following:
// BOOST_SERIALIZATION_SHARED_PTR(B)
BOOST_CLASS_EXPORT(B)
int B::count = 0;
void display(boost::shared_ptr<A>& a, boost::shared_ptr<A>& a1)
{
    std::cout << "a  = 0x" << std::hex << a.get() << std::dec << ", ";
    if (a.get()) std::cout << "is a " << typeid(*(a.get())).name() << "*, ";
    std::cout << "use count = " << std::dec << a.use_count() << std::endl;
    std::cout << "a1 = 0x" << std::hex << a1.get() << std::dec << ", ";
    if (a1.get()) std::cout << "is a " << typeid(*(a1.get())).name() << "*, ";
    std::cout << "use count = " << std::dec << a1.use_count() << std::endl;
    std::cout << "unique element count = " << A::count << std::endl;
    std::cout << std::endl;
}
int main(int argc, char* argv[])
{
    // report some build details to the console
    std::cout << std::endl;
    std::cout << "archive mode       : " << ARCHIVE_MODE     << std::endl;
    std::cout << "DT extension mode  : " << POLYMORPHIC_CODE << std::endl;
    // name the archive file appropriately
#if (ARCHIVE_MODE >= 2)
    std::string ext = "xml";                 // XML archive
#else
    std::string ext = "txt";                 // text archive
#endif
    std::string filename(boost::archive::tmpdir());    //locate a temporary directory
    filename += "/demo_shared_ptr";
    filename += ".";
    filename += ext;
    std::cout << "testfile name      : " << filename << std::endl;
    std::cout << std::endl;
    // note the test type
    std::cout << "base smart pointer tests" << std::endl;
    std::cout << std::endl;
    // create a new shared pointer to a new object of type A
    boost::shared_ptr<A> spa(new A());
    boost::shared_ptr<A> spa1;
    spa1 = spa;
    display(spa, spa1);
    // serialize the pointers
    {
        // open the archive file
        std::ofstream ofs(filename.c_str());
#if (ARCHIVE_MODE >= 2)
        boost::archive::xml_oarchive oa(ofs);
#else
        boost::archive::text_oarchive oa(ofs);
#endif
#if (ARCHIVE_MODE >= 1)
        oa << BOOST_SERIALIZATION_NVP(spa);
        oa << BOOST_SERIALIZATION_NVP(spa1);
#else
        oa << spa;
        oa << spa1;
#endif
    }
    // reset the shared pointer to NULL thereby destroying the type A object
    spa.reset();
    spa1.reset();
    display(spa, spa1);
    // restore state to one equivalent to the original
    {
        // open the archive file
        std::ifstream ifs(filename.c_str());
#if (ARCHIVE_MODE >= 2)
        boost::archive::xml_iarchive ia(ifs);
#else
        boost::archive::text_iarchive ia(ifs);
#endif
#if (ARCHIVE_MODE >= 1)
        ia >> BOOST_SERIALIZATION_NVP(spa);
        ia >> BOOST_SERIALIZATION_NVP(spa1);
#else
        ia >> spa;
        ia >> spa1;
#endif
    }
    display(spa, spa1);
    spa.reset();
    spa1.reset();
#if (POLYMORPHIC_CODE >= 1)                  // that is, use derived pointer code
    // note the test type
    std::cout << "derived smart pointer tests" << std::endl;
    std::cout << std::endl;
    // new code by David Tonge starts here
    // create a new shared pointer to a new object of type A
    spa  = boost::shared_ptr<A>(new B());
    spa1 = spa;
    display(spa, spa1);
    // serialize it
    // CAUTION: archive saved earlier will be simply overwritten
    {
        std::ofstream ofs(filename.c_str());
#if (ARCHIVE_MODE >= 2)
        boost::archive::xml_oarchive oa(ofs);
#else
        boost::archive::text_oarchive oa(ofs);
#endif
#if (ARCHIVE_MODE >= 1)
        oa << BOOST_SERIALIZATION_NVP(spa);
        oa << BOOST_SERIALIZATION_NVP(spa1);
#else
        oa << spa;
        oa << spa1;
#endif
    }
    // reset the shared pointer to NULL thereby destroying the type B object
    spa.reset();
    spa1.reset();
    display(spa, spa1);
    // restore state to one equivalent to the original
    {
        // reopen the archive
        std::ifstream ifs(filename.c_str());
#if (ARCHIVE_MODE >= 2)
        boost::archive::xml_iarchive ia(ifs);
#else
        boost::archive::text_iarchive ia(ifs);
#endif
#if (ARCHIVE_MODE >= 1)
        ia >> BOOST_SERIALIZATION_NVP(spa);
        ia >> BOOST_SERIALIZATION_NVP(spa1);
#else
        ia >> spa;
        ia >> spa1;
#endif
    }
    display(spa, spa1);
#endif // POLYMORPHIC_CODE
    return 0;
} // main()
// ---------------------------------
//  Output
// ---------------------------------
//
//  archive mode       : 2
//  DT extension mode  : 1
//  testfile name      : ./demo_shared_ptr.xml
//
//  base smart pointer tests
//
//  a  = 0x0x42ca428, is a 1A*, use count = 2
//  a1 = 0x0x42ca428, is a 1A*, use count = 2
//  unique element count = 1
//
//  a  = 0x0, use count = 0
//  a1 = 0x0, use count = 0
//  unique element count = 0
//
//  a  = 0x0x42d3a08, is a 1A*, use count = 2
//  a1 = 0x0x42d3a08, is a 1A*, use count = 2
//  unique element count = 1
//
//  derived smart pointer tests
//
//  a  = 0x0x42d4ab0, is a 1B*, use count = 2
//  a1 = 0x0x42d4ab0, is a 1B*, use count = 2
//  unique element count = 1
//
//  a  = 0x0, use count = 0
//  a1 = 0x0, use count = 0
//  unique element count = 0
//
//  a  = 0x0x42de180, is a 1B*, use count = 2
//  a1 = 0x0x42de180, is a 1B*, use count = 2
//  unique element count = 1
// ---------------------------------
//  Coding practices
// ---------------------------------
//
//  Polymorphic behavior
//
//      This demo does involve polymorphic behavior due to the
//      presence of a virtual function in the base class, in this case
//      the virtual destructor for A:
//
//          virtual ~A();  // gives rise to polymorphic behavior
//
//      If you wish to experiment, remove the virtual specifier and
//      note the now much reduced XML archive.
//
//  Default (zero-argument) constructor
//
//      All deserialized classes need a default constructor.  This
//      need not be explicitly defined, in which case the
//      compiler-supplied version is used.  For deserialization
//      purposes, the default constructor can be private (although
//      other design considerations will normally mean that it is, at
//      most, protected).
//
//  Class registration
//
//      In this demo, class registration relies on explicit export of
//      the class and its globally unique identifier (GUID).  This is
//      achieved using the following macro:
//
//          BOOST_CLASS_EXPORT(Class)  // "Class" becomes the GUID
//
//      This form of class registration is known as "key export".  A
//      more general macro also exists (but was not employed here) in
//      which the client code actively manages the GUID:
//
//          BOOST_CLASS_EXPORT_GUID(Class, "my-guid")
//
//      Class registration can also be accomplished by inserting a C++
//      registration statement within 'serialize' or, in some cases,
//      placing reliance on serialization order.  However these other
//      approaches require an up-front knowledge of the class
//      hierarchy or are dependent on program flow.  Key export is
//      hence the more flexible and less brittle strategy and should
//      therefore be preferred.  Moreover, it is probably good
//      practice to explicitly export all serialized classes, even when
//      this is not strictly required.
//
//  Base/derived information
//
//      Class hierarchy information is communicated here using the
//      following macro:
//
//          ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(BaseClass);
//
//      As before, a direct C++ statement is possible.  However this
//      broke for the XML format archive (I didn't investigate why).
//      Hence it would appear that use of the above macro is necessary
//      when serializing to XML.
//
//  Non-default names/GUIDs
//
//      If a macro which employs an explicit object name or class GUID
//      is used, then the new string literal must be valid XML.  In
//      particular, whitespace is not acceptable and its use will
//      generate an "unrecognized XML syntax" exception at run-time:
//
//          using boost::serialization::make_nvp;
//          ar & make_nvp("my data", data);  // run-time exception
//
//  Associated headers
//
//      Read the code itself for more information on the associated
//      headers and their correct placement:
//
//          <boost/serialization/base_object.hpp>
//          <boost/serialization/export.hpp>
//
//  Build and run command-lines (your library name may be different)
//
//      all warnings (the grep screening relates to -Weffc++, see man g++):
//
//          $ g++ -ggdb -Wall -Weffc++ -pedantic
//            demo_shared_ptr.mod.cpp
//            -lboost_serialization-gcc41
//            -o demo_shared_ptr
//            2>&1
//            | grep --invert-match '/usr/local/include/boost-1_34_1/boost'
//            | grep --invert-match 'instantiated from'
//
//      simplified build:
//
//          $ g++ demo_shared_ptr.mod.cpp
//            -lboost_serialization-gcc41
//            -o demo_shared_ptr
//
//      run with memory checking:
//
//          $ valgrind ./demo_shared_ptr
//
//      simplified run:
//
//          $ ./demo_shared_ptr
//
//  Compile-time static asserts
//
//      These may be difficult to interpret (in my limited
//      experience).  Note that a pointer to a C++ fundamental type
//      (for example, int*) cannot be de/serialized.
//
//  Run-time exceptions
//
//      Some common messages and probable fixes (based on the style
//      recommendations above):
//
//          "unregistered_class"
//               missing: BOOST_CLASS_EXPORT(SubClass)
//
//          "unregistered void cast"
//              missing: ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(BaseClass);
//
//          "unrecognized XML syntax"
//              name-value-pair name contains whitespace
// end of file demo_shared_ptr.mod.cpp

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net