replacing boost::variant with detail::variant

- removes large number of outdated indirect boost dependencies
- less limited in number of stored types
This commit is contained in:
Hans Dembinski
2019-04-30 16:27:14 +02:00
committed by GitHub
parent 073dffdf09
commit 170199bb9f
20 changed files with 811 additions and 151 deletions

View File

@@ -7,7 +7,6 @@
#ifndef BOOST_HISTOGRAM_AXIS_VARIANT_HPP
#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP
#include <boost/config/workaround.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/polymorphic_bin.hpp>
#include <boost/histogram/axis/traits.hpp>
@@ -15,22 +14,13 @@
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/detail/type_name.hpp>
#include <boost/histogram/detail/variant.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/list.hpp>
#include <boost/throw_exception.hpp>
#if BOOST_WORKAROUND(BOOST_CLANG, >= 1)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/get.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/variant/variant.hpp>
#if BOOST_WORKAROUND(BOOST_CLANG, >= 1)
#pragma clang diagnostic pop
#endif
#include <functional>
#include <ostream>
#include <stdexcept>
#include <tuple>
@@ -39,29 +29,75 @@
namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
struct ref_handler_impl {
static constexpr bool is_ref = false;
using type = T;
};
template <class T, class U>
struct ref_handler_impl<T, std::reference_wrapper<U>> {
static constexpr bool is_ref = true;
using type = std::reference_wrapper<T>;
};
template <class T, class U>
struct ref_handler_impl<T, std::reference_wrapper<const U>> {
static constexpr bool is_ref = true;
using type = std::reference_wrapper<const T>;
};
template <class T, class V>
using ref_handler = ref_handler_impl<T, mp11::mp_first<remove_cvref_t<V>>>;
struct variant_access {
template <class T, class Variant>
static decltype(auto) get(Variant&& v) {
using H = ref_handler<T, Variant>;
auto&& ref = v.impl.template get<typename H::type>();
return static_if_c<H::is_ref>([](auto&& ref) -> decltype(auto) { return ref.get(); },
[](auto&& ref) -> decltype(auto) { return ref; }, ref);
}
template <class T, class Variant>
static auto get_if(Variant&& v) noexcept {
using H = ref_handler<T, Variant>;
auto p = v.impl.template get_if<typename H::type>();
return static_if_c<H::is_ref>([](auto p) -> auto { return p ? &p->get() : nullptr; },
[](auto p) -> auto { return p; }, p);
}
template <class Visitor, class Variant>
static decltype(auto) apply(Visitor&& vis, Variant&& v) {
using H = ref_handler<char, Variant>;
return static_if_c<H::is_ref>(
[](auto&& vis, auto&& v) -> decltype(auto) {
return v.apply([&vis](auto&& ref) -> decltype(auto) { return vis(ref.get()); });
},
[](auto&& vis, auto&& v) -> decltype(auto) { return v.apply(vis); }, vis, v.impl);
}
};
} // namespace detail
namespace axis {
template <class T, class... Us>
T* get_if(variant<Us...>* v);
template <class T, class... Us>
const T* get_if(const variant<Us...>* v);
/// Polymorphic axis type
template <class... Ts>
class variant : public iterator_mixin<variant<Ts...>> {
using impl_type = boost::variant<Ts...>;
using raw_types = mp11::mp_transform<detail::remove_cvref_t, impl_type>;
using impl_type = detail::variant<Ts...>;
template <class T>
using is_bounded_type = mp11::mp_contains<raw_types, detail::remove_cvref_t<T>>;
using is_bounded_type = mp11::mp_contains<variant, detail::remove_cvref_t<T>>;
template <typename T>
using requires_bounded_type = std::enable_if_t<is_bounded_type<T>::value>;
// maybe metadata_type or const metadata_type, if bounded type is const
using metadata_type = std::remove_reference_t<decltype(
traits::metadata(std::declval<mp11::mp_first<impl_type>>()))>;
traits::metadata(std::declval<detail::unref_t<mp11::mp_first<impl_type>>>()))>;
public:
// cannot import ctors with using directive, it breaks gcc and msvc
@@ -71,10 +107,10 @@ public:
variant(variant&&) = default;
variant& operator=(variant&&) = default;
template <typename T, typename = requires_bounded_type<T>>
template <class T, class = requires_bounded_type<T>>
variant(T&& t) : impl(std::forward<T>(t)) {}
template <typename T, typename = requires_bounded_type<T>>
template <class T, class = requires_bounded_type<T>>
variant& operator=(T&& t) {
impl = std::forward<T>(t);
return *this;
@@ -105,12 +141,12 @@ public:
/// Return size of axis.
index_type size() const {
return visit([](const auto& x) { return x.size(); }, *this);
return visit([](const auto& a) { return a.size(); }, *this);
}
/// Return options of axis or option::none_t if axis has no options.
unsigned options() const {
return visit([](const auto& x) { return axis::traits::options(x); }, *this);
return visit([](const auto& a) { return axis::traits::options(a); }, *this);
}
/// Return reference to const metadata or instance of null_type if axis has no
@@ -138,7 +174,7 @@ public:
/// metadata.
metadata_type& metadata() {
return visit(
[](auto&& a) -> metadata_type& {
[](auto& a) -> metadata_type& {
using M = decltype(traits::metadata(a));
return detail::static_if<std::is_same<M, metadata_type&>>(
[](auto& a) -> metadata_type& { return traits::metadata(a); },
@@ -180,15 +216,12 @@ public:
[idx](const auto& a) {
return detail::value_method_switch(
[idx](const auto& a) { // axis is discrete
const double x =
detail::try_cast<double, std::runtime_error>(a.value(idx));
const double x = traits::value_as<double>(a, idx);
return polymorphic_bin<double>(x, x);
},
[idx](const auto& a) { // axis is continuous
const double x1 =
detail::try_cast<double, std::runtime_error>(a.value(idx));
const double x2 =
detail::try_cast<double, std::runtime_error>(a.value(idx + 1));
const double x1 = traits::value_as<double>(a, idx);
const double x2 = traits::value_as<double>(a, idx + 1);
return polymorphic_bin<double>(x1, x2);
},
a);
@@ -203,8 +236,7 @@ public:
template <class T>
bool operator==(const T& t) const {
// boost::variant::operator==(T) implemented only to fail, cannot use it
auto tp = get_if<T>(this);
const T* tp = detail::variant_access::template get_if<T>(*this);
return tp && detail::relaxed_equal(*tp, t);
}
@@ -213,74 +245,65 @@ public:
return !operator==(t);
}
template <class Archive>
void serialize(Archive& ar, unsigned);
template <class Visitor, class Variant>
friend auto visit(Visitor&&, Variant &&)
-> detail::visitor_return_type<Visitor, Variant>;
template <class T, class... Us>
friend T& get(variant<Us...>& v);
template <class T, class... Us>
friend const T& get(const variant<Us...>& v);
template <class T, class... Us>
friend T&& get(variant<Us...>&& v);
template <class T, class... Us>
friend T* get_if(variant<Us...>* v);
template <class T, class... Us>
friend const T* get_if(const variant<Us...>* v);
private:
boost::variant<Ts...> impl;
impl_type impl;
friend struct detail::variant_access;
friend struct boost::histogram::unsafe_access;
};
/// Apply visitor to variant.
template <class Visitor, class Variant>
auto visit(Visitor&& vis, Variant&& var)
-> detail::visitor_return_type<Visitor, Variant> {
return boost::apply_visitor(std::forward<Visitor>(vis), var.impl);
// specialization for empty argument list, useful for meta-programming
template <>
class variant<> {};
/// Apply visitor to variant (reference).
template <class Visitor, class... Us>
decltype(auto) visit(Visitor&& vis, variant<Us...>& var) {
return detail::variant_access::apply(vis, var);
}
/// Return lvalue reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
T& get(variant<Us...>& v) {
return boost::get<T>(v.impl);
/// Apply visitor to variant (movable reference).
template <class Visitor, class... Us>
decltype(auto) visit(Visitor&& vis, variant<Us...>&& var) {
return detail::variant_access::apply(vis, std::move(var));
}
/// Return rvalue reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
T&& get(variant<Us...>&& v) {
return boost::get<T>(std::move(v.impl));
/// Apply visitor to variant (const reference).
template <class Visitor, class... Us>
decltype(auto) visit(Visitor&& vis, const variant<Us...>& var) {
return detail::variant_access::apply(vis, var);
}
/// Return const reference to T, throws unspecified exception if type does not
/// match.
/// Return reference to T, throws unspecified exception if type does not match.
template <class T, class... Us>
const T& get(const variant<Us...>& v) {
return boost::get<T>(v.impl);
decltype(auto) get(variant<Us...>& v) {
return detail::variant_access::template get<T>(v);
}
/// Return movable reference to T, throws unspecified exception if type does not match.
template <class T, class... Us>
decltype(auto) get(variant<Us...>&& v) {
return std::move(detail::variant_access::template get<T>(v));
}
/// Return const reference to T, throws unspecified exception if type does not match.
template <class T, class... Us>
decltype(auto) get(const variant<Us...>& v) {
return detail::variant_access::template get<T>(v);
}
/// Returns pointer to T in variant or null pointer if type does not match.
template <class T, class... Us>
T* get_if(variant<Us...>* v) {
return boost::relaxed_get<T>(&(v->impl));
return detail::variant_access::template get_if<T>(*v);
}
/// Returns pointer to const T in variant or null pointer if type does not
/// match.
/// Returns pointer to const T in variant or null pointer if type does not match.
template <class T, class... Us>
const T* get_if(const variant<Us...>* v) {
return boost::relaxed_get<T>(&(v->impl));
return detail::variant_access::template get_if<T>(*v);
}
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
// pass-through version of get for generic programming
template <class T, class U>
decltype(auto) get(U&& u) {
@@ -300,7 +323,6 @@ const T* get_if(const U* u) {
return std::is_same<T, detail::remove_cvref_t<U>>::value ? reinterpret_cast<const T*>(u)
: nullptr;
}
#endif
} // namespace axis
} // namespace histogram

View File

@@ -17,6 +17,7 @@
#include <boost/mp11/list.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/throw_exception.hpp>
#include <functional>
#include <stdexcept>
#include <tuple>
#include <type_traits>
@@ -47,18 +48,19 @@ decltype(auto) axis_get(const T& axes) {
template <class... Ts>
decltype(auto) axis_get(std::tuple<Ts...>& axes, unsigned i) {
using namespace boost::mp11;
constexpr auto S = sizeof...(Ts);
using L = mp11::mp_unique<mp11::mp_list<Ts&...>>;
using V = mp11::mp_rename<L, axis::variant>;
return mp11::mp_with_index<S>(i, [&](auto I) { return V(std::get<I>(axes)); });
using V = mp_unique<axis::variant<std::reference_wrapper<Ts>...>>;
return mp_with_index<S>(i, [&](auto I) { return V(std::ref(std::get<I>(axes))); });
}
template <class... Ts>
decltype(auto) axis_get(const std::tuple<Ts...>& axes, unsigned i) {
using namespace boost::mp11;
constexpr auto S = sizeof...(Ts);
using L = mp11::mp_unique<mp11::mp_list<const Ts&...>>;
using V = mp11::mp_rename<L, axis::variant>;
return mp11::mp_with_index<S>(i, [&](auto I) { return V(std::get<I>(axes)); });
using L = mp_unique<mp_list<std::reference_wrapper<const Ts>...>>;
using V = mp_rename<L, axis::variant>;
return mp_with_index<S>(i, [&](auto I) { return V(std::cref(std::get<I>(axes))); });
}
template <class T>
@@ -84,17 +86,17 @@ bool axes_equal(const std::tuple<Ts...>& ts, const std::tuple<Us...>& us) {
[](const auto&, const auto&) { return false; }, ts, us);
}
template <class... Ts, class U>
bool axes_equal(const std::tuple<Ts...>& t, const U& u) {
if (sizeof...(Ts) != u.size()) return false;
template <class T, class... Us>
bool axes_equal(const T& t, const std::tuple<Us...>& u) {
if (t.size() != sizeof...(Us)) return false;
bool equal = true;
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Ts)>>(
[&](auto I) { equal &= u[I] == std::get<I>(t); });
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>(
[&](auto I) { equal &= t[I] == std::get<I>(u); });
return equal;
}
template <class T, class... Us>
bool axes_equal(const T& t, const std::tuple<Us...>& u) {
template <class... Ts, class U>
bool axes_equal(const std::tuple<Ts...>& t, const U& u) {
return axes_equal(u, t);
}

View File

@@ -15,7 +15,6 @@
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/integral.hpp>

View File

@@ -50,6 +50,19 @@ namespace detail {
template <class T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <class T>
struct unref_impl {
using type = T;
};
template <class T>
struct unref_impl<std::reference_wrapper<T>> {
using type = T;
};
template <class T>
using unref_t = typename unref_impl<T>::type;
template <class T, class U>
using convert_integer = mp11::mp_if<std::is_integral<remove_cvref_t<T>>, U, T>;
@@ -78,10 +91,6 @@ using arg_type = typename mp11::mp_at_c<args_type<T>, N>;
template <class T>
using return_type = typename boost::callable_traits::return_type<T>::type;
template <class F, class V,
class T = copy_qualifiers<V, mp11::mp_first<remove_cvref_t<V>>>>
using visitor_return_type = decltype(std::declval<F>()(std::declval<T>()));
template <typename T>
constexpr T lowest() {
return std::numeric_limits<T>::lowest();

View File

@@ -10,6 +10,7 @@
#include <boost/core/demangle.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/type_name.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <type_traits>
@@ -18,20 +19,28 @@ namespace boost {
namespace histogram {
namespace detail {
template <class T, class E, class U>
T try_cast_impl(std::false_type, U&&) {
T try_cast_impl(mp11::mp_int<0>, U&&) {
BOOST_THROW_EXCEPTION(E(cat("cannot cast ", type_name<T>(), " to ", type_name<U>())));
}
template <class T, class E, class U>
T try_cast_impl(std::true_type, U&& u) {
T try_cast_impl(mp11::mp_int<1>, U&& u) {
return static_cast<T>(u);
}
// cast fails at runtime with exception E instead of compile-time
template <class T, class E, class U>
T try_cast(U&& u) {
return try_cast_impl<T, E>(std::is_convertible<U, T>{}, std::forward<U>(u));
decltype(auto) try_cast_impl(mp11::mp_int<2>, U&& u) {
return std::forward<U>(u);
}
// cast fails at runtime with exception E instead of compile-time, T must be a value
template <class T, class E, class U>
decltype(auto) try_cast(U&& u) {
return try_cast_impl<T, E>(mp11::mp_int<(std::is_convertible<U, T>::value +
std::is_same<T, std::decay_t<U>>::value)>{},
std::forward<U>(u));
}
} // namespace detail
} // namespace histogram
} // namespace boost

View File

@@ -0,0 +1,242 @@
// Copyright (c) 2019 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_VARIANT_HPP
#define BOOST_HISTOGRAM_DETAIL_VARIANT_HPP
#include <boost/mp11.hpp>
#include <boost/throw_exception.hpp>
#include <iosfwd>
#include <stdexcept>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
T launder_cast(U&& u) {
return reinterpret_cast<T>(std::forward<U>(u));
}
// Simple C++14 variant without external boost dependencies.
//
// * No empty state, first type must have default ctor that never throws;
// if it throws anyway, the program aborts
// * All types must have copy ctors and copy assignment
// * All types must have noexcept move ctors and noexcept move assignment
// * If the current type is a reference, assignment passes through, no rebind
//
template <class... Ts>
class variant {
using default_type = mp11::mp_first<variant>;
using N = mp11::mp_size<variant>;
public:
variant() noexcept { init_default(); }
variant(const variant& x) {
x.internal_apply([this, &x](auto i) {
using T = mp11::mp_at_c<variant, i>;
this->init_i<T>(i, *x.ptr(mp11::mp_identity<T>{}, i));
});
}
variant(variant&& x) noexcept {
x.internal_apply([this, &x](auto i) {
using T = mp11::mp_at_c<variant, i>;
this->init_i<T>(i, std::move(*x.ptr(mp11::mp_identity<T>{}, i)));
});
}
variant& operator=(const variant& x) {
x.apply([this](const auto& x) { this->operator=(x); });
return *this;
}
variant& operator=(variant&& x) noexcept {
x.apply([this](auto&& x) { this->operator=(std::move(x)); });
return *this;
}
template <class U, class = std::enable_if_t<mp11::mp_contains<variant, U>::value>>
explicit variant(U&& x) noexcept {
static_assert(std::is_rvalue_reference<decltype(x)>::value, "");
constexpr auto i = find<U>();
using T = mp11::mp_at_c<variant, i>;
static_assert(std::is_nothrow_move_constructible<T>::value, "");
init_i<T>(i, std::move(x));
}
template <class U, class = std::enable_if_t<mp11::mp_contains<variant, U>::value>>
explicit variant(const U& x) {
constexpr auto i = find<U>();
using T = mp11::mp_at_c<variant, i>;
init_i<T>(i, x);
}
template <class U, class = std::enable_if_t<mp11::mp_contains<variant, U>::value>>
variant& operator=(U&& x) noexcept {
constexpr auto i = find<U>();
using T = mp11::mp_at_c<variant, i>;
static_assert(std::is_nothrow_move_constructible<T>::value, "");
if (i == index_) {
*ptr(mp11::mp_identity<T>{}, i) = std::move(x);
} else {
destroy();
init_i<T>(i, std::move(x));
}
return *this;
}
template <class U, class = std::enable_if_t<mp11::mp_contains<variant, U>::value>>
variant& operator=(const U& x) {
constexpr auto i = find<U>();
using T = mp11::mp_at_c<variant, i>;
if (i == index_) {
// nothing to do if T::operator= throws
*ptr(mp11::mp_identity<T>{}, i) = x;
} else {
destroy(); // now in invalid state
try {
// if this throws, need to return to valid state
init_i<T>(i, x);
} catch (...) {
init_default();
throw;
}
}
return *this;
}
~variant() { destroy(); }
template <class U>
bool operator==(const U& x) const noexcept {
constexpr auto i = find<U>();
static_assert(i < N::value, "argument is not a bounded type");
using T = mp11::mp_at_c<variant, i>;
return (i == index_) && *ptr(mp11::mp_identity<T>{}, i) == x;
}
template <class U>
bool operator!=(const U& x) const noexcept {
constexpr auto i = find<U>();
static_assert(i < N::value, "argument is not a bounded type");
using T = mp11::mp_at_c<variant, i>;
return (i != index_) || *ptr(mp11::mp_identity<T>{}, i) != x;
}
bool operator==(const variant& x) const noexcept {
return x.apply([this](const auto& x) { return this->operator==(x); });
}
bool operator!=(const variant& x) const noexcept {
return x.apply([this](const auto& x) { return this->operator!=(x); });
}
unsigned index() const noexcept { return index_; }
template <class T>
T& get() {
T* tp = get_if<T>();
if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
return *tp;
}
template <class T>
const T& get() const {
const T* tp = get_if<T>();
if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
return *tp;
}
template <class T>
T* get_if() noexcept {
constexpr auto i = mp11::mp_find<variant, T>{};
return i == index_ ? ptr(mp11::mp_identity<T>{}, i) : nullptr;
}
template <class T>
const T* get_if() const noexcept {
constexpr auto i = mp11::mp_find<variant, T>{};
return i == index_ ? ptr(mp11::mp_identity<T>{}, i) : nullptr;
}
template <class Functor>
decltype(auto) apply(Functor&& functor) const {
return internal_apply([this, &functor](auto i) -> decltype(auto) {
using T = mp11::mp_at_c<variant, i>;
return functor(*(this->ptr(mp11::mp_identity<T>{}, i)));
});
}
template <class Functor>
decltype(auto) apply(Functor&& functor) {
return internal_apply([this, &functor](auto i) -> decltype(auto) {
using T = mp11::mp_at_c<variant, i>;
return functor(*(this->ptr(mp11::mp_identity<T>{}, i)));
});
}
private:
template <class Functor>
decltype(auto) internal_apply(Functor&& functor) const {
return mp11::mp_with_index<sizeof...(Ts)>(index_, functor);
}
template <class T, std::size_t N>
T* ptr(mp11::mp_identity<T>, mp11::mp_size_t<N>) noexcept {
return launder_cast<T*>(&buffer_);
}
template <class T, std::size_t N>
const T* ptr(mp11::mp_identity<T>, mp11::mp_size_t<N>) const noexcept {
return launder_cast<const T*>(&buffer_);
}
void init_default() noexcept { init_i<default_type>(mp11::mp_size_t<0>{}); }
template <class T, class I, class... Args>
void init_i(I, Args&&... args) {
new (&buffer_) T(std::forward<Args>(args)...);
index_ = I::value;
}
void destroy() noexcept {
internal_apply([this](auto i) {
using T = mp11::mp_at_c<variant, i>;
this->ptr(mp11::mp_identity<T>{}, i)->~T();
});
}
template <class U>
static constexpr auto find() noexcept {
using V = std::decay_t<U>;
return mp11::mp_find<variant, V>{};
}
using buffer_t = typename std::aligned_union<0, Ts...>::type;
buffer_t buffer_;
unsigned index_;
};
template <class CharT, class Traits, class... Ts>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const variant<Ts...>& x) {
x.apply([&os](const auto& self) { os << self; });
return os;
}
// specialization for empty type list, useful for metaprogramming
template <>
class variant<> {};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,67 @@
// 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)
//
// This file is based on boost/serialization/variant.hpp.
#ifndef BOOST_HISTOGRAM_VARIANT_SERIALIZATION_HPP
#define BOOST_HISTOGRAM_VARIANT_SERIALIZATION_HPP
#include <boost/archive/archive_exception.hpp>
#include <boost/histogram/detail/variant.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/throw_exception.hpp>
namespace boost {
namespace serialization {
template <class Archive, class... Ts>
void save(Archive& ar, histogram::detail::variant<Ts...> const& v, unsigned) {
int which = static_cast<int>(v.index());
ar << BOOST_SERIALIZATION_NVP(which);
v.apply([&ar](const auto& value) { ar << BOOST_SERIALIZATION_NVP(value); });
}
template <class Archive, class... Ts>
void load(Archive& ar, histogram::detail::variant<Ts...>& v, unsigned) {
int which;
ar >> BOOST_SERIALIZATION_NVP(which);
constexpr unsigned N = sizeof...(Ts);
if (which < 0 || static_cast<unsigned>(which) >= N)
// throw on invalid which, which >= 0 can happen if type was removed from variant
boost::serialization::throw_exception(boost::archive::archive_exception(
boost::archive::archive_exception::unsupported_version));
mp11::mp_with_index<N>(static_cast<unsigned>(which), [&ar, &v](auto I) {
using T = mp11::mp_at_c<histogram::detail::variant<Ts...>, I>;
T value;
ar >> BOOST_SERIALIZATION_NVP(value);
v = std::move(value);
T* new_address = &v.template get<T>();
ar.reset_object_address(new_address, &value);
});
}
template <class Archive, class... Ts>
inline void serialize(Archive& ar, histogram::detail::variant<Ts...>& v,
unsigned file_version) {
split_free(ar, v, file_version);
}
#include <boost/serialization/tracking.hpp>
template <class... Ts>
struct tracking_level<histogram::detail::variant<Ts...>> {
typedef mpl::integral_c_tag tag;
typedef mpl::int_<::boost::serialization::track_always> type;
BOOST_STATIC_CONSTANT(int, value = type::value);
};
} // namespace serialization
} // namespace boost
#endif // BOOST_HISTOGRAM_VARIANT_SERIALIZATION_HPP

View File

@@ -16,6 +16,7 @@
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/mp11/list.hpp>
#include <boost/throw_exception.hpp>
#include <mutex>

View File

@@ -13,7 +13,6 @@
#include <boost/histogram/detail/iterator_adaptor.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <type_traits>
#include <utility>

View File

@@ -17,6 +17,7 @@
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/axis/variable.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/variant_serialization.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/unlimited_storage.hpp>
@@ -26,7 +27,6 @@
#include <boost/serialization/map.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/variant.hpp>
#include <boost/serialization/vector.hpp>
#include <tuple>
#include <type_traits>
@@ -138,9 +138,9 @@ void category<T, M, O, A>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("meta", vec_meta_.second());
}
template <class... Ts>
template <class Archive>
void variant<Ts...>::serialize(Archive& ar, unsigned /* version */) {
template <class Archive, class... Ts>
void serialize(Archive& ar, variant<Ts...>& v, unsigned /* version */) {
auto& impl = unsafe_access::axis_variant_impl(v);
ar& serialization::make_nvp("variant", impl);
}
} // namespace axis

View File

@@ -18,8 +18,12 @@ namespace histogram {
This struct enables access to private data of some classes. It is intended for library
developers who need this to implement algorithms efficiently, for example,
serialization. Users should not use this. If you are a user who absolutely needs this to
get a specific effect, please submit an issue on Github. This means that the public
interface is insufficient.
get a specific effect, please submit an issue on Github. Perhaps the public
interface is insufficient and should be extended for your use case.
Unlike the normal interface, the unsafe_access interface may change between versions.
If your code relies on unsafe_access, it may or may not break when you update Boost.
This is another reason to not use it unless you are ok with these conditions.
*/
struct unsafe_access {
/**
@@ -91,6 +95,15 @@ struct unsafe_access {
static constexpr auto& storage_adaptor_impl(storage_adaptor<T>& storage) {
return static_cast<typename storage_adaptor<T>::impl_type&>(storage);
}
/**
Get implementation of axis::variant.
@param axis instance of axis::variant.
*/
template <class Variant>
static constexpr auto& axis_variant_impl(Variant& axis) {
return axis.impl;
}
};
} // namespace histogram