Boost Serialization: Transition from versioned class to object_serializable

时间:2016-04-25 09:32:13

标签: c++ serialization boost boost-serialization

TLDR: I would like to transition a class serialisation from implementation level object_class_info to object_serializable, keeping compatibility with old data files while always writing files in the new format.

Rationale: When I started using the Boost Serialization library, I didn’t realise that it already came with a header (complex.hpp) to serialise std::complex<double>. Instead, I wrote my own, free-standing serialisation function:

namespace boost { namespace serialization {
  template<class Archive, typename T>
  void serialize(Archive& ar, std::complex<T>& comp, const unsigned int version) {
    ar & reinterpret_cast<T(&)[2]>(comp)[0];
    ar & reinterpret_cast<T(&)[2]>(comp)[1];
  }
}

This by default enables version and class info tracking, which slows code down quite a bit. The serialization function which comes with Boost is a fair bit faster.

I would now like to transition to using the Boost version always when writing out new data files, but still be able to read in old data files. Reading in new files with an old binary is not an issue.

The problem is that the new serialisation is not versioned (obviously). Further, I don’t even see how I could attempt to read in an archive using the old version of the code and immediately write it out again using the new version, as the deserialisation/serialisation traits are global properties.

What would be the best way to either a) transparently read in old and new files while always writing new files or b) reading in an old file and immediately writing it out in the new format?

1 个答案:

答案 0 :(得分:1)

You can keep the old implementation and use it if the file version is "old".

Use the boost version of complex serialization only when saving or when the file version is "new".

This should be trivial if you have an object containing the complex data to serialize, because can bump the version on the containing object to achieve this

UPDATE

Sample, using a simple wrapper to invoke the old style of serialization:

Live On Coliru

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/complex.hpp>

template <typename T> struct old_format_wrapper {
    T& wrapped;
    old_format_wrapper(T& w) : wrapped(w) {}
};

template <typename T>
old_format_wrapper<T> old_format(T& w) { return {w}; }

namespace boost { namespace serialization {
    template<class Archive, typename T>
        void serialize(Archive& ar, old_format_wrapper<std::complex<T> >& comp, unsigned) {
            ar & reinterpret_cast<T(&)[2]>(comp.wrapped)[0];
            ar & reinterpret_cast<T(&)[2]>(comp.wrapped)[1];
        }
} }

struct IHaveComplexData {
    std::complex<double> data;

    template <typename Ar> void serialize(Ar& ar, unsigned version) {
        switch(version) {
            case 0: { // old
                    auto wrap = old_format(data);
                    ar & wrap;
                }
                break;
            case 1: // new
            default:
                ar & data; // uses boost serialization
                break;
        }
    }
};

int main() {
    {
        boost::archive::text_oarchive oa(std::cout);
        IHaveComplexData o { { 2, 33 } };
        oa << o;
    }

    {
        std::istringstream iss("22 serialization::archive 13 0 0 0 0 2.00000000000000000e+00 3.30000000000000000e+01");
        boost::archive::text_iarchive ia(iss);
        IHaveComplexData o;
        ia >> o;
        std::cout << o.data;
    }
}

Prints (depending on your boost version):

22 serialization::archive 13 0 0 0 0 2.00000000000000000e+00 3.30000000000000000e+01
(2,33)

Of course, you can now set BOOST_CLASS_VERSION(IHaveComplexData, 1)