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-09 02:09:48


Further to this, I've removed std::map from the equation. The same problem
occurs when AMap contains just a shared_ptr<A>. Code is as follows:

##############################################################################################################
lib.h

#ifndef __TESTLIB__H_
#define __TESTLIB__H_

#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;

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("a", m_a);
    }

    a_ptr m_a;
};

#endif

##############################################################################################################
lib.cpp

#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include "lib.h"

#include <boost/serialization/export.hpp>

BOOST_CLASS_EXPORT(A);
BOOST_CLASS_EXPORT(B);
BOOST_CLASS_EXPORT(AMap);

##############################################################################################################
main.cpp

#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <fstream>
#include <iostream>

#include "lib.h"

const char* fname="/tmp/testlib.xml";

void save(c_a_ptr a)
{
    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();
        pam->m_a = a_ptr(new B());
        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;
}

On Thu, Dec 9, 2010 at 2:01 PM, Allan Johns <allan.johns_at_[hidden]>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>
>
> BOOST_CLASS_EXPORT(A);
> BOOST_CLASS_EXPORT(B);
> BOOST_CLASS_EXPORT(AMap);
>
>
>
> ##############################################################################################################
> 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
>
>
>
>
>


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