Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
184 views
in Technique[技术] by (71.8m points)

c++ - Deserializing constructor doesn't read data correctly

I am trying to deserialize an object that does not have a default constructor. I've seen that you can do this by passing an archive to a constructor. However, when I do this it does not seem to read the data correctly? Here is an example - Works() outputs "1 2" as it should (using a default constructor and the operator>>), but DoesntWork() outputs "0 0". I've stepped through and everything seems to be getting called appropriately. Can anyone explain the difference between these two functions?

#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;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You shouldn't call serialize methods directly: operator >> for an archive does way more than just calling serialize; depending on the type of archive it first needs to load a preamble etc. You can verify this by stepping through with the debugger, or by checking what's inside test.archive, it is something like

22 serialization::archive 12 0 0 1.000000000e+000 2.000000000e+000

so right after constructing a text_iarchive the first two calls to operator & will happen to see those 2 0's in there instead of the actual data.

Your constructor should be:

template<class TArchive>
Point(TArchive& archive)
{
  archive >> *this;
}

Edit here's an example of how to use SFINAE to make sure the copy constructor can still be invoked

Point( const Point& rh ) :
  mX( rh.mX ),
  mY( rh.mY )
{
}

template<class TArchive>
Point( TArchive& archive,
       std::enable_if_t< !std::is_same< TArchive, Point >::value >* = nullptr )
{
  archive >> *this;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...