Search code examples
c++vectorboost-serialization

boost vector serialization append issue


Consider the following example:

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


#include <iostream>
#include <sstream>

using namespace boost;
using namespace std;


int main()
{
    std::ostringstream oss;
    boost::archive::text_oarchive oa(oss);



    vector<int> src1 = {1}, src2 = {2};

    vector<int> dst;

    oa << src1;
    oa << src2;

    string serialized = oss.str();
    std::istringstream iss(serialized);
    boost::archive::text_iarchive ia(iss);


    ia >> dst;

    cout << "size=" << dst.size() << endl;

    ia >> dst;
    cout << "size=" << dst.size() << endl;
}

Output:
size=1
size=1

Everything is fine. However if change vector<int> to vector<string>:

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


#include <iostream>
#include <sstream>

using namespace boost;
using namespace std;


int main()
{
    std::ostringstream oss;
    boost::archive::text_oarchive oa(oss);


    vector<string> src1 = {"hi"}, src2 = {"hi"};

    vector<string> dst;


    oa << src1;
    oa << src2;

    string serialized = oss.str();
    std::istringstream iss(serialized);
    boost::archive::text_iarchive ia(iss);


    ia >> dst;

    cout << "size=" << dst.size() << endl;

    ia >> dst;
    cout << "size=" << dst.size() << endl;
}

Output:
size=1
size=2

Result is appended to dst, not assigned. Why? How "assign semantic" can be achieved?

compiler: gcc version 5.2.1 20151010 (Ubuntu 5.2.1-22ubuntu2)
compilation: g++ -std=c++11 test2.cc -lboost_serialization


Solution

  • There is following code at sources of boost 1.58.

    if(detail::is_default_constructible<U>()){
        t.resize(count);
        typename std::vector<U, Allocator>::iterator hint;
        hint = t.begin();
        while(count-- > 0){
            ar >> boost::serialization::make_nvp("item", *hint++);
        }
    }
    else{
        t.reserve(count);
        while(count-- > 0){
            detail::stack_construct<Archive, U> u(ar, item_version);
            ar >> boost::serialization::make_nvp("item", u.reference());
            t.push_back(u.reference());
            ar.reset_object_address(& t.back() , & u.reference());
         }
    }
    

    And detail::is_default_constructible returns false for std::string (for some reasons boost::has_trivial_constructor is used) and true for int. So, when U is int, resize will be called and then value assignment will be used, but when U is std::string reserve will be used (reserve don't actually resize container) and then push_back will be used.

    Simple workaround will be just clear vector before second read from archive.

    ia >> dst;
    
    cout << "size=" << dst.size() << endl;
    
    dst.clear();
    ia >> dst;
    cout << "size=" << dst.size() << endl;