0

I have recently been using save_construct_data() and load_construct_data() when I need to serialize an object without a default constructor. Since it doesn't make sense to do:

MyObject a; // can't do this because there is no default constructor
archive >> a;

we must do:

MyObject* aPointer;
archive >> a;

which calls load_construct_data() before serialize(). However, of course this only works if the object was serialized using save_constructor_data() which is only called if it is written as a pointer, e.g.

MyObject a(1,2);
MyObject aPointer = &a;
archive << aPointer;

This is all working fine, but it seems like archive << a; works fine, but logically doesn't make sense, as it will never be able to be deserialized. Is there a way to disallow this call so that objects (perhaps class members of a larger class, etc.) don't accidentally write the Object not through a pointer?

------------- EDIT ----------

Attempting to follow SergeyA's suggestion, I have made the following demo. Unfortunately it does not seem to read the data correctly?

#include <fstream>

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

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

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

public:
    template<class TArchive>
    Point(TArchive& archive)
    {
        serialize(archive, 0);
    }

    Point(){} // Only provided to test Works()

    Point(const float x, const float y) : mX(x), mY(y) { }

    float mX = 4;
    float mY = 5;
};

void Works()
{
    std::cout << "Works():" << std::endl;
    Point p(1,2);

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

    // read from a text archive
    std::ifstream inputStream("test.archive");
    boost::archive::text_iarchive inputArchive(inputStream);
    Point pointRead;
    inputArchive >> pointRead;

    std::cout << pointRead.mX << " " << pointRead.mY << std::endl;
}

void DoesntWork()
{
    std::cout << "DoesntWork():" << std::endl;
    Point p(1,2);

    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.mX << " " << pointRead.mY << std::endl;
}

int main()
{
    Works(); // Output "1 2"
    DoesntWork(); // Output "0 0"
    return 0;
}
David Doria
  • 9,873
  • 17
  • 85
  • 147
  • 1
    Wrong design. If you want to dissallow default objects, just imlement deserializating constructor. But do not introduce pointers when they are not needed! – SergeyA Feb 29 '16 at 16:02
  • You can probably use type traits, if you're at C++11: http://en.cppreference.com/w/cpp/types/is_default_constructible – Steve Feb 29 '16 at 16:50
  • @SergeyA What do you mean by "deserializing constructor"? – David Doria Feb 29 '16 at 17:56
  • It is a constructor which would take your 'archive' object and deserialize from it. – SergeyA Feb 29 '16 at 17:57
  • @SergeyA So then there is no need for the save/load_constructor_data() at all? – David Doria Feb 29 '16 at 17:59
  • @DavidDoria, I am not sure what is the significance of those functions. – SergeyA Feb 29 '16 at 18:04
  • @SergeyA And then what goes in my `serialize()` member function? If I have an archive and I want to `archive << myClass;` that is where `serialize()` would be called. I guess it would just be impossible to call `MyClass myClass; archive >> myClass;` because there is no default constructor, so I would just call `serialize(archive);` from the serializing constructor? – David Doria Feb 29 '16 at 19:07
  • @SergeyA This pattern makes sense, but it seems to write two extra zeros to the archive? See the edited question with an example. The output of Works() is "1 2", but the output of DoesntWork() is "0 0". Can you explain the difference in the two functions? – David Doria Mar 01 '16 at 02:40
  • I added a question with a more appropriate title: http://stackoverflow.com/questions/35722135/deserializing-construtor-doesnt-read-data-correctly The answer to the current question is, as SergeyA pointed out, "don't do this, and prefer a deserializing constructor" (once we get it to work :) ). – David Doria Mar 01 '16 at 11:51

0 Answers0