c++serializationboostboost-serialization

Input stream problem when using std::array


When there is no bool b in struct A, the code works. When bool b is there, ar & mat gives "input stream error" but registering elements of std::array one by one works. What's wrong here?

#include <fstream>
#include <boost/serialization/array.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

struct A
{
    std::array<int, 3> mat;
    bool b;

    template<class Archive> void serialize(Archive& ar, const unsigned int version)
    {
        //ar & mat[0];
        //ar & mat[1];
        //ar & mat[2];
        ar & mat;
        ar & b;
    }
};


int main()
{
    {
        std::string fname = "save.sr";
        std::ofstream ofs(fname);
        boost::archive::text_oarchive oa(ofs);
        A a;
        oa << a;
    }

    {
        std::string fname = "save.sr";
        std::ifstream ifs(fname);
        boost::archive::text_iarchive ia(ifs);
        A a;
        ia >> a;
    }

    return 0;
}

Solution

  • I didn't see what could be wrong. Then I looked with asan/ubsan and found domain conversion issues: http://coliru.stacked-crooked.com/a/edee75a22450d9ee

    At first I thought maybe there would be a bug in a specific version of Boost[¹]. Then it dawned on me.

    There are benign warnings:

    g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp -fsanitize=address,undefined -lboost_serialization && ./a.out
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct extended_type_info_typeid'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct oserializer'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct extended_type_info_typeid'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct iserializer'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct extended_type_info_typeid'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct extended_type_info_typeid'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct oserializer'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct extended_type_info_typeid'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct iserializer'
    /usr/local/include/boost/serialization/singleton.hpp:181:12: runtime error: reference binding to null pointer of type 'const struct extended_type_info_typeid'
    

    And then we got the domain error:

    /usr/local/include/boost/archive/text_oarchive.hpp:65:50: runtime error: load of value 255, which is not a valid value for type 'bool'

    Notice how it is the oarchive that runs into them, and confirm:

    debug: "22 serialization::archive 17 0 0 0 0 3 -2049146224 32767 -2049145920 1
    "
    

    Those are... indeterminate values. Not a big problem for a complete integral domain, but it is for bool. So, fix your initialization:

    struct A {
        std::array<int, 3> mat {};
        bool b {};
    
        template <class Ar> void serialize(Ar& ar, unsigned) {
            ar & mat & b;
        }
    };
    

    Of course, you can use other styles:

    std::array<int, 3> mat = {0,0,0};
    bool b = false;
    

    Or the old-fashioned constructor:

    std::array<int, 3> mat;
    bool b;
    
    A(std::array<int, 3> mat = {}, bool b = {}) : mat(mat), b(b) {}
    

    Etc. as long as you make sure you initialize your member data.

    Fixed On COliru

    #include <boost/archive/text_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/serialization/array.hpp>
    #include <iomanip>
    #include <iostream>
    #include <sstream>
    #include <boost/version.hpp>
    
    struct A {
        std::array<int, 3> mat = {0,0,0};
        bool b = false;
    
        template <class Ar> void serialize(Ar& ar, unsigned) {
            ar & mat & b;
        }
    };
    
    int main() {
        std::stringstream ss;
        {
            boost::archive::text_oarchive oa(ss);
            A a;
            oa << a;
        }
    
        std::cout << "BOOST_VERSION: " << BOOST_VERSION << "\n";
        std::cout << "debug: " << std::quoted(ss.str()) << "\n";
    
        {
            boost::archive::text_iarchive ia(ss);
            A a;
            ia >> a;
        }
    }
    

    Prints

    BOOST_VERSION: 107500
    debug: "22 serialization::archive 18 0 0 0 0 3 0 0 0 0
    "
    

    [¹] This is what a wild goose chase looks like