Opened 7 years ago

Closed 7 years ago

#11342 closed Bugs (fixed)

Vector serialization with non-default-constructible element type fails to compile

Reported by: bugs@… Owned by: Robert Ramey
Milestone: To Be Determined Component: serialization
Version: Boost 1.58.0 Severity: Regression
Keywords: Cc:

Description

The following sample compiles in Boost 1.57.0 but doesn't in Boost 1.58.0:

#include <boost/serialization/nvp.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>

class Foo{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int)
    {
        ar & BOOST_SERIALIZATION_NVP(i);
    }
    int i;
    Foo():i(0){}
public:
    Foo(int k):i(k){}
};

int main(int argc, char *argv[])
{
    std::vector< Foo> f;
    f.push_back(Foo(12));
    std::ofstream os("path");
    boost::archive::xml_oarchive oa(os);
    oa << boost::serialization::make_nvp("f", f);
    os.close();
    std::vector<Foo> g;
    std::ifstream is("path");
    boost::archive::xml_iarchive ia(is);
    ia >> boost::serialization::make_nvp("f", g);
}

The problem is a new piece of code that tries to switch on is_default_constructible<T> *at compile time*. That can't work: (from serialization/vector.hpp in the unoptimized version of load(...)):

if(detail::is_default_constructible<U>()){
    t.resize(count);
    // ... snip ... 
}
else{
    t.reserve(count);
    // ... snip ... 
}

Instead of having the branches in the same flow code, it should be dispatched so that only the applicable branch is instantiated. I tested with this simplistic approach:

namespace sehe_bugfix {
    template<class Archive, class U, class Allocator>
    inline void load_elements(
        Archive & ar,
        std::vector<U, Allocator> &t,
        const unsigned int /* file_version */,
        collection_size_type count,
        mpl::true_
    ){
        const boost::archive::library_version_type library_version(
            ar.get_library_version()
        );
        item_version_type item_version(0);
        if(boost::archive::library_version_type(3) < library_version){
            ar >> BOOST_SERIALIZATION_NVP(item_version);
        }
        t.resize(count);
        typename std::vector<U, Allocator>::iterator hint;
        hint = t.begin();
        while(count-- > 0){
            ar >> boost::serialization::make_nvp("item", *hint++);
        }
    }

    template<class Archive, class U, class Allocator>
    inline void load_elements(
        Archive & ar,
        std::vector<U, Allocator> &t,
        const unsigned int /* file_version */,
        collection_size_type count,
        mpl::false_
    ){
        const boost::archive::library_version_type library_version(
            ar.get_library_version()
        );
        item_version_type item_version(0);
        if(boost::archive::library_version_type(3) < library_version){
            ar >> BOOST_SERIALIZATION_NVP(item_version);
        }
        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());
        }
    }
}

template<class Archive, class U, class Allocator>
inline void load(
    Archive & ar,
    std::vector<U, Allocator> &t,
    const unsigned int file_version,
    mpl::false_
){
    const boost::archive::library_version_type library_version(
        ar.get_library_version()
    );
    // retrieve number of elements
    item_version_type item_version(0);
    collection_size_type count;
    ar >> BOOST_SERIALIZATION_NVP(count);

    sehe_bugfix::load_elements(ar, t, file_version, count, detail::is_default_constructible<U>());
}

Change History (2)

comment:1 by bugs@…, 7 years ago

PS. This was in reference to this SO post: http://stackoverflow.com/a/30437359/85371

comment:2 by Robert Ramey, 7 years ago

Resolution: fixed
Status: newclosed

The fix for this has been checked into the develop and master branch and will appear in the next release. If you can't wait for that, you can clone/download the latest versions from github.

Note: See TracTickets for help on using tickets.