Tavis Bones
Tavis Bones

Reputation: 255

Boost Serialization multiple objects

I am serializing a class with boost using binary. I'm using ios::append in order to keep appending multiple objects to this file. How do I go about retrieving all of the objects that are stored?

Here is my test class which tries a multiple serialize and retrieve them. I've comment the failure point where I don't get correct data.

using namespace std;
 class Data {
 public:
double get_latitude() const {
    return _latitude;
}

double get_longitude() const {
    return _longitude;
}

void set_latitude(double _latitude) {
    this->_latitude = _latitude;
}

void set_longitude(double _longitude) {
    this->_longitude = _longitude;
}
private:
double _latitude;
double _longitude;
friend class boost::serialization::access;
// When the class Archive corresponds to an output archive, the
// & operator is defined similar to <<.  Likewise, when the class Archive
// is a type of input archive the & operator is defined similar to >>.
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
    ar & _latitude;
    ar & _longitude;
}
};

class DataTest: public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE( DataTest);
CPPUNIT_TEST(testMultipleSaveData);
CPPUNIT_TEST_SUITE_END();

public:

void testMultipleSaveData() {
    Data data;
    data.set_latitude(1.0);
    data.set_longitude(2.0);
    saveData(data);
    Data secondData;
    secondData.set_latitude(5.0);
    secondData.set_longitude(6.0);
    saveData(secondData);
    Data returnData;
    Data return2Data;
    {
        // create and open an archive for input
        std::ifstream ifs("data.dat", ios::binary);
        boost::archive::binary_iarchive ia(ifs);
        // read class state from archive
        ia >> returnData;
        ia >> return2Data;
        // archive and stream closed when destructors are called
    }
    CPPUNIT_ASSERT_EQUAL(data.get_latitude(), returnData.get_latitude());
    CPPUNIT_ASSERT_EQUAL(data.get_longitude(), returnData.get_longitude());
    //Failure on next line
    CPPUNIT_ASSERT_EQUAL(secondData.get_latitude(), return2Data.get_latitude());
    CPPUNIT_ASSERT_EQUAL(secondData.get_longitude(), return2Data.get_longitude());
}

void saveData(Data data) {
    std::ofstream ofs("data.dat", ios::binary | ios::app);
    boost::archive::binary_oarchive oa(ofs);
    oa << data;
}

 };
 CPPUNIT_TEST_SUITE_REGISTRATION( DataTest);

Upvotes: 9

Views: 6052

Answers (3)

Andriy Tylychko
Andriy Tylychko

Reputation: 16256

Decided to add another answer to avoid complete mess.

Your problem is that you serialize to separate instances of boost::archive::binary_oarchive. Boost Archive stores some internal info at the beginning of the file (I won't call it a header because Boost Serialization has separate header to store version number), and you receive two copies of this info, at the beginning of the file and between your Data serializations.

Boost Archive wasn't designed for such usage. Even specifying boost::archive::no_header like in:

boost::archive::text_oarchive oa(ofs, boost::archive::no_header);

doesn't help, because this option configures another header, that contains version number. You need to serialize to the same instance of Boost Archive.

Upvotes: 8

Andriy Tylychko
Andriy Tylychko

Reputation: 16256

1) if the number of objects stored is constant - load all of them in the same order you stored them:

storing:

std::ofstream ofs(FILENAME, ios::app);
boost::archive::text_oarchive oa(ofs);
oa << data1 << data2 << ... << data_n;

loading:

std::ifstream ifs(FILENAME);
boost::archive::text_iarchive ia(ifs);
ia >> data1 >> data2 >> ... >> data_n;

2) if the number of objects stored can vary - serialize the number:

storing:

std::ofstream ofs(FILENAME, ios::app);
boost::archive::text_oarchive oa(ofs);
oa << number_of_objects;
for (size_t i = 0; i != number_of_objects; ++i)
    oa << data[i];

loading:

std::ifstream ifs(FILENAME);
boost::archive::text_iarchive ia(ifs);
size_t number_of_objects;
ia >> number_of_objects;
// allocate number_of_objects objects
for (size_t i = 0; i != number_of_objects; ++i)
    ia >> data[i];

[EDIT] taking into account your edited question:
first of all, you use binary_archive with text-mode files! you need to open the file (for both reading and writing) with std::ios::binary flag. this is a bug and can be a reason of your problem ("can be" because we don't see your exact serialization implementation).

I cannot compile your code because it's incomplete. try to implement simple class with serialization and store/load 2 objects on this simple example to easily find what wrong with more complicated cases

[EDIT2] did you take binary_archive from Boost.Serialization examples? if so be aware it's not portable, I don't know if this matters for you

Upvotes: 3

Edward Strange
Edward Strange

Reputation: 40849

If you want to read more than one, then tell it to load more than one. Serialization only knows to load multiple objects when you've told it to either explicitly or by serializing a container of objects.

Upvotes: 1

Related Questions