3

I am encountering an issue with using boost serialization of a std::vector of std::unique_ptr's. The type of the unique_ptr does not matter, and in the below example, integers are used:

#include <boost/serialization/unique_ptr.hpp>

#include <boost/serialization/vector.hpp>

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <fstream>

namespace boost {
namespace serialization {

template <class Archive>
void serialize(Archive& ar, std::vector<unique_ptr<int>>& g, const unsigned int version) {
    ar& g;
}

}  // namespace serialization
}  // namespace boost

int main(int argc, char** argv){
std::vector<std::unique_ptr<int>> v_out;
const std::string archName = "test_arch.xml";

v_out.push_back(make_unique<int>(1));
v_out.push_back(make_unique<int>(2));
v_out.push_back(make_unique<int>(3));

// Serialize vector
std::ofstream ofs(archName);
{
    boost::archive::xml_oarchive oa(ofs);
    oa << BOOST_SERIALIZATION_NVP(v_out);
}
ofs.close();

std::vector<std::unique_ptr<int>> v_in;
// Deserialize vector
std::ifstream ifs(archName);
{
    boost::archive::xml_iarchive ia(ifs);
    // next line fails to build
    ia >> BOOST_SERIALIZATION_NVP(v_in);
}
ifs.close();


remove(archName.c_str());

}

As specified by the comment, the program fails to compile when trying to use the deserialize operator from the input archive into a std::vector<std::unique_ptr<...>>.

The error thrown is:

/usr/include/c++/5/ext/new_allocator.h:120: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^

which is thrown in GCC's new_allocator.h, line 120.

116: #if __cplusplus >= 201103L
117:       template<typename _Up, typename... _Args>
118:         void
119:         construct(_Up* __p, _Args&&... __args)
120:    { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
121: 
122:       template<typename _Up>
123:         void 
124:         destroy(_Up* __p) { __p->~_Up(); }
125: #else
126: ...

The above example build and runs as expected on both Windows and OS X - This only fails to compile on linux.
The app is linked to the following libraries (using CMake):

target_link_libraries(app ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_SERIALIZATION_LIBRARY})

The link command is equal on all platforms. I am using boost 1.67 and compiling on Ubuntu 16.04 using GCC 5.4.0

The following CMakeLists.txt is used for the project:

set (CMAKE_CXX_STANDARD 14)

add_executable(test main.cpp)

set(Boost_NO_SYSTEM_PATHS OFF)
set(BOOST_INCLUDEDIR "$ENV{BOOST_ROOT}")

find_package(Boost COMPONENTS serialization REQUIRED)

target_link_libraries(test  ${Boost_SERIALIZATION_LIBRARY})
Morten B.
  • 93
  • 8
  • 2
    Possible duplicate of [boost serialization std::unique\_ptr support](https://stackoverflow.com/questions/12915267/boost-serialization-stdunique-ptr-support) – Mikhail Apr 26 '18 at 08:32
  • 1
    @Mikhail That post was resolved by inclusion of `#include `, which is not the issue here. Serialization of unique_ptr of integers should be supported out of the box, currently in boost. – Morten B. Apr 26 '18 at 08:47
  • @MortenB. in that example though; they're not using a vector. What it LOOKS like from your error message, is the boost serialisation of vector tries to use the copy constructor of whatever is being stored - and that just won't work for a std::unique_ptr. I just had a look at the 1.66 boost though and it does a boost::move of the object; so maybe you just need to upgrade your version of boost, or write your own serialisation of vector? – UKMonkey Apr 26 '18 at 08:56
  • Also https://stackoverflow.com/questions/13347776/boost-serialization-of-stl-collection-of-std-unique-ptrs/13353341 – Mikhail Apr 26 '18 at 09:10
  • @UKMonkey exactly, this should be supported in boost 1.66 (and i am using boost 1.67). As specified, i do compilation on windows and mac systems using the exact same boost version, where the above construct perfectly compiles. So again, this is only an issue on my linux machine. – Morten B. Apr 26 '18 at 09:25

1 Answers1

2

You don't need to provide serialization for the vector. In fact, doing so broke things because you failed to provide NVP wrapping in your serialize function.

Just remove that.

Next up, Serialization does not expect (unique pointers) to primitive types. For that reason in this example it does matter whether you use int or some trivial struct X.

Side note: you need consistent XML names on (de)serialize. You had v_out versus v_in which doesn't work.

Fixed sample:

Live On Coliru

#include <boost/serialization/unique_ptr.hpp>

#include <boost/serialization/vector.hpp>

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <fstream>
#include <memory>

struct X {
    X(int = 0) {}
    template <typename Ar> void serialize(Ar&, unsigned) {}
};

int main() {
    std::vector<std::unique_ptr<X> > v_out;
    const std::string archName = "test_arch.xml";

    v_out.push_back(std::make_unique<X>(1));
    v_out.push_back(std::make_unique<X>(2));
    v_out.push_back(std::make_unique<X>(3));

    // Serialize vector
    {
        std::ofstream ofs(archName);
        boost::archive::xml_oarchive oa(ofs);
        oa << boost::serialization::make_nvp("root", v_out);
    }

    // Deserialize vector
    {
        std::ifstream ifs(archName);
        boost::archive::xml_iarchive ia(ifs);
        // next line fails to build
        std::vector<std::unique_ptr<X> > v_in;
        ia >> boost::serialization::make_nvp("root", v_in);
    }

    remove(archName.c_str());
}

Alternatively

To have non-intrusive serialization:

struct X {
    X(int i = 0) : _i(i) {}
    int _i;
};

namespace boost { namespace serialization {
    template <class Archive>
    void serialize(Archive& ar, X& x, unsigned) {
        ar& BOOST_SERIALIZATION_NVP(x._i);
    }
} }

Live On Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
  • No it doesn't? Have you not visited those links? It compiles live, right in front of you. That's with GCC 7.2 and Boost 1.66. I just tried (again) with GCC 5.4 and Boost 1.67 (Ubuntu 16.04) and it also works. – sehe Apr 27 '18 at 10:21
  • See for yourself, [Live On Wandbox](https://wandbox.org/permlink/Gtz3dWqAxsqQK23U) – sehe Apr 27 '18 at 10:23
  • This sadly throws the same compilation error as the initial question, in the `new_allocator.h` compiler provided header throws error `/usr/include/c++/5/ext/new_allocator.h:120: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) ...` I'm worrying that my boost setup is what is causing the errors, though this should not be the case, since boost version 1.67 is reported found by `find_package` in the cmakefile. CMakeLists.txt has been atached to the question – Morten B. Apr 27 '18 at 10:24
  • It's not very nice to keep removing comments, even after people responded. I'll be away from the keyboard for a few hours now – sehe Apr 27 '18 at 10:25
  • 1
    I think you're getting mixed up includes. I'm not a CMake guru, but the include path doesn't work for me, so using [the docs](https://cmake.org/cmake/help/v3.5/module/FindBoost.html) I made this simpler [CMakeLists.txt](https://paste.ubuntu.com/p/9BsCZfHZyt/). You can see it working from a clean Ubuntu 16.04 instance using [this Dockerfile](https://paste.ubuntu.com/p/XF74P8zZx8/) - It takes 3 minutes in total. Video demo [here](http://stackoverflow-sehe.s3.amazonaws.com/355e6cb8-0038-4539-82bb-ea6177c42f97/dockerdemo.mp4) – sehe Apr 28 '18 at 00:23