Reputation: 1372
I'm trying to serialize an object using boost serialization. Everything works perfectly if I use a text_archive, I can serialize, deserialize back, and everything is in its place. I tried to serialize with a binary archive, with mixed result. If I use a boost array_sink, connected to a char buffer, everything works smoothly. If I change to a dynamically allocated buffer, the serialization fails, and the corresponding vector is left empty. I found many examples of people using a iostream with back inserter, and nobody had such a problem. Here is an example that give me the error:
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>
#include <cassert>
#include <iostream>
class MyClass
{
public:
MyClass(int myVar) : _myVar(myVar) {}
MyClass() {}
int _myVar = 0;
friend bool operator== (const MyClass& c1, const MyClass& c2)
{
return c1._myVar == c2._myVar;
}
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int/* version*/)
{
ar & _myVar;
}
};
int main(int, char**)
{
std::shared_ptr<MyClass> srcPtr(new MyClass(42));
std::shared_ptr<MyClass> dstPtr;
std::cout << "Serialize using stringstream and text archive" << std::endl;
std::stringstream oss;
boost::archive::text_oarchive toa(oss);
toa << srcPtr;
std::cout << oss.str() << std::endl;
std::stringstream iss(oss.str());
boost::archive::text_iarchive tia(iss);
tia >> dstPtr;
assert(*srcPtr == *dstPtr);
std::cout << "Dest var is: " << dstPtr-> _myVar << std::endl;
dstPtr.reset();
std::cout << "Done" << std::endl;
//Static buffer and binary
std::cout << "Serialize using static buffer and binary archive" << std::endl;
char testBuffer[4096];
boost::iostreams::stream<boost::iostreams::array_sink> static_os(testBuffer);
boost::archive::binary_oarchive static_oa(static_os);
static_oa << srcPtr;
std::cout << "Static buffer, buffer size: " << static_os.tellp() << std::endl;
boost::iostreams::stream<boost::iostreams::array_source> static_is(testBuffer);
boost::archive::binary_iarchive static_ia(static_is);
static_ia >> dstPtr;
assert(*srcPtr == *dstPtr);
std::cout << "Dest var is: " << dstPtr-> _myVar << std::endl;
dstPtr.reset();
std::cout << "Done" << std::endl;
//Dynamic vector
std::cout << "Serialize using dynamic buffer and binary archive" << std::endl;
std::vector<char> buffer;
boost::iostreams::stream obs(boost::iostreams::back_inserter(buffer));
boost::archive::binary_oarchive oba(obs);
oba << srcPtr;
std::cout << "BufferSize: " << buffer.size() << std::endl;
boost::iostreams::stream<boost::iostreams::array_source> ibs(buffer.data(),buffer.size());
boost::archive::binary_iarchive iba(ibs);
iba >> dstPtr;
assert(*srcPtr == *dstPtr);
std::cout << "Dest var is: " << dstPtr-> _myVar << std::endl;
dstPtr.reset();
std::cout << "Done" << std::endl;
}
The output is
Serialize using stringstream and text archive
22 serialization::archive 18 0 1 1 1 0
0 42
Dest var is: 42
Done
Serialize using static buffer and binary archive
Static buffer, buffer size: 60
Dest var is: 42
Done
Serialize using dynamic buffer and binary archive
BufferSize: 0
terminate called after throwing an instance of 'boost::wrapexcept<std::ios_base::failure[abi:cxx11]>'
what(): no read access: iostream error
Aborted (core dumped)
Upvotes: 1
Views: 41
Reputation: 393239
Like I commented earlier, this is a bit of a pitfall. SOme archive types need completion in the archive destructor (think e.g. of XML archives).
On a lot of archive types a simple flush may seem to suffice. In fact, in text archives the newlines will typically cause the implicit flush on the underlying OS text stream implementation.
However, to be correct you need to complete the archive before consuming the stream content.
Here’s my simplified version that demonstrates all three:
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>
#include <cassert>
#include <iostream>
struct MyClass {
MyClass(int myVar=0) : _myVar(myVar) {}
bool operator== (const MyClass& o) const { return _myVar == o._myVar; }
private:
int _myVar = 0;
friend class boost::serialization::access;
void serialize (auto &ar, unsigned) { ar & _myVar; }
};
namespace io = boost::iostreams;
namespace ba = boost::archive;
int main() {
std::shared_ptr<MyClass const> srcPtr(new MyClass(42));
std::cout << "Serialize using stringstream and text archive" << std::endl;
std::stringstream oss;
{
ba::text_oarchive toa (oss);
toa << srcPtr;
}
std::cout << "Serialized string is " << oss.tellp () << " bytes" << std::endl;
std::stringstream iss(oss.str());
{
ba::text_iarchive tia (iss);
std::shared_ptr<MyClass> dstPtr;
tia >> dstPtr;
assert (*srcPtr == *dstPtr);
}
std::cout << "Done" << std::endl;
//Static buffer and binary
std::cout << "Serialize using static buffer and binary archive" << std::endl;
char testBuffer[4096];
{
io::stream<io::array_sink> static_os (testBuffer);
ba::binary_oarchive static_oa (static_os);
static_oa << srcPtr;
std::cout << "Static buffer, buffer size: " << static_os.tellp() << std::endl;
}
{
io::stream<io::array_source> static_is (testBuffer);
ba::binary_iarchive static_ia (static_is);
std::shared_ptr<MyClass> dstPtr;
static_ia >> dstPtr;
assert (*srcPtr == *dstPtr);
}
std::cout << "Done" << std::endl;
//Dynamic vector
std::cout << "Serialize using dynamic buffer and binary archive" << std::endl;
std::vector<char> buffer;
{
io::stream obs (io::back_inserter (buffer));
ba::binary_oarchive oba (obs);
oba << srcPtr;
}
std::cout << "BufferSize: " << buffer.size() << std::endl;
{
io::stream<io::array_source> ibs (buffer.data (), buffer.size ());
ba::binary_iarchive iba (ibs);
std::shared_ptr<MyClass> dstPtr;
iba >> dstPtr;
assert (*srcPtr == *dstPtr);
}
std::cout << "Done" << std::endl;
}
Printing
Serialize using stringstream and text archive
Serialized string is 44 bytes
Done
Serialize using static buffer and binary archive
Static buffer, buffer size: 60
Done
Serialize using dynamic buffer and binary archive
BufferSize: 60
Done
Upvotes: 0