boost :: serialization:具有私有默认构造函数的对象在向量中工作,但不在地图中工作

时间:2015-05-24 17:55:21

标签: c++ boost stdmap boost-serialization

请考虑以下代码:

void

在序列化Foos矢量时可以正常工作。但是,如果我尝试序列化Foos的映射,它在私有默认构造函数上失败:

#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);
}

失败
std::map<std::string, Foo> f;
f.insert(std::make_pair("hello", Foo(12)));
std::ofstream os("path");
boost::archive::xml_oarchive oa(os);
oa << boost::serialization::make_nvp("f", f);
os.close();
std::map<std::string, Foo> g;
std::ifstream is("path");
boost::archive::xml_iarchive ia(is);
ia >> boost::serialization::make_nvp("f", g);

我正在使用clang Ubuntu clang版本3.4-1ubuntu3(标签/ RELEASE_34 / final)(基于LLVM 3.4)

并推出了Ubuntu 14.04LTS附带的1.55版本。

我尝试过提供load_construct_data()函数,如下所示:

In file included from main.cpp:2:
In file included from /usr/local/include/boost/serialization/nvp.hpp:19:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/utility:70:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_pair.h:109:18: error: field of type 'Foo' has private default constructor
      : first(), second() { }
                 ^
/usr/local/include/boost/serialization/access.hpp:132:17: note: in instantiation of member function 'std::pair<const std::basic_string<char>, Foo>::pair' requested here
        ::new(t)T;
                ^
/usr/local/include/boost/serialization/serialization.hpp:93:13: note: in instantiation of function template specialization 'boost::serialization::access::construct<std::pair<const std::basic_string<char>, Foo> >' requested here
    access::construct(t);
            ^
/usr/local/include/boost/serialization/serialization.hpp:158:9: note: in instantiation of function template specialization 'boost::serialization::load_construct_data<boost::archive::xml_iarchive, std::pair<const std::basic_string<char>, Foo> >' requested here
        load_construct_data(ar, t, v);
        ^
/usr/local/include/boost/serialization/detail/stack_constructor.hpp:58:31: note: in instantiation of function template specialization 'boost::serialization::load_construct_data_adl<boost::archive::xml_iarchive, std::pair<const std::basic_string<char>, Foo> >' requested here
        boost::serialization::load_construct_data_adl(
                              ^
/usr/local/include/boost/serialization/collections_load_imp.hpp:83:48: note: in instantiation of member function 'boost::serialization::detail::stack_construct<boost::archive::xml_iarchive, std::pair<const std::basic_string<char>, Foo> >::stack_construct' requested here
        detail::stack_construct<Archive, type> t(ar, v);
                                               ^
/usr/local/include/boost/serialization/collections_load_imp.hpp:158:16: note: (skipping 12 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
        hint = ifunc(ar, s, item_version, hint);
               ^
/usr/local/include/boost/archive/detail/common_iarchive.hpp:66:18: note: in instantiation of function template specialization 'boost::archive::load<boost::archive::xml_iarchive, std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > >' requested here
        archive::load(* this->This(), t);
                 ^
/usr/local/include/boost/archive/basic_xml_iarchive.hpp:86:39: note: in instantiation of function template specialization 'boost::archive::detail::common_iarchive<boost::archive::xml_iarchive>::load_override<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > >' requested here
        this->detail_common_iarchive::load_override(t.value(), 0);
                                      ^
/usr/local/include/boost/archive/xml_iarchive.hpp:93:38: note: in instantiation of function template specialization 'boost::archive::basic_xml_iarchive<boost::archive::xml_iarchive>::load_override<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > >' requested here
        basic_xml_iarchive<Archive>::load_override(t, 0);
                                     ^
/usr/local/include/boost/archive/detail/interface_iarchive.hpp:60:23: note: in instantiation of function template specialization 'boost::archive::xml_iarchive_impl<boost::archive::xml_iarchive>::load_override<const boost::serialization::nvp<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > > >' requested here
        this->This()->load_override(t, 0);
                      ^
main.cpp:50:8: note: in instantiation of function template specialization 'boost::archive::detail::interface_iarchive<boost::archive::xml_iarchive>::operator>><const boost::serialization::nvp<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > > >' requested here
    ia >> boost::serialization::make_nvp("f", g);
       ^
main.cpp:34:5: note: implicitly declared private here
    Foo():i(0){}
    ^

但是我仍然遇到同样的错误,因为它在实例化std :: pair时需要构造函数

2 个答案:

答案 0 :(得分:4)

喔。 AHA。

我刚使用Boost 1.57.0将情况与map<string, Foo>进行比较。

嗯,你好运。您已经找到了另一个库版本依赖项(可能是一个错误)。

  • 不使用它,但提供私有默认构造函数,GCC 4.8.2编译得很好: Live On Coliru [1]

  • GCC 4.9.0虽然无法编译(它也使用了较新版本的标准库)。 std::pair<>默认构造函数无法在那里编译,因为Foo不是默认可构造的: Live On Coliru

解决方案

幸运的是,save_construct_data / load_construct_data的解决方案再次挽救了这一天。

但是,您需要满足元素类型实际而不是Foo这一事实,但需要std::pair<T const, Foo>

template <class Archive, typename K> inline friend void save_construct_data(Archive& ar, std::pair<K, Foo> const* v, const unsigned int) {
    std::cerr << __PRETTY_FUNCTION__ << "\n";
    ar & boost::serialization::make_nvp("first", v->first);
    ar & boost::serialization::make_nvp("second", v->second.i);
}
template <class Archive, typename K> inline friend void load_construct_data(Archive& ar, std::pair<K, Foo>* v, const unsigned int) {
    std::cerr << __PRETTY_FUNCTION__ << "\n";
    typename std::remove_cv<K>::type first;
    ar & boost::serialization::make_nvp("first", first);
    int tmp;
    ar & boost::serialization::make_nvp("second", tmp);
    new(v) std::pair<K, Foo>(first, tmp);
}

现在一切正常:

<强> Live On Coliru

#include <boost/serialization/nvp.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>
#include <boost/version.hpp>
#include <fstream>
#include <iostream>

class Foo {
    friend class boost::serialization::access;

    template <class Archive> void serialize(Archive &, const unsigned int) {
        std::cerr << __PRETTY_FUNCTION__ << "\n";
    }

    template <class Archive, typename K> inline friend void save_construct_data(Archive& ar, std::pair<K, Foo> const* v, const unsigned int) {
        std::cerr << __PRETTY_FUNCTION__ << "\n";
        ar & boost::serialization::make_nvp("first", v->first);
        ar & boost::serialization::make_nvp("second", v->second.i);
    }
    template <class Archive, typename K> inline friend void load_construct_data(Archive& ar, std::pair<K, Foo>* v, const unsigned int) {
        std::cerr << __PRETTY_FUNCTION__ << "\n";
        typename std::remove_cv<K>::type first;
        ar & boost::serialization::make_nvp("first", first);
        int tmp;
        ar & boost::serialization::make_nvp("second", tmp);
        new(v) std::pair<K, Foo>(first, tmp);
    }

    int i;

  public:
    Foo(int k) : i(k) {}

    friend std::ostream& operator<<(std::ostream& os, Foo const& foo) {
        return os << "Foo { " << foo.i << " }";
    }
};

namespace boost { namespace serialization {


} }

int main() {
    using Data = std::map<std::string, Foo>;
    std::cout << "Boost version: " << BOOST_VERSION << "\n";

    {
        auto f = Data { {"a", 12 }, {"b", 42} };
        //for (auto& e : f) std::cout << e.first << ", " << e.second << "\n";
        std::ofstream os("path");
        boost::archive::xml_oarchive oa(os);
        oa << boost::serialization::make_nvp("f", f);
    }

    {
        Data g;
        std::ifstream is("path");
        boost::archive::xml_iarchive ia(is);
        ia >> boost::serialization::make_nvp("f", g);

        for (auto& e : g)
            std::cout << e.first << ", " << e.second << "\n";
    }
}

打印哪些:

Boost version: 105700
void save_construct_data(Archive&, const std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_oarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_oarchive]
void save_construct_data(Archive&, const std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_oarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_oarchive]
void load_construct_data(Archive&, std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_iarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_iarchive]
void load_construct_data(Archive&, std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_iarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_iarchive]
a, Foo { 12 }
b, Foo { 42 }

[1] (我无法在Coliru上链接它,因为那里的boost库已经重新编译到GCC 5.0 ABI)

备注

更好,更通用的解决方案是对load/save_construct_data命名空间中的非默认可构造类型进行boost::serialization一般操作。这样,人们就不会知道&#34;关于std::pair<>实施细节。他们可以为自己的用户类型实现load/save_construct_data,JustWork™是将它们放在矢量还是地图中。

尽管实现这一点并不简单,但可能会干扰Boost Serialization框架内部的其他一些机制。

我希望得到Boost Serialization维护者的一些帮助,以便以可靠的方式做到这一点。所以,我今天似乎要提交两张票。

答案 1 :(得分:3)

它也不一定适用于矢量。向量反序列化首先将向量调整为所需大小。这要求元素是可默认构造的。

请注意这只是一个问题,因为

  1. 无法通过serialization::access朋友“令牌”
  2. 访问构建
  3. 该类是默认可构造的
  4. 文件化解决方案

    文档tells you to use save_construct_data and load_construct_data适用于非默认构造的类型。

    具体来说,他们承诺它也适用于这些STL容器:

      

    除了指针的反序列化之外,这些覆盖还用于反序列化STL容器,其元素类型没有默认构造函数。

    在实践中,这在v1.57.0中运行良好:

    但是在1.58.0中这不是真的......

    Boost 1.58.0中的错误

    版本1.58.0似乎打破了这个:

    代码似乎需要检查(来自serialization/vector.hpp未优化版本中的load(...)

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

    但是,这会在运行时进行检查。该方法将静态拒绝编译。糟糕。

    修复

    不应将分支放在相同的流代码中,而应调度它以便仅实​​例化适用的分支。我用这种简单的方法进行了测试:

    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>());
    }
    

    它有效。

    概要

    可悲的是,我现在没有时间调查map<>情况。但我怀疑事情是相似的。记录的解决方案应该仍然有效。它可能仍然会被打破。

    我将在今天晚些时候向boost问题跟踪器报告上述问题。

    我希望答案可以帮助您找到/解决方案