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