|
Boost Users : |
From: Robert Ramey (ramey_at_[hidden])
Date: 2007-09-04 11:30:19
I think there is a misconception here. Consider the following:
bus_stop * bs = new bus_stop_corner(0)
// bs now contains a pointer to the base class portion of the derived
object.
// so far so good.
bus_stop bs1 = *bs;
// bs1 contains a copy of just the base class portion of the original
derived object.
// The derived portion has been lost. This is referred to as "object
slicing"
// the following operation
ar << *bs
// is equivalent to
bs1 = *bs
ar << bs1
// which will lose the derived portion of the class
The best way to avoid this problem is to make base classes "abstract
by making at least on virtual function (may just the destructor) = 0.
This will detect the problem at compile time.
Robert Ramey
Len Berman wrote:
> 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;
> }
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