c++boostboost-serialization

Input stream error when deserializing with boost


I'm trying to use the boost serialization library to serialize a class. The serializing works just fine but when I try to read the contents that were wrote to a file a get an "input error" exception.

I read the Documentation of the boost serialization library and came up with the (1) to understand how to serialize and deserialize a class with non-default constructors. I was trying to keep as close to the example from the documentation as I could.

I tried removing ar & name from the serialize method (which the documentation example did) in the Book class which also resulted in an input stream error.

When debugging I recognized that the exception gets thrown in the load _access class in the load_primitive` function.

Source Code (1)

#include <fstream>
#include <filesystem>

#include <boost/serialization/access.hpp>
#include <boost/serialization/string.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

class Book 
{
private:


    std::string name;

    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive& ar, const unsigned int file_version)
    {
        ar & name;
    }

public:

    Book(std::string name): name(name) {}

};

namespace boost {
namespace serialization {

    template<class Archive>
    inline void save_construct_data(Archive& ar, Book* book, const unsigned int file_version)
    {
        ar << book->name;
    }

    template<class Archive>
    inline void load_construct_data(Archive& ar, Book* book, const unsigned int file_version)
    {
        std::string name;
        ar >> name;
        ::new(book)Book(name);
    }
}
}

int main() 
{
    Book book("Harry Potter");

    std::ofstream ofs("Book.txt");
    boost::archive::text_oarchive oa(ofs);
    oa << &book;
    ofs.close();

    Book* read;
    std::ifstream ifs("Book.txt", std::ios::binary);
    boost::archive::text_iarchive ia(ifs);
    ia >> read;
    ifs.close();
}

I am getting the following error

terminate called after throwing an instance of 'boost::archive::archive_exception'
  what():  input stream error

Thanks in advance for any awnser. Felix


Solution

  • save_construct_data() and load_construct_data() should work together, but in your code save_construct_data() is never called. This happens because it has incorrect signature: Book* should be const Book*.

    You then get a stream error because serialization and deserialization are inconsistent with each other – serialization writes Book::name once, in serialize(), but deserialization tries to read it twice, in load_construct_data() and in serialize().

    Complete working example (main() is unchanged, except removing std::ios::binary):

    class Book {
        friend class boost::serialization::access;
    
    private:
        std::string name;
    
        template<class Archive>
        void serialize(Archive&, unsigned int) { }
    
        template<class Archive>
        friend void save_construct_data(Archive& ar, const Book* book, unsigned int) {
            ar << book->name;
        }
    
        template<class Archive>
        friend void load_construct_data(Archive& ar, Book* book, unsigned int) {
            std::string name;
            ar >> name;
            ::new(book) Book(std::move(name));
        }
    
    public:
        Book(std::string name): name(std::move(name)) { }
    };
    

    I declared save_construct_data() as a friend inside the class to simplify code. For some reason, if it is defined in boost::serialization namespace, you need to add a friend declaration for that function in addition to befriending boost::serialization::access. This is not mentioned in the documentation, but code doesn't compile without that friend declaration.