Exception when using boost::serialization and binary format

763 views Asked by At

I'm trying to implement lazy reading through boost::serialization. It was easy to save/load std::vector, but that require reading the whole vector in one go, which wasn't really feasable when having a large number of objects.

My idea was to store the object count as the first object in the archive and then just keep adding geopoints at the end of the file. If you have a better idea I'm all ears.

The writer class appear to work and the reader get the right value as count in the constructor, but then it crashes when trying to read a GeoPoint using operator>>, on this line:

archive >>p;

With the output:

Stream writing 1.25, 3.2
Stream writing 2.5, 6.4
Stream writing 3.75, 9.6
terminate called after throwing an instance of 'boost::archive::archive_exception'
  what():  input stream error

Here's an SSSC illustrating my problem:

#include <fstream>
#include <iostream>
#include <stdint.h>

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/version.hpp>

using namespace std;

struct GeoPoint {
    GeoPoint() : lon(0), lat(0) {}
    GeoPoint(float _lon, float _lat) : lon(_lon), lat(_lat) {}
    float lon;
    float lat;
};

namespace boost {
    namespace serialization {
        template<class Archive>
        void serialize(Archive& ar, GeoPoint& point, const unsigned version) {
            if (version == 1) {
                ar & point.lon;
                ar & point.lat;
            }
        }
    } //serialization
} //boost
BOOST_CLASS_VERSION(GeoPoint, 1);

class PointReader {
public:
    PointReader(const std::string& path)
        : stream(path.c_str(), ios::in), archive(stream), read(0) {
        archive >>count; //count as the first object in the archive
    }
    PointReader& operator >>(GeoPoint& p) {
        archive >>p; //Crash and then the BT goes into boost files
        ++read;
        return *this;
    }
    bool is_good() {
        return read < count;
    }
private:
    uint32_t read;
    uint32_t count;
    std::ifstream stream;
    boost::archive::binary_iarchive archive;
};

class PointWriter {
public:
    PointWriter(const std::string& path)
        : filepath(path), stream(path.c_str(), ios::out), archive(stream), count(0) { }

    ~PointWriter() {
        if(stream.is_open())
            close();
    }
    void close() {
        stream.close();
        stream.open(filepath.c_str(), ios::out);
        boost::archive::binary_oarchive out(stream);
        out <<count; //write new count as the first object in the archive
        stream.close();
    }
    PointWriter& operator <<(const GeoPoint& p) {
        archive <<p;
        ++count;
        return *this;
    }
private:
    std::string filepath;
    uint32_t count;
    std::ofstream stream;
    boost::archive::binary_oarchive archive;
};

int main(int argc, char** argv) {
    PointWriter streamw("foo2.data");
    for(int a = 1; a < 4; ++a) {
        GeoPoint p;
        p.lon = float(a*1.25f);
        p.lat = float(a*3.2f);
        cout <<"Stream writing " <<p.lon <<", " <<p.lat <<endl;
        streamw <<p;
    }
    streamw.close();

    PointReader streamr("foo2.data");
    uint32_t loops = 0;
    while(streamr.is_good()) {
        GeoPoint p;
        streamr >>p; //Crash
        cout <<"Streamr read " <<p.lon <<", " <<p.lat <<endl;
        if (++loops > 10)
            break;
    }
    return 0;
}

Assuming you have boost installed in /usr/include you can build it using this command:

g++ -o sssc sssc.cpp -I/usr/include/ -lboost_serialization
0

There are 0 answers