Construct object from boost serialization archive
Asked Answered
C

2

1

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;
}
Calyces answered 6/3, 2012 at 18:26 Comment(9)
yes, why not? just add "ar >> data;" in the constructor and you are done. (The real challenge would be if 'data' were const)Susi
@Susi What would you do if 'data' doesn't have a default constructor?Rochette
@DavidDoria, as Alexander Stepanov says, "if you don't make your classes default constructible then you get what you deserve". All classes should have a default constructor, if not (and you can't control that) you have to go around this problem at the level of Foo( Archive & ar ) : data(something) { or have a level of indirection as Foot(Archive & ar) : data(somefunction(ar)){ (but something is smelly in the first place).Susi
@Susi How are default constructors a good thing? It just explodes the number of states that your object can be in (most of them bad/invalid that you have to watch out for), no?Rochette
@DavidDoria, you are confusing default constructor with uninitialized variables. A default contructor is supposed to leave your object in a valid (and if possible predictable state).Susi
@Susi Say you have a LineSegment class - you are suggesting it is better to give it two arbitrary points (say (0,0) and (1,1)) rather than disallow LineSegments without intentionally set points?Rochette
@DavidDoria, I don't deny you have a point. I strugled with this for a long time myself. In all cases I found that it is very useful to have a default object and (depending on the operations allow on that object) there is usually a clear choice. What about a empty LineSegment (let's say from (0,0) to (0,0))?Susi
@Susi (sorry to steer these comments way off topic - should we delete them?) Then functions like Vector LineSegment::direction() wouldn't make sense (what is the direction from a point to the same point?) This is just a trivial example class so no use dwelling on it, but I think it makes the point that sometimes an object (in this case, a representation of a physical thing) doesn't make sense unless it is initialized with meaningful values, and it seems to make sense to differentiate between "this valid object is meaningful" and "this other valid object is nonsense".Rochette
@DavidDoria, I think it is not off-topic because this problem is at the core of the philosophy of serialization library. I always found "excuses" for not having a default constructor, and at the end they create more problems than what they solve. You have to deal with the problem of underfined direction() in any case, regardless of which is the default object. Of course if you consider "an empty LineSegement is not a valid segment" then it cannot be the default. However this opens a can of worms because your LineSegment space is not going to be a vector space.Susi
R
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;
}
Rochette answered 27/2, 2016 at 0:25 Comment(0)
S
0

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.

Susi answered 30/3, 2016 at 0:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.