getting rid of buffer.hpp, simplified adaptive_storage, make buffer is never in invalid state because of throw

This commit is contained in:
Hans Dembinski 2019-02-05 00:25:24 +01:00
parent 5c0c5f59b4
commit c1bc7cbed8
3 changed files with 236 additions and 327 deletions

View File

@ -12,7 +12,6 @@
#include <boost/config.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/cstdint.hpp>
#include <boost/histogram/detail/buffer.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/iterator/iterator_facade.hpp>
@ -95,6 +94,56 @@ bool safe_radd(T& t, const U& u) {
return safe_radd(is_unsigned_integral<U>{}, t, u);
}
template <class Allocator>
auto create_buffer(Allocator& a, std::size_t n) {
using AT = std::allocator_traits<Allocator>;
auto ptr = AT::allocate(a, n); // may throw
static_assert(std::is_trivially_copyable<decltype(ptr)>::value,
"ptr must be trivially copyable");
auto it = ptr;
const auto end = ptr + n;
try {
// this loop may throw
while (it != end) AT::construct(a, it++, typename AT::value_type{});
} catch (...) {
// release resources that were already acquired before rethrowing
while (it != ptr) AT::destroy(a, --it);
AT::deallocate(a, ptr, n);
throw;
}
return ptr;
}
template <class Allocator, class Iterator>
auto create_buffer(Allocator& a, std::size_t n, Iterator iter) {
using AT = std::allocator_traits<Allocator>;
auto ptr = AT::allocate(a, n); // may throw
static_assert(std::is_trivially_copyable<decltype(ptr)>::value,
"ptr must be trivially copyable");
auto it = ptr;
const auto end = ptr + n;
try {
// this loop may throw
while (it != end) AT::construct(a, it++, *iter++);
} catch (...) {
// release resources that were already acquired before rethrowing
while (it != ptr) AT::destroy(a, --it);
AT::deallocate(a, ptr, n);
throw;
}
return ptr;
}
template <class Allocator>
void destroy_buffer(Allocator& a, typename std::allocator_traits<Allocator>::pointer p,
std::size_t n) {
BOOST_ASSERT(p);
using AT = std::allocator_traits<Allocator>;
auto it = p + n;
while (it != p) AT::destroy(a, --it);
AT::deallocate(a, p, n);
}
} // namespace detail
/**
@ -216,69 +265,138 @@ private:
using types = mp11::mp_list<uint8_t, uint16_t, uint32_t, uint64_t, mp_int, double>;
template <class T>
static constexpr char type_index() {
static constexpr char type_index() noexcept {
return static_cast<char>(mp11::mp_find<types, T>::value);
}
struct buffer_type {
allocator_type alloc;
std::size_t size;
std::size_t size = 0;
char type = 0;
void* ptr = nullptr;
// no allocation here
buffer_type(std::size_t s = 0, const allocator_type& a = allocator_type()) noexcept
: alloc(a), size(s) {}
template <class F, class... Ts>
decltype(auto) apply(F&& f, Ts&&... ts) const {
// this is intentionally not a switch, the if-chain is faster in benchmarks
if (type == type_index<uint8_t>())
return f(reinterpret_cast<uint8_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<uint16_t>())
return f(reinterpret_cast<uint16_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<uint32_t>())
return f(reinterpret_cast<uint32_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<uint64_t>())
return f(reinterpret_cast<uint64_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<mp_int>())
return f(reinterpret_cast<mp_int*>(ptr), std::forward<Ts>(ts)...);
return f(reinterpret_cast<double*>(ptr), std::forward<Ts>(ts)...);
}
buffer_type(std::size_t s = 0, allocator_type a = {})
: alloc(std::move(a)), size(s), type(type_index<uint8_t>()) {
if (size > 0) {
// rebind allocator
using alloc_type = typename std::allocator_traits<
allocator_type>::template rebind_alloc<uint8_t>;
alloc_type a(alloc);
try {
ptr = detail::create_buffer(a, size); // may throw
} catch (...) {
size = 0;
throw;
}
}
}
buffer_type(buffer_type&& o) noexcept
: alloc(std::move(o.alloc)), size(o.size), type(o.type), ptr(o.ptr) {
o.size = 0;
o.type = 0;
o.ptr = nullptr;
}
buffer_type& operator=(buffer_type&& o) noexcept {
if (this != &o) {
using std::swap;
swap(alloc, o.alloc);
swap(size, o.size);
swap(type, o.type);
swap(ptr, o.ptr);
}
return *this;
}
buffer_type(const buffer_type& o) : alloc(o.alloc) {
o.apply([this, &o](auto* otp) {
using T = detail::naked<decltype(*otp)>;
this->template make<T>(o.size, otp);
});
}
buffer_type& operator=(const buffer_type& o) {
*this = buffer_type(o);
return *this;
}
~buffer_type() noexcept { destroy(); }
void destroy() noexcept {
BOOST_ASSERT((ptr == nullptr) == (size == 0));
if (ptr == nullptr) return;
apply([this](auto* tp) {
using T = detail::naked<decltype(*tp)>;
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(alloc); // rebind allocator
detail::destroy_buffer(a, tp, size);
});
size = 0;
type = 0;
ptr = nullptr;
}
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4244) // possible loss of data
#endif
template <class T, class U>
T* create_impl(T*, const U* init) {
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(alloc); // rebind allocator
return init ? detail::create_buffer_from_iter(a, size, init)
: detail::create_buffer(a, size, 0);
template <class T>
void make(std::size_t n) {
// note: order of commands is to not leave buffer in invalid state upon throw
destroy();
if (n > 0) {
// rebind allocator
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(alloc);
ptr = detail::create_buffer(a, n); // may throw
}
size = n;
type = type_index<T>();
}
// create_impl: no specialization for mp_int, it has no ctor which accepts
// allocator, cannot pass state :(
template <class T, class U = T>
T* create(const U* init = nullptr) {
return create_impl(static_cast<T*>(nullptr), init);
template <class T, class U>
void make(std::size_t n, U iter) {
// note: iter may be current ptr, so create new buffer before deleting old buffer
T* new_ptr = nullptr;
const auto new_type = type_index<T>();
if (n > 0) {
// rebind allocator
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(alloc);
new_ptr = detail::create_buffer(a, n, iter); // may throw
}
destroy();
size = n;
type = new_type;
ptr = new_ptr;
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
template <class T>
void set(T* p) {
type = type_index<T>();
ptr = p;
}
};
template <class F, class B, class... Ts>
static decltype(auto) apply(F&& f, B&& b, Ts&&... ts) {
// this is intentionally not a switch, the if-chain is faster in benchmarks
if (b.type == type_index<uint8_t>())
return f(reinterpret_cast<uint8_t*>(b.ptr), b, std::forward<Ts>(ts)...);
if (b.type == type_index<uint16_t>())
return f(reinterpret_cast<uint16_t*>(b.ptr), b, std::forward<Ts>(ts)...);
if (b.type == type_index<uint32_t>())
return f(reinterpret_cast<uint32_t*>(b.ptr), b, std::forward<Ts>(ts)...);
if (b.type == type_index<uint64_t>())
return f(reinterpret_cast<uint64_t*>(b.ptr), b, std::forward<Ts>(ts)...);
if (b.type == type_index<mp_int>())
return f(reinterpret_cast<mp_int*>(b.ptr), b, std::forward<Ts>(ts)...);
return f(reinterpret_cast<double*>(b.ptr), b, std::forward<Ts>(ts)...);
}
template <class Buffer>
class reference_t {
public:
@ -318,28 +436,26 @@ private:
return !operator<(rhs);
}
operator double() const { return apply(getter(), *buffer_, idx_); }
operator double() const {
return buffer_->apply(
[this](const auto* tp) { return static_cast<double>(tp[idx_]); });
}
protected:
template <class Binary, class U>
bool op(const reference_t<U>& rhs) const {
const auto i = idx_;
const auto j = rhs.idx_;
return apply(
[i, j, &rhs](const auto* ptr, const Buffer&) {
const auto& pi = ptr[i];
return apply([&pi, j](const auto* q, const U&) { return Binary()(pi, q[j]); },
*rhs.buffer_);
},
*buffer_);
return buffer_->apply([i, j, &rhs](const auto* ptr) {
const auto& pi = ptr[i];
return rhs.buffer_->apply([&pi, j](const auto* q) { return Binary()(pi, q[j]); });
});
}
template <class Binary, class U>
bool op(const U& rhs) const {
const auto i = idx_;
return apply(
[i, &rhs](const auto* tp, const auto&) { return Binary()(tp[i], rhs); },
*buffer_);
return buffer_->apply([i, &rhs](const auto* tp) { return Binary()(tp[i], rhs); });
}
template <class U>
@ -359,30 +475,33 @@ public:
using base_type::base_type;
reference& operator=(const reference& t) {
apply(setter(), *base_type::buffer_, base_type::idx_, t);
t.buffer_->apply([this, &t](const auto* otp) { *this = otp[t.idx_]; });
return *this;
}
template <class T>
reference& operator=(const T& t) {
apply(setter(), *base_type::buffer_, base_type::idx_, t);
base_type::buffer_->apply([this, &t](auto* tp) {
tp[base_type::idx_] = 0;
adder()(tp, *base_type::buffer_, base_type::idx_, t);
});
return *this;
}
template <class T>
reference& operator+=(const T& t) {
apply(adder(), *base_type::buffer_, base_type::idx_, t);
base_type::buffer_->apply(adder(), *base_type::buffer_, base_type::idx_, t);
return *this;
}
template <class T>
reference& operator-=(const T& t) {
apply(adder(), *base_type::buffer_, base_type::idx_, negate(t));
base_type::buffer_->apply(adder(), *base_type::buffer_, base_type::idx_, negate(t));
return *this;
}
reference& operator++() {
apply(incrementor(), *base_type::buffer_, base_type::idx_);
base_type::buffer_->apply(incrementor(), *base_type::buffer_, base_type::idx_);
return *this;
}
};
@ -421,63 +540,32 @@ public:
using const_iterator = iterator_t<const value_type, const_reference, const buffer_type>;
using iterator = iterator_t<value_type, reference, buffer_type>;
~adaptive_storage() { apply(destroyer(), buffer); }
adaptive_storage(const adaptive_storage& o) { apply(setter(), o.buffer, buffer); }
adaptive_storage& operator=(const adaptive_storage& o) {
if (this != &o) { apply(setter(), o.buffer, buffer); }
return *this;
}
adaptive_storage(adaptive_storage&& o) : buffer(std::move(o.buffer)) {
o.buffer.size = 0;
o.buffer.ptr = nullptr;
}
adaptive_storage& operator=(adaptive_storage&& o) {
if (this != &o) { std::swap(buffer, o.buffer); }
return *this;
}
explicit adaptive_storage(allocator_type a = {}) : buffer(0, std::move(a)) {}
adaptive_storage(const adaptive_storage&) = default;
adaptive_storage& operator=(const adaptive_storage&) = default;
adaptive_storage(adaptive_storage&&) = default;
adaptive_storage& operator=(adaptive_storage&&) = default;
template <class T>
adaptive_storage(const storage_adaptor<T>& s) : buffer(s.size()) {
buffer.set(buffer.template create<uint8_t>());
std::size_t i = 0;
for (auto&& x : s) apply(setter(), buffer, i++, x);
adaptive_storage(const storage_adaptor<T>& s) {
using V = detail::naked<decltype(s[0])>;
constexpr auto ti = type_index<V>();
if (ti < mp11::mp_size<types>::value)
buffer.template make<V>(s.size(), s.begin());
else {
buffer.template make<double>(s.size(), s.begin());
}
}
template <class C, class = detail::requires_iterable<C>>
adaptive_storage& operator=(const C& s) {
apply(destroyer(), buffer);
buffer.size = s.size();
using V = detail::naked<decltype(s[0])>;
const unsigned ti = type_index<V>();
if (ti < mp11::mp_size<types>::value)
buffer.set(buffer.template create<V>());
else
buffer.set(buffer.template create<double>());
std::size_t i = 0;
for (auto&& x : s) apply(setter(), buffer, i++, x);
template <class Iterable, class = detail::requires_iterable<Iterable>>
adaptive_storage& operator=(const Iterable& s) {
*this = adaptive_storage(s);
return *this;
}
explicit adaptive_storage(const allocator_type& a = allocator_type()) : buffer(0, a) {}
allocator_type get_allocator() const { return buffer.alloc; }
void reset(std::size_t s) {
apply(destroyer(), buffer);
buffer.size = s;
try {
buffer.set(buffer.template create<uint8_t>());
} catch (...) {
buffer.size = 0;
buffer.type = 0;
buffer.ptr = nullptr;
throw;
}
}
void reset(std::size_t s) { buffer.template make<uint8_t>(s); }
std::size_t size() const noexcept { return buffer.size; }
@ -486,29 +574,27 @@ public:
bool operator==(const adaptive_storage& o) const noexcept {
if (size() != o.size()) return false;
return apply(
[&o](const auto* ptr, const buffer_type&) {
return apply(
[ptr](const auto* optr, const buffer_type& ob) {
return std::equal(ptr, ptr + ob.size, optr, equal_to());
},
o.buffer);
},
buffer);
return buffer.apply([&o](const auto* ptr) {
return o.buffer.apply([ptr, &o](const auto* optr) {
return std::equal(ptr, ptr + o.size(), optr, equal_to());
});
});
}
template <class T>
bool operator==(const T& o) const {
if (size() != o.size()) return false;
return apply(
[&o](const auto* ptr, const buffer_type&) {
return std::equal(ptr, ptr + o.size(), std::begin(o), equal_to());
},
buffer);
return buffer.apply([&o](const auto* ptr) {
return std::equal(ptr, ptr + o.size(), std::begin(o), equal_to());
});
}
adaptive_storage& operator+=(const adaptive_storage& rhs) {
BOOST_ASSERT(size() == rhs.size());
auto add = [this](auto* otp) {
for (std::size_t i = 0; i < buffer.size; ++i)
buffer.apply(adder(), buffer, i, otp[i]);
};
if (this == &rhs) {
/*
Self-adding is a special-case, because the source buffer ptr may be invalided by
@ -517,9 +603,9 @@ public:
in user code, but the unit-tests do this a lot.
*/
const auto copy = rhs;
apply(buffer_adder(), copy.buffer, buffer);
copy.buffer.apply(add);
} else {
apply(buffer_adder(), rhs.buffer, buffer);
rhs.buffer.apply(add);
}
return *this;
}
@ -533,15 +619,19 @@ public:
adaptive_storage& operator-=(const adaptive_storage& rhs) {
BOOST_ASSERT(size() == rhs.size());
auto sub = [this](auto* otp) {
for (std::size_t i = 0; i < buffer.size; ++i)
buffer.apply(adder(), buffer, i, negate(otp[i]));
};
if (this == &rhs) {
/*
Self-subtracting is a special-case, because the source buffer ptr may be
invalided by growth. We avoid this by making a copy of the source.
*/
const auto copy = rhs;
apply(buffer_subtractor(), copy.buffer, buffer);
copy.buffer.apply(sub);
} else {
apply(buffer_subtractor(), rhs.buffer, buffer);
rhs.buffer.apply(sub);
}
return *this;
}
@ -554,7 +644,7 @@ public:
}
adaptive_storage& operator*=(const double x) {
apply(multiplier(), buffer, x);
buffer.apply(multiplier(), buffer, x);
return *this;
}
@ -565,53 +655,21 @@ public:
/// @private used by unit tests, not part of generic storage interface
template <class T>
adaptive_storage(std::size_t s, const T* p, const allocator_type& a = allocator_type())
: buffer(s, a) {
buffer.set(buffer.template create<T>(p));
adaptive_storage(std::size_t s, const T* p, allocator_type a = {})
: buffer(0, std::move(a)) {
buffer.template make<T>(s, p);
}
template <class Archive>
void serialize(Archive&, unsigned);
private:
struct destroyer {
template <class T, class Buffer>
void operator()(T* tp, Buffer& b) {
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(b.alloc); // rebind allocator
detail::destroy_buffer(a, tp, b.size);
b.ptr = nullptr;
}
};
struct setter {
template <class T, class OBuffer, class Buffer>
void operator()(T* optr, const OBuffer& ob, Buffer& b) {
if (b.size == ob.size && b.type == ob.type) {
std::copy(optr, optr + ob.size, reinterpret_cast<T*>(b.ptr));
} else {
apply(destroyer(), b);
b.size = ob.size;
b.set(b.template create<T>(optr));
}
}
template <class T, class Buffer, class U>
void operator()(T* tp, Buffer& b, std::size_t i, const U& u) {
tp[i] = 0;
adder()(tp, b, i, u);
}
};
struct incrementor {
template <class T, class Buffer>
void operator()(T* tp, Buffer& b, std::size_t i) {
if (!detail::safe_increment(tp[i])) {
using U = mp11::mp_at_c<types, (type_index<T>() + 1)>;
U* ptr = b.template create<U>(tp);
destroyer()(tp, b);
b.set(ptr);
b.template make<U>(b.size, tp);
++reinterpret_cast<U*>(b.ptr)[i];
}
}
@ -639,9 +697,7 @@ private:
if (detail::safe_radd(tp[i], x)) return;
if (x >= 0) {
using V = mp11::mp_at_c<types, (type_index<T>() + 1)>;
auto ptr = b.template create<V>(tp);
destroyer()(tp, b);
b.set(ptr);
b.template make<V>(b.size, tp);
if_U_is_integral(std::true_type{}, static_cast<V*>(b.ptr), b, i, x);
} else {
if_U_is_integral(std::false_type{}, tp, b, i, x);
@ -650,9 +706,7 @@ private:
template <class T, class Buffer, class U>
void if_U_is_integral(std::false_type, T* tp, Buffer& b, std::size_t i, const U& x) {
auto ptr = b.template create<double>(tp);
destroyer()(tp, b);
b.set(ptr);
b.template make<double>(b.size, tp);
operator()(static_cast<double*>(b.ptr), b, i, x);
}
@ -667,34 +721,11 @@ private:
}
};
struct buffer_adder {
template <class T, class OBuffer, class Buffer>
void operator()(T* tp, const OBuffer&, Buffer& b) {
for (std::size_t i = 0; i < b.size; ++i) { apply(adder(), b, i, tp[i]); }
}
};
struct buffer_subtractor {
template <class T, class OBuffer, class Buffer>
void operator()(T* tp, const OBuffer&, Buffer& b) {
for (std::size_t i = 0; i < b.size; ++i) { apply(adder(), b, i, negate(tp[i])); }
}
};
struct getter {
template <class T, class Buffer>
double operator()(T* tp, Buffer&, std::size_t i) {
return static_cast<double>(tp[i]);
}
};
struct multiplier {
template <class T, class Buffer>
void operator()(T* tp, Buffer& b, const double x) {
// potential lossy conversion that cannot be avoided
auto ptr = b.template create<double>(tp);
destroyer()(tp, b);
b.set(ptr);
b.template make<double>(b.size, tp);
operator()(reinterpret_cast<double*>(b.ptr), b, x);
}

View File

@ -1,111 +0,0 @@
// Copyright 2018 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_DETAIL_BUFFER_HPP
#define BOOST_HISTOGRAM_DETAIL_BUFFER_HPP
#include <boost/histogram/detail/meta.hpp>
#include <cstddef>
#include <memory>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class AT, class... Ts>
void create_buffer_impl(std::true_type, typename AT::allocator_type& a, std::size_t n,
typename AT::pointer it, Ts&&... ts) {
// never throws
for (const auto end = it + n; it != end; ++it)
AT::construct(a, it, std::forward<Ts>(ts)...);
}
template <class AT, class... Ts>
void create_buffer_impl(std::false_type, typename AT::allocator_type& a, std::size_t n,
typename AT::pointer it, Ts&&... ts) {
const auto ptr = it;
try {
for (const auto end = it + n; it != end; ++it)
AT::construct(a, it, std::forward<Ts>(ts)...);
} catch (...) {
// release resources that were already acquired before rethrowing
while (it != ptr) AT::destroy(a, it--);
AT::deallocate(a, ptr, n);
throw;
}
}
template <class AT, class Iterator, class... Ts>
void create_buffer_from_iter_impl(std::true_type, typename AT::allocator_type& a,
std::size_t n, typename AT::pointer it, Iterator iter,
Ts&&... ts) {
// never throws
for (const auto end = it + n; it != end; ++it)
AT::construct(a, it, *iter++, std::forward<Ts>(ts)...);
}
template <class AT, class Iterator, class... Ts>
void create_buffer_from_iter_impl(std::false_type, typename AT::allocator_type& a,
std::size_t n, typename AT::pointer it, Iterator iter,
Ts&&... ts) {
const auto ptr = it;
try {
for (const auto end = it + n; it != end; ++it)
AT::construct(a, it, *iter++, std::forward<Ts>(ts)...);
} catch (...) {
// release resources that were already acquired before rethrowing
while (it != ptr) AT::destroy(a, it--);
AT::deallocate(a, ptr, n);
throw;
}
}
template <class Allocator, class... Ts>
typename std::allocator_traits<Allocator>::pointer create_buffer(Allocator& a,
std::size_t n,
Ts&&... ts) {
using AT = std::allocator_traits<Allocator>;
using T = typename AT::value_type;
auto ptr = AT::allocate(a, n); // may throw
create_buffer_impl<AT>(std::is_nothrow_constructible<T, Ts...>(), a, n, ptr,
std::forward<Ts>(ts)...);
return ptr;
}
template <class Allocator, class Iterator, class... Ts>
typename std::allocator_traits<Allocator>::pointer create_buffer_from_iter(Allocator& a,
std::size_t n,
Iterator iter,
Ts&&... ts) {
using AT = std::allocator_traits<Allocator>;
using T = typename AT::value_type;
using U = decltype(*iter);
auto ptr = AT::allocate(a, n); // may throw
create_buffer_from_iter_impl<AT>(std::is_nothrow_constructible<T, U>(), a, n, ptr, iter,
std::forward<Ts>(ts)...);
return ptr;
}
template <class Allocator>
void destroy_buffer(Allocator& a, typename std::allocator_traits<Allocator>::pointer p,
std::size_t n) {
if (!p) return;
using AT = std::allocator_traits<Allocator>;
auto it = p + n;
const auto end = p;
while (it != end) {
--it;
AT::destroy(a, it);
}
AT::deallocate(a, p, n);
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@ -18,7 +18,6 @@
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/axis/variable.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/buffer.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/unsafe_access.hpp>
@ -147,18 +146,6 @@ void variant<Ts...>::serialize(Archive& ar, unsigned /* version */) {
} // namespace axis
namespace detail {
struct serializer {
template <class T, class Buffer, class Archive>
void operator()(T*, Buffer& b, Archive& ar) {
if (Archive::is_loading::value) {
BOOST_ASSERT(b.ptr == nullptr);
b.set(b.template create<T>());
}
ar& serialization::make_nvp(
"buffer", serialization::make_array(reinterpret_cast<T*>(b.ptr), b.size));
}
};
template <class Archive, class T>
void serialize(Archive& ar, vector_impl<T>& impl, unsigned /* version */) {
ar& serialization::make_nvp("vector", static_cast<T&>(impl));
@ -187,23 +174,25 @@ template <class A>
template <class Archive>
void adaptive_storage<A>::serialize(Archive& ar, unsigned /* version */) {
if (Archive::is_loading::value) {
apply(destroyer(), buffer);
try {
ar& serialization::make_nvp("type", buffer.type);
ar& serialization::make_nvp("size", buffer.size);
apply(detail::serializer(), buffer, ar);
} catch (...) {
if (buffer.ptr == nullptr) {
buffer.size = 0;
buffer.type = 0;
}
throw;
}
buffer_type dummy(0, buffer.alloc);
std::size_t size;
ar& serialization::make_nvp("type", dummy.type);
ar& serialization::make_nvp("size", size);
dummy.apply([this, size](auto* tp) {
BOOST_ASSERT(tp == nullptr);
using T = detail::naked<decltype(*tp)>;
buffer.template make<T>(size);
});
} else {
ar& serialization::make_nvp("type", buffer.type);
ar& serialization::make_nvp("size", buffer.size);
apply(detail::serializer(), buffer, ar);
}
buffer.apply([this, &ar](auto* tp) {
using T = detail::naked<decltype(*tp)>;
ar& serialization::make_nvp(
"buffer",
serialization::make_array(reinterpret_cast<T*>(buffer.ptr), buffer.size));
});
}
template <class Archive, class A, class S>