1

I must serialize in Boost vector (or any other container) of abstract class. Because it is impossible to create vector of abstract class directly, I created vector of reference_wrapper's to this abstract class.

But how to serialize in Boost std::reference_wrapper to abstract class? When I try to do it directly, I get an error, that default constructor of reference_wrapper is inaccessible.

Thanks in advance.

  • Would you care to create a MCVE? – sehe May 17 '15 at 12:41
  • No, what is MCVE? How to create it? I can't find any info about it in google. –  May 17 '15 at 16:05
  • Ok. I was trying to be hip and [get with the times](http://stackoverflow.com/help/mcve). Just create a [SSCCE](http://sscce.org) – sehe May 17 '15 at 16:29

1 Answers1

2

Thinking some more about this you wouldn't want to serialize the references.

Since logically references are non-reseatable, and don't "hold any data", they could never be "deserialized" (what would they point to? temporaries? leaked heap objects?).

Of course reference_wrappers are reseatable, but in that case you're really just using them as raw pointers (potentially owning pointers).

In that case, just have them serialized as pointers and optionally use object tracking to prevent duplicate (de)serialization.

Hint I think you'd benefit from boost::ptr_vector which comes with serialization support built in. See below (and docs)


Demo

While coming up with a simple approach that "teaches" Boost Serialization to use reference_wrapper just as a raw pointer (which is likely what you require):

Live On Coliru

/////////////////////////////////////////////////
// Allow serialization of `reference_wrapper`
namespace boost { namespace serialization {
    template<class Ar, class T> void save_construct_data(Ar & ar, std::reference_wrapper<T> const * t, unsigned) {
        T const * p = &t->get();
        ar << p;
    }
    template<class Ar, class T> void load_construct_data(Ar & ar, std::reference_wrapper<T> * t, unsigned) {
        T* p = nullptr;
        ar >> p;
        ::new(t) std::reference_wrapper<T>(*p);
    }

    template <class Ar, typename T> inline void serialize(Ar &, std::reference_wrapper<T>&, unsigned) { }
} }

However, I found out that there's a bigger problem with the fact that reference_wrapper isn't default-constructible wreaks havoc with the way std::vector is deserialized.

I'd simplify and just use pointers when you mean pointers:

Live On Coliru

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/export.hpp>

#include <boost/ptr_container/serialize_ptr_vector.hpp>
#include <functional>

using namespace boost;

/////////////////////////////////////////////////
// defining a virtual class hierarchy...
struct B {
    virtual ~B() = default;
    virtual std::ostream& print(std::ostream& os) const { return os << __PRETTY_FUNCTION__; }

  private:
    friend class boost::serialization::access;
    template <typename Ar> void serialize(Ar&, unsigned) const {
    }
};

struct D1 : B {
    virtual std::ostream& print(std::ostream& os) const { return os << _data; }

  private:
    std::string _data = "forty two";

    friend class boost::serialization::access;
    template <typename Ar> void serialize(Ar& ar, unsigned) {
        ar & boost::serialization::base_object<B>(*this);
        ar & _data;
    }
};

struct D2 : B {
    virtual std::ostream& print(std::ostream& os) const { return os << _data; }

  private:
    int _data = 42;

    friend class boost::serialization::access;
    template <typename Ar> void serialize(Ar& ar, unsigned) {
        ar & boost::serialization::base_object<B>(*this);
        ar & _data;
    }
};

BOOST_CLASS_EXPORT(B)
BOOST_CLASS_EXPORT(D1)
BOOST_CLASS_EXPORT(D2)

/////////////////////////////////////////////////
// sample data structure representing our program

struct Data {
    ptr_vector<B> storage;
    std::vector<B*> refvect;

    void fill_sample() {
        storage.push_back(new D1);
        storage.push_back(new D1);
        storage.push_back(new D2);
        storage.push_back(new D1);

        refvect.clear();
        for (auto it = storage.rbegin(); it != storage.rend(); ++it)
            refvect.push_back(&*it);
    }

    friend std::ostream& operator<<(std::ostream& os, Data const& data) {
        for (auto i : data.refvect) i->print(std::cout) << "\n";
        return os;
    }

    template <typename Ar> void serialize(Ar& ar, unsigned) {
        ar & storage & refvect;
    }
};

#include <sstream>
int main() {

    std::stringstream stream;
    {
        archive::binary_oarchive oa(stream);

        Data x;
        x.fill_sample();
        oa << x;

        std::cout << "Before:\n" << x << "\n";
    }

    {
        archive::binary_iarchive ia(stream);

        Data y;
        ia >> y;
        std::cout << "After:\n" << y << "\n";
    }

}

Prints

Before:
forty two
42
forty two
forty two

After:
forty two
42
forty two
forty two
sehe
  • 374,641
  • 47
  • 450
  • 633