Hi Robert, I'm a bit confused about the slicing problem. I didn't think slicing would occur because the argument to Archive's operator<< is a reference argument; however, I had been expecting the template serialize to behave like a virtual function and, of course, it doesn't. I took a different approach, writing a small main (below) which merely serializes a bus_route. I add the bus_stop_corner to the bus_route and everything works as I expected. The bus_route is saved with the information from the derived class and the class is restored correctly to the derived class. Similarly, if I serialize &bs0 directly, everything is OK. This leasds me hope that it should be possible to serialize the object, bs0, directly since it is a reference to the base class part of a bus_stop_corner. Having a big difference between a reference and a pointer feels to me that there is some symmetry lacking which means that I am not understanding something fundamental. It should be possible to write something like ar << bs0, and have the information associated with the derived class be saved. Is this what you were referring to as the slicing problem. This seems very strange to me: I add &bs0 to the bus_route or serialize it directly and it serializes correctly, i.e. with the derived class info, and yet there is no way to serialize the object, bs0, directly and also get the derived class. It looks like all of the arguments are reference arguments so this is different than what I usually think of as slicing. Can you help me understand this? Thanks. --Len int main(int argc, char *argv[]) { // fill in the data // make a few stops const bus_stop_corner *bsc0 = new bus_stop_corner(gps_position(34, 135, 52.560f),gps_position(134, 22, 78.30f),"24th Street", "10th Avenue"); const bus_stop& bs0(*bsc0); bus_route br; br.append( const_cast<bus_stop*> (&bs0) ); br.append( dynamic_cast<bus_stop*>( const_cast<bus_stop_corner*> (bsc0)) ); { std::ofstream ofs("demofile1.txt"); boost::archive::text_oarchive oa(ofs); // oa << const_cast<const bus_route&>(br); oa & br; std::cout << "br" << std::endl << br; } std::ifstream ifs("demofile1.txt"); boost::archive::text_iarchive ia(ifs); //oa << *bsc0; bus_route brN; // ia & brN; compiles whether & or >> is used ia >> brN; std::cout << "brN" << std::endl << brN ; } Thanks again. --Len Robert Ramey wrote:
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 mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users