$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
From: Len Berman (lenberman_at_[hidden])
Date: 2007-09-04 10:33:07
Hi,
I am trying to understand the differences between intrusive and non-intrusive style of serialization for derived classes.  The classes I want to serialize look like:
class Base {}
template<class TYPE> class Derived :  public Base , public std::vector<TYPE> {}
where it is intended that TYPE is a built in C type (int, double, ...)
I'm starting by trying to understand the demo.cpp example since that example serializes derived classes.
My first goal is to serialize bus_stop_corner and bus_stop_destination directly through the base class.  These are serialized through a pointer indirectly when bus_stop_schedule is serialized; however, I've been unable to do this directly. I've gone about it using the pseudo-code in 'Pointers to Objects of Derived Classes'; however, I get the following compilation error:
g++ -g demo2.cpp -o demo2 /usr/lib/libboost_serialization.a
(I'm using g++ (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21))
/usr/include/boost/archive/detail/oserializer.hpp: In function $-1òøvoid boost::archive::save(Archive&, T&) [with Archive = boost::archive::text_oarchive, T = bus_stop*]òù:
/usr/include/boost/archive/basic_text_oarchive.hpp:78:   instantiated from $-1òøvoid boost::archive::basic_text_oarchive<Archive>::save_override(T&, int) [with T = bus_stop*, Archive = boost::archive::text_oarchive]òù
/usr/include/boost/archive/detail/interface_oarchive.hpp:78:   instantiated from $-1òøArchive& boost::archive::detail::interface_oarchive<Archive>::operator<<(T&) [with T = bus_stop*, Archive = boost::archive::text_oarchive]òù
demo2.cpp:164:   instantiated from here
/usr/include/boost/archive/detail/oserializer.hpp:567: error: invalid application of $-1òøsizeofòù to incomplete type òøboost::STATIC_ASSERTION_FAILURE<false>òù 
I've also tried by taking the full demo.cpp, and creating a function, save_stop, modeled after save_schedule.  This compiles but saves only the base class.  This larger piece of code is below, after  ++++++++++++++++++.  Differences from demo.cpp are marked with //<<<<
Obviously, I'm missing something; hopefully, something simple.  
Any help greatly appreciated.
Thanks.
--Len
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// demo.cpp
//
// (C) Copyright 2002-4 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)
#include <iomanip>
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/tmpdir.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
class gps_position
{
   friend std::ostream & operator<<(std::ostream &os, const gps_position &gp);
   friend class boost::serialization::access;
   int degrees;
   int minutes;
   float seconds;
   template<class Archive>
   void serialize(Archive & ar, const unsigned int /* file_version */){
       ar & degrees & minutes & seconds;
   }
public:
   // every serializable class needs a constructor
   gps_position(){};
   gps_position(int _d, int _m, float _s) : 
       degrees(_d), minutes(_m), seconds(_s)
   {}
};
std::ostream & operator<<(std::ostream &os, const gps_position &gp)
{
   return os << ' ' << gp.degrees << (unsigned char)186 << gp.minutes << '\'' << gp.seconds << '"';
}
/////////////////////////////////////////////////////////////
// One bus stop
//
// illustrates serialization of serializable members
//
class bus_stop_corner;
class bus_stop_destination;
class bus_stop
{
   friend class boost::serialization::access;
   friend std::ostream & operator<<(std::ostream &os, const bus_stop &gp);
   virtual std::string description() const = 0;
   gps_position latitude;
   gps_position longitude;
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)    {
                        // register all derived classes.
                        ar.register_type(static_cast<bus_stop_corner>(NULL));
                        ar.register_type(static_cast<bus_stop_destination>(NULL));
                        ar & latitude;
                        ar & longitude;
   }
protected:
   bus_stop(const gps_position & _lat, const gps_position & _long) :
       latitude(_lat), longitude(_long)
   {}
public:
        bus_stop(){}
        //	virtual void serialize(std::ostream &ar, const unsigned int  file_version) = 0;
        virtual ~bus_stop(){}
};
BOOST_IS_ABSTRACT(bus_stop)
std::ostream & operator<<(std::ostream &os, const bus_stop &bs)
{
   return os << bs.latitude << bs.longitude << ' ' << bs.description();
}
/////////////////////////////////////////////////////////////
// Several kinds of bus stops
//
// illustrates serialization of derived types
//
class bus_stop_corner : public bus_stop
{
   friend class boost::serialization::access;
   std::string street1;
   std::string street2;
   virtual std::string description() const
   {
       return street1 + " and " + street2;
   }
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)
   {
       // save/load base class information
       ar & boost::serialization::base_object<bus_stop>(*this);
       ar & street1 & street2;
   }
public:
   bus_stop_corner(){}
// 	virtual void serialize(std::ostream &ar, const unsigned int  file_version){
// 		ar << "bus_stop_corner";
// 	};
   bus_stop_corner(const gps_position & _lat, const gps_position & _long,
       const std::string & _s1, const std::string & _s2
   ) :
       bus_stop(_lat, _long), street1(_s1), street2(_s2)
   {
   }
};
class bus_stop_destination : public bus_stop
{
   friend class boost::serialization::access;
   std::string name;
   virtual std::string description() const
   {
       return name;
   }
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)
   {
                        description();
       ar & boost::serialization::base_object<bus_stop>(*this) & name;
   }
public:
   
   bus_stop_destination(){}
// 	virtual void serialize(std::ostream &ar, const unsigned int  file_version){
// 		ar << "bus_stop_destination";
// };
   bus_stop_destination(
       const gps_position & _lat, const gps_position & _long, const std::string & _name
   ) :
       bus_stop(_lat, _long), name(_name)
   {
   }
};
int main(int argc, char *argv[])
{   
   // fill in the data
   // make a few stops
        bus_stop_corner *bsc0;
        bus_stop *bs0 = bsc0 = new bus_stop_corner(
                                                                                                                                                                                 gps_position(34, 135, 52.560f),
                                                                                                                                                                                 gps_position(134, 22, 78.30f),
                                                                                                                                                                                 "24th Street", "10th Avenue"
                                                                                                                                                                                 );
   std::ofstream ofs("demofile1.txt");
   boost::archive::text_oarchive oa(ofs);
                oa << bs0;
}
++++++++++++++++++++++++++++++++++++++
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// demo.cpp
//
// (C) Copyright 2002-4 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)
#include <iomanip>
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/tmpdir.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
/////////////////////////////////////////////////////////////
// The intent of this program is to serve as a tutorial for
// users of the serialization package.  An attempt has been made
// to illustrate most of the facilities of the package.  
//
// The intent is to create an example suffciently complete to
// illustrate the usage and utility of the package while
// including a minimum of other code. 
//
// This illustration models the bus system of a small city.
// This includes, multiple bus stops,  bus routes and schedules.
// There are different kinds of stops.  Bus stops in general will
// will appear on multiple routes.  A schedule will include
// muliple trips on the same route.
/////////////////////////////////////////////////////////////
// gps coordinate
//
// llustrates serialization for a simple type
//
class gps_position
{
   friend std::ostream & operator<<(std::ostream &os, const gps_position &gp);
   friend class boost::serialization::access;
   int degrees;
   int minutes;
   float seconds;
   template<class Archive>
   void serialize(Archive & ar, const unsigned int /* file_version */){
       ar & degrees & minutes & seconds;
   }
public:
   // every serializable class needs a constructor
   gps_position(){};
   gps_position(int _d, int _m, float _s) : 
       degrees(_d), minutes(_m), seconds(_s)
   {}
};
std::ostream & operator<<(std::ostream &os, const gps_position &gp)
{
   return os << ' ' << gp.degrees << (unsigned char)186 << gp.minutes << '\'' << gp.seconds << '"';
}
/////////////////////////////////////////////////////////////
// One bus stop
//
// illustrates serialization of serializable members
//
class bus_stop
{
   friend class boost::serialization::access;
   friend std::ostream & operator<<(std::ostream &os, const bus_stop &gp);
   virtual std::string description() const = 0;
   gps_position latitude;
   gps_position longitude;
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)
   {
       ar & latitude;
       ar & longitude;
   }
protected:
   bus_stop(const gps_position & _lat, const gps_position & _long) :
       latitude(_lat), longitude(_long)
   {}
public:
   bus_stop(){}
   virtual ~bus_stop(){}
};
BOOST_IS_ABSTRACT(bus_stop)
std::ostream & operator<<(std::ostream &os, const bus_stop &bs)
{
   return os << bs.latitude << bs.longitude << ' ' << bs.description();
}
/////////////////////////////////////////////////////////////
// Several kinds of bus stops
//
// illustrates serialization of derived types
//
class bus_stop_corner : public bus_stop
{
   friend class boost::serialization::access;
   std::string street1;
   std::string street2;
   virtual std::string description() const
   {
       return street1 + " and " + street2;
   }
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)
   {
       // save/load base class information
       ar & boost::serialization::base_object<bus_stop>(*this);
       ar & street1 & street2;
   }
public:
   bus_stop_corner(){}
   bus_stop_corner(const gps_position & _lat, const gps_position & _long,
       const std::string & _s1, const std::string & _s2
   ) :
       bus_stop(_lat, _long), street1(_s1), street2(_s2)
   {
   }
};
class bus_stop_destination : public bus_stop
{
   friend class boost::serialization::access;
   std::string name;
   virtual std::string description() const
   {
       return name;
   }
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)
   {
                        description();
       ar & boost::serialization::base_object<bus_stop>(*this) & name;
   }
public:
   
   bus_stop_destination(){}
   bus_stop_destination(
       const gps_position & _lat, const gps_position & _long, const std::string & _name
   ) :
       bus_stop(_lat, _long), name(_name)
   {
   }
};
/////////////////////////////////////////////////////////////
// a bus route is a collection of bus stops
//
// illustrates serialization of STL collection templates.
//
// illustrates serialzation of polymorphic pointer (bus stop *);
//
// illustrates storage and recovery of shared pointers is correct
// and efficient.  That is objects pointed to by more than one
// pointer are stored only once.  In such cases only one such
// object is restored and pointers are restored to point to it
//
class bus_route
{
   friend class boost::serialization::access;
   friend std::ostream & operator<<(std::ostream &os, const bus_route &br);
   typedef bus_stop * bus_stop_pointer;
   std::list<bus_stop_pointer> stops;
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)
   {
       // in this program, these classes are never serialized directly but rather
       // through a pointer to the base class bus_stop. So we need a way to be
       // sure that the archive contains information about these derived classes.
       //ar.template register_type<bus_stop_corner>();
       ar.register_type(static_cast<bus_stop_corner *>(NULL));
       //ar.template register_type<bus_stop_destination>();
       ar.register_type(static_cast<bus_stop_destination *>(NULL));
       // serialization of stl collections is already defined
       // in the header
       ar & stops;
   }
public:
   bus_route(){}
   void append(bus_stop *_bs)
   {
       stops.insert(stops.end(), _bs);
   }
};
std::ostream & operator<<(std::ostream &os, const bus_route &br)
{
   std::list<bus_stop *>::const_iterator it;
   // note: we're displaying the pointer to permit verification
   // that duplicated pointers are properly restored.
   for(it = br.stops.begin(); it != br.stops.end(); it++){
       os << '\n' << std::hex << "0x" << *it << std::dec << ' ' << **it;
   }
   return os;
}
/////////////////////////////////////////////////////////////
// a bus schedule is a collection of routes each with a starting time
//
// Illustrates serialization of STL objects(pair) in a non-intrusive way.
// See definition of operator<< <pair<F, S> >(ar, pair) and others in
// serialization.hpp
// 
// illustrates nesting of serializable classes
//
// illustrates use of version number to automatically grandfather older
// versions of the same class.
class bus_schedule
{
public:
   // note: this structure was made public. because the friend declarations
   // didn't seem to work as expected.
   struct trip_info
   {
       template<class Archive>
       void serialize(Archive &ar, const unsigned int file_version)
       {
           // in versions 2 or later
           if(file_version >= 2)
               // read the drivers name
               ar & driver;
           // all versions have the follwing info
           ar & hour & minute;
       }
       // starting time
       int hour;
       int minute;
       // only after system shipped was the driver's name added to the class
       std::string driver;
       trip_info(){}
       trip_info(int _h, int _m, const std::string &_d) :
           hour(_h), minute(_m), driver(_d)
       {}
   };
private:
   friend class boost::serialization::access;
   friend std::ostream & operator<<(std::ostream &os, const bus_schedule &bs);
   friend std::ostream & operator<<(std::ostream &os, const bus_schedule::trip_info &ti);
   std::list<std::pair<trip_info, bus_route *> > schedule;
   template<class Archive>
   void serialize(Archive &ar, const unsigned int version)
   {
       ar & schedule;
   }
public:
   void append(const std::string &_d, int _h, int _m, bus_route *_br)
   {
       schedule.insert(schedule.end(), std::make_pair(trip_info(_h, _m, _d), _br));
   }
   bus_schedule(){}
};
BOOST_CLASS_VERSION(bus_schedule, 2)
std::ostream & operator<<(std::ostream &os, const bus_schedule::trip_info &ti)
{
   return os << '\n' << ti.hour << ':' << ti.minute << ' ' << ti.driver << ' ';
}
std::ostream & operator<<(std::ostream &os, const bus_schedule &bs)
{
   std::list<std::pair<bus_schedule::trip_info, bus_route *> >::const_iterator it;
   for(it = bs.schedule.begin(); it != bs.schedule.end(); it++){
       os << it->first << *(it->second);
   }
   return os;
}
void save_schedule(const bus_schedule &s, const char * filename){
   // make an archive
   std::ofstream ofs(filename);
   boost::archive::text_oarchive oa(ofs);
   oa << s;
}
//<<<<
void save_stop(const bus_stop &s, const char * filename){
   // make an archive
   std::ofstream ofs(filename);
   boost::archive::text_oarchive oa(ofs);
                oa.register_type(static_cast<bus_stop_corner* >(NULL));
                //ar.template register_type<bus_stop_destination>();
                oa.register_type(static_cast<bus_stop_destination* >(NULL));
   oa << s;
}
//>>>>
void
restore_schedule(bus_schedule &s, const char * filename)
{
   // open the archive
   std::ifstream ifs(filename);
   boost::archive::text_iarchive ia(ifs);
   // restore the schedule from the archive
   ia >> s;
}
int main(int argc, char *argv[])
{   
   // make the schedule
   bus_schedule original_schedule;
   // fill in the data
   // make a few stops
   bus_stop *bs0 = new bus_stop_corner(
       gps_position(34, 135, 52.560f),
       gps_position(134, 22, 78.30f),
       "24th Street", "10th Avenue"
   );
                //<<<<
   std::string filename1(boost::archive::tmpdir());
   filename1 += "/demofile1.txt";
                save_stop( *bs0 , filename1.c_str());
                //>>>>
   bus_stop *bs1 = new bus_stop_corner(
       gps_position(35, 137, 23.456f),
       gps_position(133, 35, 54.12f),
       "State street", "Cathedral Vista Lane"
   );
   bus_stop *bs2 = new bus_stop_destination(
       gps_position(35, 136, 15.456f),
       gps_position(133, 32, 15.300f),
       "White House"
   );
   bus_stop *bs3 = new bus_stop_destination(
       gps_position(35, 134, 48.789f),
       gps_position(133, 32, 16.230f),
       "Lincoln Memorial"
   );
   // make a  routes
   bus_route route0;
   route0.append(bs0);
   route0.append(bs1);
   route0.append(bs2);
   // add trips to schedule
   original_schedule.append("bob", 6, 24, &route0);
   original_schedule.append("bob", 9, 57, &route0);
   original_schedule.append("alice", 11, 02, &route0);
   // make aother routes
   bus_route route1;
   route1.append(bs3);
   route1.append(bs2);
   route1.append(bs1);
   // add trips to schedule
   original_schedule.append("ted", 7, 17, &route1);
   original_schedule.append("ted", 9, 38, &route1);
   original_schedule.append("alice", 11, 47, &route1);
   // display the complete schedule
   std::cout << "original schedule";
   std::cout << original_schedule;
   
   std::string filename(boost::archive::tmpdir());
   filename += "/demofile.txt";
   // save the schedule
   save_schedule(original_schedule, filename.c_str());
   // ... some time later
   // make  a new schedule
   bus_schedule new_schedule;
   restore_schedule(new_schedule, filename.c_str());
   // and display
   std::cout << "\nrestored schedule";
   std::cout << new_schedule;
   // should be the same as the old one. (except for the pointer values)
   delete bs0;
   delete bs1;
   delete bs2;
   delete bs3;
   return 0;
}