Boost logo

Boost Users :

Subject: [Boost-users] [serialization] Sets of shared pointers won't load
From: John Pallister (john_at_[hidden])
Date: 2010-03-08 20:00:52


Hello list,

I'm using Visual C++ Express 2008 and Boost 1.42. I have a graph of
classes I'm trying to serialise, and I'm using shared_ptrs to manage
multiple references to the same object, but I can't deserialise my
graph corectly. I get errors like:

First-chance exception at 0x7c812afb in sams-6.exe: Microsoft C++
exception: std::basic_string<char,std::char_traits<char>,std::allocator<char>
> at memory location 0x03d7f28c..
HEAP[sams-6.exe]: Invalid allocation size - AC1051B4 (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in sams-6.exe: Microsoft C++
exception: std::bad_alloc at memory location 0x03c7dc5c..
HEAP[sams-6.exe]: Invalid allocation size - AC1051A8 (exceeded 7ffdefff)

Here are my classes:

struct stats_t {
    friend class boost::serialization::access;

    typedef int ts_t;

    size_t bytes, packets;
    ts_t firstPacketTs, lastPacketTs;

    stats_t() : bytes(0), packets(0), firstPacketTs(0), lastPacketTs(0) { }

private:
    template<class Archive> void serialize(Archive &ar, unsigned int
/* file_version */) {
        ar & bytes & packets & firstPacketTs & lastPacketTs;
    }
};

struct dest_port_t {
    friend class boost::serialization::access;

    endpoint_t endpoint;
    shp_socket_t socket;
    bool inUse;
    stats_t stats;
    std::string name;
    boost::asio::ip::udp::endpoint baue;

    dest_port_t(const endpoint_t &e, const std::string
&n=std::string()) : endpoint(e), inUse(false), name(n),
        baue(boost::asio::ip::address_v4(e.first), e.second) { }

private:
    template<class Archive> void serialize(Archive &ar, unsigned int
/* file_version */) {
        ar & endpoint & name & inUse & stats;
    }
};

typedef boost::shared_ptr<dest_port_t> shp_dest_port_t;
typedef std::set<shp_dest_port_t> dest_port_shpset_t;

BOOST_SERIALIZATION_SHARED_PTR( dest_port_t )

namespace boost { namespace serialization {
    template<class Archive> inline void load_construct_data(Archive
&ar, dest_port_t *dpp, unsigned int /* file_version */) {
        endpoint_t endpoint;
        std::string name;

        ar >> endpoint;
        ar >> name;
        ::new(dpp) dest_port_t(endpoint, name);
        ar >> dpp->inUse;
        ar >> dpp->stats;
    }
} }

struct dest_t {
    friend class boost::serialization::access;

    endpoint_t endpoint;
    dest_port_shpset_t ports;
    size_t connections;
    std::string name;

    dest_t(const endpoint_t &e=endpoint_t(), const std::string
&n=std::string()) : endpoint(e), connections(0), name(n) { }

private:
    template<class Archive> void serialize(Archive &ar, unsigned int
/* file_version */) {
        ar & endpoint & ports & connections & name;
    }
};

typedef boost::shared_ptr<dest_t> shp_dest_t;
typedef std::set<shp_dest_t> dest_shpset_t;

BOOST_SERIALIZATION_SHARED_PTR( dest_t )

struct source_t {
    friend class boost::serialization::access;

    endpoint_t endpoint;
    size_t connections;
    stats_t stats;
    const bool foreign; // True for 'unrecognised' sources.
    std::string name;

    source_t(const endpoint_t &e=endpoint_t(), bool f=false, const
std::string &n=std::string()) : endpoint(e), connections(0),
foreign(f), name(n) { }

private:
    template<class Archive> void serialize(Archive &ar, unsigned int
/* file_version */) {
        ar & endpoint & connections & stats & const_cast<bool
&>(foreign) & name;
    }
};

typedef boost::shared_ptr<source_t> shp_source_t;
typedef std::set<shp_source_t> source_shpset_t;

BOOST_SERIALIZATION_SHARED_PTR( source_t )

struct connection_t {
    friend class boost::serialization::access;

    shp_source_t source;
    shp_dest_port_t dest_port;
    shp_dest_t dest;
    stats_t stats;

    connection_t(const shp_source_t &s, const shp_dest_port_t &dp,
const shp_dest_t &d=shp_dest_t())
      : source(s), dest_port(dp), dest(d) { }

private:
    template<class Archive> void serialize(Archive &ar, unsigned int
/* file_version */) {
        ar & source & dest_port & dest & stats;
    }
};

typedef std::set<connection_t> connection_set_t;
typedef std::map<endpoint_t, connection_set_t> talkspurt_map_t;

namespace boost { namespace serialization {
    template<class Archive> inline void load_construct_data(Archive
&ar, connection_t *cp, unsigned int /* file_version */) {
        shp_source_t src;
        shp_dest_port_t dest_port;
        shp_dest_t dest;

        ar >> src;
        ar >> dest_port;
        ar >> dest;
        ::new(cp) connection_t(src, dest_port, dest);
        ar >> cp->stats;
    }
} }

class UdpWorker : public syn::Thread {
protected:
    size_t errors_;
    stats_t total_, invalid_, ignored_;
    dest_shpset_t dests_;
    source_shpset_t sources_;
    talkspurt_map_t talkspurtMap_;
};

So it's UdpWorker -> dest_shpset_t -> dest_t -> dest_port_shpset_t -> stats_t,
UdpWorker -> source_shpset_t -> source_t -> stats_t,
UdpWorker -> talkspurt_map_t -> connection_set_t -> shp_source_t etc.

The code to save is:

void UdpWorker::save() {
    try {
        ofstream os(stateFile_.c_str());
        boost::archive::text_oarchive oa(os);

        oa << errors_;
        oa << total_;
        oa << invalid_;
        oa << ignored_;
        oa << sources_;
        oa << dests_;
        oa << talkspurtMap_;
    }
    catch (boost::archive::archive_exception &ae) {
        log("UdpWorker::save(): Error - caught archive_exception
\"%s\" while saving to text archive \"%s\".",
            ae.what(), stateFile_.c_str());
    }
    catch (...) {
        log("UdpWorker::save(): Error - caught unknown exception while
saving to text archive \"%s\".", stateFile_.c_str());
    }
}

And the loading code is very similar:

void UdpWorker::load() {
    try {
        ifstream is(stateFile_.c_str());
        boost::archive::text_iarchive ia(is);

        ia >> errors_;
        ia >> total_;
        ia >> invalid_;
        ia >> ignored_;
        ia >> sources_;
        ia >> dests_;
        ia >> talkspurtMap_;
    }
    catch (boost::archive::archive_exception &ae) {
        log("UdpWorker::load(): Error - caught archive_exception
\"%s\" while loading from text archive \"%s\".",
            ae.what(), stateFile_.c_str());
    }
    catch (...) {
        log("UdpWorker::load(): Error - caught unknown exception while
loading from text archive \"%s\".", stateFile_.c_str());
    }
}

The archive seems to be written OK, but when loading it seems to load
the sources_ set OK and then die on the dests_ set, with the bad_alloc
exceptions given above. So the talkspurt_map_t hasn't been tested
yet...

I've tried the binary archive, but I get pretty much the same error.

I'd really appreciate it if someone with a more experienced eye could
have a look at my classes and tell me if there's anything suspicious.

Is BOOST_SERIALIZATION_SHARED_PTR still required in 1.42? I couldn't
find any mention of it in the documentation, but many examples seem to
use it. It looks like it's a no-op anyway on this compiler.

Thanks very much,

John :^P

--
John Pallister
john_at_[hidden]
john_at_[hidden]

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