2

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::vectors 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.

1 Answer 1

1

There are two major things missing:

  1. 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); } 
  2. 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) 
  3. 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:

  • I'm not sure what the friends are for. This could be my character flaw
  • Consider moving things into namespaces so e.g. ::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.]

Now It Works

Live On Wandbox

#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 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that works! If you want to complete your answer: if the InputArchive is in a namespace (I removed the namespace in the code I posted), then the two boost macros should be called outside of the namespace (and be passed ns::InputArchive, where ns is the namespace).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.