Boost logo

Boost Users :

Subject: Re: [Boost-users] http://www.boost.org/doc/libs/1_53_0/libs/serialization/example/demo.cpp does not work properly when using polymorphic xml archive
From: Robert Ramey (ramey_at_[hidden])
Date: 2013-06-14 19:02:08


Fadhel wrote:
> Hi all,
>
> I used
> http://www.boost.org/doc/libs/1_53_0/libs/serialization/example/demo.cpp
> file as a first introduction to boost serialization. I would like to
> use polymorphic archives with XML serilialization format (to avoid
> having the templatized version). I edited this example and run it. Writing
> the data has worked fine -- except for the base_class serialization
> which needed an nvp -- but reading it causes a memory/segmentation
> fault problem. The same error happens with a binary archive. Using a
> text archive works fine. I'm new to boost serialization and debugging
> the code did not help me a lot.

Note that there are specific tests/examples for using polymorphic
archives. They might not be mentioned in the documentation. Look
in the examples and test directories.

> Is there anything specific to the serialization of base classes or
> abstract classes with the polymorphic XML format which needs to be
> handled? Or is this related to the serialization of pointers to the
> base class.

Try making the following changes below. These mirror the more
common way that the polymorphic archives would be used. The
whole idea is to delay the actual serialization archive selection until
runtime.

> Thanks for your help.
> Best Regards,
> Fadhel BA
>
> Here is the code I'm using: ( I removed some comments to make the code
> shorter)
>
> /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
> // demo.cpp
> #include <cstddef> // NULL
> #include <iomanip>
> #include <iostream>
> #include <fstream>
> #include <string>
>
> #include <boost/archive/polymorphic_iarchive.hpp>
> #include <boost/archive/polymorphic_oarchive.hpp>
> #include <boost/archive/polymorphic_xml_iarchive.hpp>
> #include <boost/archive/polymorphic_xml_oarchive.hpp>
> #include <boost/archive/polymorphic_binary_iarchive.hpp>
> #include <boost/archive/polymorphic_binary_oarchive.hpp>
> #include <boost/archive/polymorphic_text_iarchive.hpp>
> #include <boost/archive/polymorphic_text_oarchive.hpp>
>
> #include <boost/serialization/base_object.hpp>
> #include <boost/serialization/utility.hpp>
> #include <boost/serialization/list.hpp>
> #include <boost/serialization/assume_abstract.hpp>
> #include <boost/serialization/nvp.hpp>
>
> /////////////////////////////////////////////////////////////
> // 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;
>
> void serialize(boost::archive::polymorphic_oarchive &ar, const
> unsigned int /* file_version */) const {
> ar << BOOST_SERIALIZATION_NVP(degrees);
> ar << BOOST_SERIALIZATION_NVP(minutes);
> ar << BOOST_SERIALIZATION_NVP(seconds);
> }
>
> void serialize(boost::archive::polymorphic_iarchive &ar, const
> unsigned int /* file_version */) {
> ar >> BOOST_SERIALIZATION_NVP(degrees);
> ar >> BOOST_SERIALIZATION_NVP(minutes);
> ar >> BOOST_SERIALIZATION_NVP(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;
>
> void serialize(boost::archive::polymorphic_oarchive &ar, const
> unsigned int version) const
> {
> ar << BOOST_SERIALIZATION_NVP(latitude);
> ar << BOOST_SERIALIZATION_NVP(longitude);
> }
>
> void serialize(boost::archive::polymorphic_iarchive &ar, const
> unsigned int version)
> {
> ar >> BOOST_SERIALIZATION_NVP(latitude);
> ar >> BOOST_SERIALIZATION_NVP(longitude);
> }
> protected:
> bus_stop(const gps_position & _lat, const gps_position & _long) :
> latitude(_lat), longitude(_long)
> {}
> public:
> bus_stop(){}
> virtual ~bus_stop(){}
> };
>
> BOOST_SERIALIZATION_ASSUME_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;
> }
>
> void serialize(boost::archive::polymorphic_oarchive &ar, const
> unsigned int version) const
> {
> // save/load base class information
> ar << boost::serialization::make_nvp("base",
> boost::serialization::base_object<bus_stop>(*this));
> ar << BOOST_SERIALIZATION_NVP(street1);
> ar << BOOST_SERIALIZATION_NVP(street2);;
> }
>
> void serialize(boost::archive::polymorphic_iarchive &ar, const
> unsigned int version)
> {
> // save/load base class information
> ar >> boost::serialization::make_nvp("base",
> boost::serialization::base_object<bus_stop>(*this));
> ar >> BOOST_SERIALIZATION_NVP(street1);
> ar >> BOOST_SERIALIZATION_NVP(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;
> }
>
> void serialize(boost::archive::polymorphic_oarchive &ar, const
> unsigned int version) const
> {
> ar << boost::serialization::make_nvp("base",
> boost::serialization::base_object<bus_stop>(*this));
> ar << BOOST_SERIALIZATION_NVP(name);
> }
>
> void serialize(boost::archive::polymorphic_iarchive &ar, const
> unsigned int version)
> {
> ar >> boost::serialization::make_nvp("base",
> boost::serialization::base_object<bus_stop>(*this));
> ar >> BOOST_SERIALIZATION_NVP(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 *);
> //
>
> 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;
>
> void serialize(boost::archive::polymorphic_oarchive &ar, const
> unsigned int version) const
> {
> // 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 << BOOST_SERIALIZATION_NVP(stops);
> }
>
> void serialize(boost::archive::polymorphic_iarchive &ar, const
> unsigned int version)
> {
>
> ar.register_type(static_cast<bus_stop_corner *>(NULL));
> ar.register_type(static_cast<bus_stop_destination *>(NULL));
> // in the header
> ar >> BOOST_SERIALIZATION_NVP(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
>
> class bus_schedule
> {
> public:
> // note: this structure was made public. because the friend
> declarations
> // didn't seem to work as expected.
> struct trip_info
> {
> void serialize(boost::archive::polymorphic_oarchive &ar, const
> unsigned int file_version) const
> {
> // in versions 2 or later
> if(file_version >= 2)
> // read the drivers name
> ar << BOOST_SERIALIZATION_NVP(driver);
> // all versions have the follwing info
> ar << BOOST_SERIALIZATION_NVP(hour);
> ar << BOOST_SERIALIZATION_NVP(minute);
> }
>
> void serialize(boost::archive::polymorphic_iarchive &ar,
> const unsigned int file_version)
> {
> // in versions 2 or later
> if(file_version >= 2)
> // read the drivers name
> ar >> BOOST_SERIALIZATION_NVP(driver);
> // all versions have the follwing info
> ar >> BOOST_SERIALIZATION_NVP(hour);
> ar >> BOOST_SERIALIZATION_NVP(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;
>
> void serialize(boost::archive::polymorphic_oarchive &ar, const
> unsigned int version) const
> {
> ar << BOOST_SERIALIZATION_NVP(schedule);
> }
>
> void serialize(boost::archive::polymorphic_iarchive &ar, const
> unsigned int version)
> {
> ar >> BOOST_SERIALIZATION_NVP(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);

       // try the following.
> //boost::archive::polymorphic_xml_oarchive oa(ofs);
       boost::archive::polymorphic_oarchive & oa =
boost::archive::polymorphic_xml_oarchive(ofs)

> oa << s;
> }
>
> void
> restore_schedule(bus_schedule &s, const char * filename)
> {
> // open the archive
> std::ifstream ifs(filename);
> //boost::archive::polymorphic_xml_iarchive ia(ifs);
       boost::archive::polymorphic_iarchive & ia =
boost::archive::polymorphic_xml_iarchive(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"
> );
> 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("C:/Dev/XML");
> filename += "/demofile.xml";
>
> // save the schedule
> save_schedule(original_schedule, filename.c_str());
>
> // ... some time later
> // make a new schedule
> bus_schedule new_schedule;
>
> std::cout << "\n\nstarting restored 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