Boost logo

Boost :

Subject: Re: [boost] serialization crash with polymorphic classes, shared_ptr and std::map
From: Allan Johns (allan.johns_at_[hidden])
Date: 2010-12-10 01:06:09


Hi Robert,

Thanks for the help, however I've discovered and fixed the problem (but some
more light on the matter would still be great).

It seems that serialization of shared_ptr<T> can differ to that of
shared_ptr<const T> - the object tracking changes in some cases. I suppose
this makes sense, given that the two are indeed different types.

thanks again
A

On Fri, Dec 10, 2010 at 6:41 AM, Robert Ramey <ramey_at_[hidden]> wrote:

> Try making the changes below
>
> Robert Ramey
>
> Allan Johns wrote:
> > Hi. I have hit a problem with boost serialization (occurs in all
> > versions of boost - 1.37.0 .. 1.45.0).
> >
> > Summary: I have virtual base class A, derived B, and an AMap class
> > (also A-derived) which holds shared_ptr<A>'s.
> > I get different data written out depending on whether I serialise out
> > an
> > AMap stored in a shared_ptr<A> or a shared_ptr<const A>, and in one
> > case (non-const) I get a segfault on load.
> >
> > The following code illustrates the problem in entirety and is the
> > simplest case I found which generates the problem (I tried several
> > variations -
> > saving the map directly, removing A as AMap's base class etc, etc, to
> > no avail). You should be able to cut'n'paste and compile into a single
> > executable. I am testing on linux + centOS.
> >
> >
> ##############################################################################################################
> > lib.h
> >
> > #ifndef __TESTLIB__H_
> > #define __TESTLIB__H_
> >
> > #include <map>
> > #include <boost/serialization/nvp.hpp>
> > #include <boost/serialization/shared_ptr.hpp>
> > #include <boost/serialization/base_object.hpp>
> >
> > class A {
> > public:
> > A(){}
> > virtual ~A(){}
> > template<class Archive> void serialize(Archive & ar, const
> > unsigned int version){}
> > };
> >
> > typedef boost::shared_ptr<A> a_ptr;
> > typedef boost::shared_ptr<const A> c_a_ptr;
> > typedef std::map<int, a_ptr> a_map;
> >
> >
> > class B : public A{
> > public:
> > B(){}
> > virtual ~B(){}
> > template<class Archive> void serialize(Archive & ar, const
> > unsigned int version){
> > ar & boost::serialization::make_nvp("base_class",
> > boost::serialization::base_object<A>(*this));
> > }
> > };
> >
> >
> > class AMap : public A {
> > public:
> > AMap(){}
> > virtual ~AMap(){}
> > template<class Archive> void serialize(Archive & ar, const
> > unsigned int version){
> > ar & boost::serialization::make_nvp("base_class_a",
> > boost::serialization::base_object<A>(*this));
> > ar & boost::serialization::make_nvp("map", m_map);
> > }
> >
> > a_map m_map;
> > };
> >
> > #endif
> >
> >
> >
> ##############################################################################################################
> > lib.cpp
> >
> > #include <boost/archive/xml_oarchive.hpp>
> > #include <boost/archive/xml_iarchive.hpp>
> > #include "lib.h"
> >
> > #include <boost/serialization/map.hpp>
> > #include <boost/serialization/export.hpp>
> >
>
> // drop the folling:
> > BOOST_CLASS_EXPORT(A);
> > BOOST_CLASS_EXPORT(B);
> > BOOST_CLASS_EXPORT(AMap);
>
> // insert the following
> BOOST_SERIALIZATION_SHARED_PTR(B)
>
>
> >
> >
> ##############################################################################################################
> > main.cpp
> >
> > #include <boost/archive/xml_oarchive.hpp>
> > #include <boost/archive/xml_iarchive.hpp>
> > #include <boost/serialization/map.hpp>
> > #include <fstream>
> > #include <iostream>
> >
> > #include "lib.h"
> >
> > const char* fname="/tmp/testlib.xml";
> >
> >
> > void save(*a_ptr* a) // NOTE!!! change me to c_a_ptr to get a
> > segfault on load
> > {
> > std::ofstream fs(fname);
> > boost::archive::xml_oarchive ar(fs);
> > ar & boost::serialization::make_nvp("root", a);
> > }
> >
> >
> > int main(int argc, char** argv)
> > {
> > {
> > std::cout << "saving..." << std::endl;
> > AMap* pam = new AMap();
> > a_ptr a(new B());
> > pam->m_map.insert(a_map::value_type(1, a));
> >
> > a_ptr am(pam);
> > save(am);
> > }
> >
> > {
> > std::cout << "loading..." << std::endl;
> > a_ptr am;
> > std::ifstream fs(fname);
> > boost::archive::xml_iarchive ar(fs);
> > ar & boost::serialization::make_nvp("root", am);
> > assert(am);
> > }
> >
> > return 0;
> > }
> >
> >
> > Note that changing main's save() arg to a shared_ptr<const A> changes
> > the data written out.
> >
> > Here is an example xml archive resulting from saving via a non-const
> > shared_ptr (the case that WORKS on load):
> >
> > <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
> > <!DOCTYPE boost_serialization>
> > <boost_serialization signature="serialization::archive" version="5">
> > <root class_id="0" tracking_level="0" version="1">
> > <px class_id="2" class_name="AMap" tracking_level="1"
> > version="0" object_id="_0">
> > <base_class_a class_id="1" tracking_level="1"
> > version="0" object_id="_1"></base_class_a>
> > <base_class_map class_id="3" tracking_level="0"
> > version="0"> <count>1</count>
> > <item_version>0</item_version>
> > <item class_id="4" tracking_level="0"
> > version="0"> <first>1</first>
> > <second>
> > <px class_id="5" class_name="B"
> > tracking_level="1" version="0" object_id="_2">
> > <base_class
> > object_id="_3"></base_class>
> > </px>
> > </second>
> > </item>
> > </base_class_map>
> > </px>
> > </root>
> > </boost_serialization>
> >
> >
> > Here is the same data but written out via the CONST shared_ptr (the
> > case
> > that FAILS on load):
> >
> > <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
> > <!DOCTYPE boost_serialization>
> > <boost_serialization signature="serialization::archive" version="5">
> > <root class_id="0" tracking_level="0" version="1">
> > <px class_id="2" class_name="AMap" tracking_level="1"
> > version="0" object_id="_0">
> > <base_class_a class_id="1" tracking_level="1"
> > version="0" object_id="_1"></base_class_a>
> > <base_class_map class_id="3" tracking_level="0"
> > version="0"> <count>1</count>
> > <item_version>0</item_version>
> > <item class_id="4" tracking_level="0"
> > version="0"> <first>1</first>
> > <second *class_id="5"
> > tracking_level="0" version="1"*>
> > <px class_id="6" class_name="B"
> > tracking_level="1" version="0" object_id="_2">
> > <base_class
> > object_id="_3"></base_class>
> > </px>
> > </second>
> > </item>
> > </base_class_map>
> > </px>
> > </root>
> > </boost_serialization>
> >
> >
> > Note the difference, which I've put in bold.
> >
> > If somebody could explain what is happening here I'd be very grateful.
> > Obviously something is going on with the object tracking... and what
> > I find most strange is that the data that loads correctly actually
> > looks incorrect (<second> doesn't have tracking-level=0, whereas
> > tracking-level=0 wherever else there's a shared_ptr, as I'd expect).
> >
> > Note: I have tested using the xml and binary archives... xml
> > segfaults, binary throws a stream_error.
> >
> > Thanks in advance
> > Allan
> > _______________________________________________
> > Unsubscribe & other changes:
> > http://lists.boost.org/mailman/listinfo.cgi/boost
>
>
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk