Boost logo

Boost :

Subject: Re: [boost] serialization crash with polymorphic classes, shared_ptr and std::map
From: Robert Ramey (ramey_at_[hidden])
Date: 2010-12-09 14:41:19


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


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