Construct object from boost serialization archive

2019-05-31 02:06发布

问题:

Is it possible to construct objects from directly from the archive?

Something like this...

// Non-working pseudo code
struct Foo {
    BOOST_SERIALIZATION_SPLIT_MEMBER();
    std::vector<int> data;

    Foo() {
        // populate "data" by doing calculation
        data.push_back(1); data.push_back(2);
    }

    template<class Archive>
    Foo( Archive & ar ) {
        // populate "data" by rading the archive
    }

    template<class Archive>
    void save(Archive & ar, const unsigned int version) const {
        // Normal serialization of data
        ar << data;
    }
};

int main(int argc, const char *argv[])
{
    // deserialize
    boost::archive::text_iarchive oar(std::cin);
    Foo foo(oar);

    return 0;
}

回答1:

You can use a deserializing constructor:

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

#include <fstream>

class Point
{
public:
    Point() = default;
    Point(boost::archive::text_iarchive& archive)
    {
        archive >> *this;
    }

    float x = 1.;
    float y = 2.;

private:
    friend class boost::serialization::access;

    template<class TArchive>
    void serialize(TArchive & archive, const unsigned int version)
    {
        archive & x;
        archive & y;
    }
};


int main()
{
    Point p;
    p.x = 5;
    p.y = 6;

    std::ofstream outputStream("test.archive");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive << p;
    outputStream.close();

    std::ifstream inputStream("test.archive");
    boost::archive::text_iarchive inputArchive(inputStream);

    Point pointRead(inputArchive);

    std::cout << pointRead.x << " " << pointRead.y << std::endl;

    return 0;
}


回答2:

As I said in the comment. Yes there is no problem with constructing from an archive. (Another alternative is to have static load function but that can have performance penalties).

The only potential problem I see with your approach is that your constructor can take almost anything as an argument and that can create problems. And that can interfere with the copy constructor and other single argument constructors relying in implicit conversion.

So one has to restrict to take archives only.

There are different methods to do this, but based in this conversation http://marc.info/?l=boost&m=121131260728308&w=2, and by the fact that the inheritance tree of the archives is documented http://www.boost.org/doc/libs/1_35_0/libs/serialization/doc/class_diagram.html, I think this is the best solution is to check that the argument derives from basic_iarchive.

#include<type_traits>
struct Foo {
    ...
    std::vector<int> data;    
    Foo() {
        // populate "data" by doing calculation
        data.push_back(1); data.push_back(2);
    }

    template<class IArchive, 
        typename = std::enable_if_t<std::is_base_of<boost::archive::detail::basic_iarchive, IArchive>::value>>
    Foo( IArchive & ar ) {
        ar >> data;
        // populate "data" by reading the archive
    }
    ...    
};

int main(int argc, const char *argv[])
{
    // deserialize
    boost::archive::text_iarchive iar(std::cin); 
    Foo foo(iar); // will also work with other archives
}

As for what happens when your data is not default constructive see the discussion above.