mirror of
https://github.com/boostorg/histogram.git
synced 2026-02-18 14:12:11 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
242
include/boost/histogram/detail/variant.hpp
Normal file
242
include/boost/histogram/detail/variant.hpp
Normal 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
|
||||
67
include/boost/histogram/detail/variant_serialization.hpp
Normal file
67
include/boost/histogram/detail/variant_serialization.hpp
Normal 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
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user