Reputation: 7258
I'm working with boost serialization and I have written a custom input archive that derives from boost's binary input archive. Event though this class is for now doing exactly the same as boost's binary_iarchive, I get a boost::archive::archive_exception
when deserializing std::vector
s of primitive types. I don't get any issues serializing, e.g. an std::string
, or an std::vector<std::string>
.
Here is the full code:
The InputArchive implementation:
#ifndef __INPUT_ARCHIVE_H
#define __INPUT_ARCHIVE_H
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/impl/basic_binary_iprimitive.ipp>
#include <boost/archive/impl/basic_binary_iarchive.ipp>
class InputArchive;
using iarchive = boost::archive::binary_iarchive_impl<
InputArchive,
std::istream::char_type,
std::istream::traits_type>;
class InputArchive : public iarchive {
friend class boost::archive::detail::interface_iarchive<InputArchive>;
friend class boost::archive::basic_binary_iarchive<InputArchive>;
friend class boost::archive::load_access;
public:
template<typename ... Args>
InputArchive(Args&& ... args)
: iarchive(std::forward<Args>(args)..., boost::archive::archive_flags::no_header)
{}
};
#endif
Dummy class, for testing purpose:
#ifndef __DUMMY_PRODUCT_H
#define __DUMMY_PRODUCT_H
#include <vector>
#include <boost/serialization/vector.hpp>
#include <string>
#include <boost/serialization/string.hpp>
struct dummy_product {
std::vector<char> data;
template<typename A>
void serialize(A& ar, const unsigned int version) {
ar & data;
}
};
#endif
And example code using the archive:
#include <boost/archive/binary_oarchive.hpp>
#include "InputArchive.hpp"
#include "DummyProduct.hpp"
#include <string>
#include <sstream>
#include <iostream>
int main() {
dummy_product p;
p.data.resize(16);
std::string buffer;
{
std::stringstream ss_value;
boost::archive::binary_oarchive oa(ss_value, boost::archive::archive_flags::no_header);
oa << p;
buffer = ss_value.str();
}
for(auto i : buffer) {
std::cout << (int)i << " ";
}
std::cout << std::endl;
{
std::stringstream ss_value(buffer);
//boost::archive::binary_iarchive ia(ss_value, boost::archive::archive_flags::no_header);
InputArchive ia(ss_value);
ia >> p;
}
}
This code throws the following exception:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): input stream error
As you can see, the InputArchive simply inherits from the appropriate boost::archive::binary_iarchive_impl
and does nothing more. If I replace the InputArchive with a boost::archive::binary_iarchive
there is no issue. If I use std::string
instead of std::vector<char>
in dummy_product
there is no issue either. The exception seems to be happening only for std::vector
of primitive types.
Any idea where this problem comes from? I'm using boost 1.75.0 and gcc 8.3.0.
Upvotes: 2
Views: 520
Reputation: 393064
There are two major things missing:
Q. As you can see, the InputArchive simply inherits from the appropriate boost::archive::binary_iarchive_impl and does nothing more.
A. Yeah, but not in the appropriate way. Your constructor forgets to initialize the archive. Just forwarding to the base class constructor is half the work:
enum {
forced_flags = boost::archive::archive_flags::no_header
};
InputArchive(std::istream & is, unsigned int flags = 0) :
iarchive(is, flags | forced_flags)
{
init(flags | forced_flags);
}
InputArchive(std::streambuf & bsb, unsigned int flags = 0) :
iarchive(bsb, flags | forced_flags)
{
init(flags | forced_flags);
}
Next up, there is an optimization for primitive types for archives that allow bitwise binary output. Yours is one of them, so you need tell Boost about it:
BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(InputArchive)
To be really cool, you probably also want to correctly have type inforation for polymorphic types registered with your archive type. If you do, you want to tell Boost about it as well:
BOOST_SERIALIZATION_REGISTER_ARCHIVE(InputArchive)
Loose notes:
friends
are for. This could be my character flaw::iarchive
isn't polluting the global namespace for no reason. [Also, consider just going with CRTP and spelling the name out inside the class definition.]#ifndef __INPUT_ARCHIVE_H
#define __INPUT_ARCHIVE_H
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/impl/basic_binary_iprimitive.ipp>
#include <boost/archive/impl/basic_binary_iarchive.ipp>
class InputArchive;
using iarchive = boost::archive::binary_iarchive_impl<
InputArchive,
std::istream::char_type,
std::istream::traits_type>;
class InputArchive : public iarchive {
enum {
forced_flags = boost::archive::archive_flags::no_header
};
//friend class boost::archive::detail::interface_iarchive<InputArchive>;
//friend class boost::archive::basic_binary_iarchive<InputArchive>;
//friend class boost::archive::load_access;
public:
InputArchive(std::istream & is, unsigned int flags = 0) :
iarchive(is, flags | forced_flags)
{
init(flags | forced_flags);
}
InputArchive(std::streambuf & bsb, unsigned int flags = 0) :
iarchive(bsb, flags | forced_flags)
{
init(flags | forced_flags);
}
};
// required by export
BOOST_SERIALIZATION_REGISTER_ARCHIVE(InputArchive)
BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(InputArchive)
#endif
#ifndef __DUMMY_PRODUCT_H
#define __DUMMY_PRODUCT_H
#include <vector>
#include <boost/serialization/vector.hpp>
#include <string>
#include <boost/serialization/string.hpp>
struct dummy_product {
std::vector<char> data;
template<typename A>
void serialize(A& ar, unsigned) {
ar & data;
}
};
#endif
#include <boost/archive/binary_oarchive.hpp>
//#include "InputArchive.hpp"
//#include "DummyProduct.hpp"
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
int main() {
dummy_product p;
p.data.resize(16);
std::string buffer;
{
std::stringstream ss_value;
boost::archive::binary_oarchive oa(ss_value, boost::archive::archive_flags::no_header);
oa << p;
buffer = ss_value.str();
}
for(int i : buffer) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)i << " ";
}
std::cout << std::endl;
{
std::stringstream ss_value(buffer);
// boost::archive::binary_iarchive ia(ss_value, boost::archive::archive_flags::no_header);
InputArchive ia(ss_value);
ia >> p;
}
}
Prints
00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Upvotes: 1