mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-09 23:04:07 +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:
parent
073dffdf09
commit
170199bb9f
@ -22,14 +22,15 @@ install:
|
||||
|
||||
# Replacing Boost module with this project and installing Boost dependencies
|
||||
- xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram\
|
||||
- python tools\boostdep\depinst\depinst.py --git_args "--depth 10 --jobs 2" histogram
|
||||
- python tools\boostdep\depinst\depinst.py -N units -N range -N accumulators --git_args "--depth 10 --jobs 2" histogram
|
||||
|
||||
# Adding missing toolsets and preparing Boost headers
|
||||
- cmd /c bootstrap
|
||||
- b2 headers
|
||||
|
||||
test_script:
|
||||
- b2 -j2 -q libs\histogram\test
|
||||
- cd libs\histogram\test
|
||||
- ..\..\..\b2 -j2 -q minimal serialization
|
||||
|
||||
on_failure:
|
||||
## Uncomment the following line to stop VM and enable interactive login
|
||||
|
@ -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
|
||||
|
12
test/Jamfile
12
test/Jamfile
@ -34,8 +34,6 @@ project
|
||||
;
|
||||
|
||||
alias cxx14 :
|
||||
[ compile-fail make_histogram_fail0.cpp ]
|
||||
[ compile-fail make_histogram_fail1.cpp ]
|
||||
[ run algorithm_project_test.cpp ]
|
||||
[ run algorithm_reduce_test.cpp ]
|
||||
[ run algorithm_sum_test.cpp ]
|
||||
@ -52,6 +50,7 @@ alias cxx14 :
|
||||
[ run detail_iterator_adaptor_test.cpp ]
|
||||
[ run detail_large_int_test.cpp ]
|
||||
[ run detail_linearize_test.cpp ]
|
||||
[ run detail_variant_test.cpp ]
|
||||
[ run histogram_dynamic_test.cpp ]
|
||||
[ run histogram_growing_test.cpp ]
|
||||
[ run histogram_mixed_test.cpp ]
|
||||
@ -68,6 +67,12 @@ alias cxx17 :
|
||||
[ run deduction_guides_test.cpp ] :
|
||||
[ requires cpp_deduction_guides ]
|
||||
;
|
||||
|
||||
# check that useful error messages are produced when library is used incorrectly
|
||||
alias failure :
|
||||
[ compile-fail make_histogram_fail0.cpp ]
|
||||
[ compile-fail make_histogram_fail1.cpp ]
|
||||
;
|
||||
|
||||
alias threading :
|
||||
[ run histogram_threaded_test.cpp ]
|
||||
@ -83,17 +88,20 @@ alias serialization :
|
||||
[ run storage_adaptor_serialization_test.cpp libserial ]
|
||||
[ run histogram_serialization_test.cpp libserial ]
|
||||
[ run unlimited_storage_serialization_test.cpp libserial ]
|
||||
[ run detail_variant_serialization_test.cpp libserial ]
|
||||
;
|
||||
|
||||
alias libserial :
|
||||
/boost/serialization//boost_serialization :
|
||||
<link>static ;
|
||||
|
||||
# "failure" not included in "all", because it is distracting
|
||||
alias all : cxx14 cxx17 threading accumulators range units serialization ;
|
||||
alias minimal : cxx14 cxx17 threading ;
|
||||
|
||||
explicit cxx14 ;
|
||||
explicit cxx17 ;
|
||||
explicit failure ;
|
||||
explicit threading ;
|
||||
explicit accumulators ;
|
||||
explicit range ;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <boost/histogram/detail/type_name.hpp>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@ -22,7 +23,17 @@
|
||||
using namespace boost::histogram;
|
||||
namespace tr = axis::transform;
|
||||
|
||||
namespace std {
|
||||
template <class T>
|
||||
ostream& operator<<(ostream& os, const reference_wrapper<T>& t) {
|
||||
os << t.get();
|
||||
return os;
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
int main() {
|
||||
{ (void)axis::variant<>{}; }
|
||||
|
||||
{
|
||||
using meta_type = std::vector<int>;
|
||||
auto a = axis::variant<axis::integer<double>, axis::category<std::string, meta_type>>{
|
||||
@ -50,20 +61,36 @@ int main() {
|
||||
BOOST_TEST_EQ(a.options(), axis::option::overflow_t::value);
|
||||
}
|
||||
|
||||
// axis::variant with reference
|
||||
// axis::variant with std::reference_wrapper
|
||||
{
|
||||
using A = axis::integer<>;
|
||||
using V = axis::variant<A&>;
|
||||
using B = axis::regular<>;
|
||||
auto a = A(1, 5, "foo");
|
||||
V ref(a);
|
||||
BOOST_TEST_EQ(ref.size(), 4);
|
||||
BOOST_TEST_EQ(ref.value(0), 1);
|
||||
BOOST_TEST_EQ(ref.metadata(), a.metadata());
|
||||
BOOST_TEST_EQ(ref.options(), a.options());
|
||||
// change original through ref
|
||||
axis::get<A>(ref) = A(7, 14);
|
||||
BOOST_TEST_EQ(a.size(), 7);
|
||||
BOOST_TEST_EQ(a.value(0), 7);
|
||||
auto b = B(3, 1, 5, "bar");
|
||||
axis::variant<std::reference_wrapper<A>, std::reference_wrapper<B>> r1(std::ref(a));
|
||||
BOOST_TEST_EQ(r1, a);
|
||||
BOOST_TEST_NE(r1, A(2, 4));
|
||||
BOOST_TEST_NE(r1, b);
|
||||
BOOST_TEST_EQ(r1.size(), 4);
|
||||
BOOST_TEST_EQ(r1.value(0), 1);
|
||||
BOOST_TEST_EQ(r1.metadata(), a.metadata());
|
||||
BOOST_TEST_EQ(r1.options(), a.options());
|
||||
// change original through r1
|
||||
axis::get<A>(r1).metadata() = "bar";
|
||||
BOOST_TEST_EQ(a.metadata(), "bar");
|
||||
r1 = std::ref(b);
|
||||
BOOST_TEST_EQ(r1, b);
|
||||
|
||||
axis::variant<std::reference_wrapper<const A>, std::reference_wrapper<const B>> r2(
|
||||
std::cref(b));
|
||||
BOOST_TEST_EQ(r2, b);
|
||||
BOOST_TEST_NE(r2, B(4, 1, 5));
|
||||
BOOST_TEST_NE(r2, a);
|
||||
BOOST_TEST_EQ(r2.size(), 3);
|
||||
BOOST_TEST_EQ(r2.value(0), 1);
|
||||
BOOST_TEST_EQ(r2.metadata(), "bar");
|
||||
b.metadata() = "baz";
|
||||
BOOST_TEST_EQ(r2.metadata(), "baz");
|
||||
}
|
||||
|
||||
// axis::variant copyable
|
||||
|
@ -27,11 +27,6 @@
|
||||
using namespace boost::histogram;
|
||||
using namespace boost::histogram::detail;
|
||||
|
||||
struct VisitorTestFunctor {
|
||||
template <typename T>
|
||||
T operator()(T&&);
|
||||
};
|
||||
|
||||
int main() {
|
||||
// has_method_value*
|
||||
{
|
||||
@ -310,22 +305,6 @@ int main() {
|
||||
BOOST_TEST_TRAIT_SAME(arg_type<decltype(&Foo::f2)>, long);
|
||||
}
|
||||
|
||||
// visitor_return_type
|
||||
{
|
||||
using V1 = axis::variant<char>;
|
||||
using V2 = axis::variant<int>&;
|
||||
using V3 = const axis::variant<long>&;
|
||||
using V4 = axis::variant<const char&>;
|
||||
using V5 = axis::variant<const char&>&;
|
||||
using V6 = const axis::variant<const char&>&;
|
||||
BOOST_TEST_TRAIT_SAME(visitor_return_type<VisitorTestFunctor, V1>, char);
|
||||
BOOST_TEST_TRAIT_SAME(visitor_return_type<VisitorTestFunctor, V2>, int&);
|
||||
BOOST_TEST_TRAIT_SAME(visitor_return_type<VisitorTestFunctor, V3>, const long&);
|
||||
BOOST_TEST_TRAIT_SAME(visitor_return_type<VisitorTestFunctor, V4>, const char&);
|
||||
BOOST_TEST_TRAIT_SAME(visitor_return_type<VisitorTestFunctor, V5>, const char&);
|
||||
BOOST_TEST_TRAIT_SAME(visitor_return_type<VisitorTestFunctor, V6>, const char&);
|
||||
}
|
||||
|
||||
// static_if
|
||||
{
|
||||
struct callable {
|
||||
|
@ -41,8 +41,11 @@ int main() {
|
||||
auto a1 = axis::integer<>(0, 1);
|
||||
auto a2 = axis::integer<>(1, 2);
|
||||
auto tup = std::make_tuple(a1, a2);
|
||||
using E1 = axis::variant<axis::integer<>&>;
|
||||
using E1 = axis::variant<std::reference_wrapper<axis::integer<>>>;
|
||||
BOOST_TEST_TRAIT_SAME(decltype(detail::axis_get(tup, 0)), E1);
|
||||
BOOST_TEST_EQ(detail::axis_get(tup, 0), a1);
|
||||
BOOST_TEST_EQ(detail::axis_get(tup, 1), a2);
|
||||
BOOST_TEST_NE(detail::axis_get(tup, 0), a2);
|
||||
}
|
||||
|
||||
// sequence equality
|
||||
|
33
test/detail_variant_serialization_test.cpp
Normal file
33
test/detail_variant_serialization_test.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
// 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)
|
||||
|
||||
// This test is inspired by the corresponding boost/beast test of detail_variant.
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/detail/variant.hpp>
|
||||
#include <boost/histogram/detail/variant_serialization.hpp>
|
||||
#include "utility_serialization.hpp"
|
||||
|
||||
using namespace boost::histogram::detail;
|
||||
|
||||
int main() {
|
||||
const char* filename = XML_PATH "detail_variant_serialization_test.xml";
|
||||
|
||||
{
|
||||
variant<int, double> a(1.0);
|
||||
print_xml(filename, a);
|
||||
|
||||
variant<int, double> b(42);
|
||||
BOOST_TEST_NE(a, b);
|
||||
load_xml(filename, b);
|
||||
BOOST_TEST_EQ(a, b);
|
||||
|
||||
variant<int> c; // load incompatible version
|
||||
BOOST_TEST_THROWS(load_xml(filename, c), boost::archive::archive_exception);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
8
test/detail_variant_serialization_test.xml
Normal file
8
test/detail_variant_serialization_test.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<!DOCTYPE boost_serialization>
|
||||
<boost_serialization signature="serialization::archive" version="17">
|
||||
<item class_id="0" tracking_level="1" version="0" object_id="_0">
|
||||
<which>1</which>
|
||||
<value>1.00000000000000000e+00</value>
|
||||
</item>
|
||||
</boost_serialization>
|
238
test/detail_variant_test.cpp
Normal file
238
test/detail_variant_test.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
// 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)
|
||||
|
||||
// This test is inspired by the corresponding boost/beast test of detail_variant.
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/detail/variant.hpp>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace boost::histogram::detail;
|
||||
using namespace std::literals;
|
||||
|
||||
template <int>
|
||||
struct Q {
|
||||
Q() noexcept { ++Q::count; }
|
||||
Q(const Q& q) : data(q.data) {
|
||||
if (q.data == 0xBAD) // simulate failing copy ctor
|
||||
throw std::bad_alloc{};
|
||||
++Q::count;
|
||||
}
|
||||
Q(Q&& q) noexcept {
|
||||
data = q.data;
|
||||
moved = true;
|
||||
++Q::count;
|
||||
}
|
||||
Q& operator=(const Q& q) {
|
||||
if (q.data == 0xBAD) // simulate failing copy ctor
|
||||
throw std::bad_alloc{};
|
||||
data = q.data;
|
||||
return *this;
|
||||
}
|
||||
Q& operator=(Q&& q) noexcept {
|
||||
data = q.data;
|
||||
moved = true;
|
||||
return *this;
|
||||
}
|
||||
~Q() { --Q::count; }
|
||||
|
||||
Q(int x) : Q() { data = x; }
|
||||
|
||||
operator int() const noexcept { return data; }
|
||||
|
||||
int data;
|
||||
bool moved = false;
|
||||
static int count;
|
||||
};
|
||||
|
||||
template <int N>
|
||||
int Q<N>::count = 0;
|
||||
|
||||
int main() {
|
||||
// test Q
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
{
|
||||
Q<1> q(5);
|
||||
BOOST_TEST_EQ(q, 5);
|
||||
BOOST_TEST_EQ(Q<1>::count, 1);
|
||||
Q<1> q2(q);
|
||||
BOOST_TEST_EQ(q2, 5);
|
||||
BOOST_TEST_NOT(q2.moved);
|
||||
Q<1> q3(std::move(q));
|
||||
BOOST_TEST_EQ(q3, 5);
|
||||
BOOST_TEST(q3.moved);
|
||||
Q<1> q4;
|
||||
q4 = Q<1>(3);
|
||||
BOOST_TEST_EQ(q4, 3);
|
||||
BOOST_TEST(q4.moved);
|
||||
Q<1> q5(0xBAD); // ok
|
||||
BOOST_TEST_THROWS((Q<1>(q5)), std::bad_alloc);
|
||||
}
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
|
||||
// default ctor and dtor
|
||||
{
|
||||
variant<Q<1>> v;
|
||||
BOOST_TEST_EQ(v.index(), 0);
|
||||
BOOST_TEST_EQ(Q<1>::count, 1);
|
||||
|
||||
variant<> v2;
|
||||
(void)v2;
|
||||
}
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
|
||||
// copy ctor
|
||||
{
|
||||
using V = variant<Q<1>, Q<2>>;
|
||||
Q<1> q1{5};
|
||||
const V v1(q1);
|
||||
BOOST_TEST_EQ(Q<1>::count, 2);
|
||||
BOOST_TEST_EQ(Q<2>::count, 0);
|
||||
BOOST_TEST_EQ(v1.index(), 0);
|
||||
BOOST_TEST_EQ(v1, q1);
|
||||
BOOST_TEST_NOT(v1.get<Q<1>>().moved);
|
||||
const Q<2> q2{3};
|
||||
V v2(q2);
|
||||
BOOST_TEST_EQ(Q<1>::count, 2);
|
||||
BOOST_TEST_EQ(Q<2>::count, 2);
|
||||
BOOST_TEST_EQ(v2.index(), 1);
|
||||
BOOST_TEST_EQ(v2, q2);
|
||||
BOOST_TEST_NOT(v2.get<Q<2>>().moved);
|
||||
V v3(v1);
|
||||
BOOST_TEST_EQ(v3.index(), 0);
|
||||
BOOST_TEST_EQ(v3, q1);
|
||||
BOOST_TEST_EQ(Q<1>::count, 3);
|
||||
BOOST_TEST_EQ(Q<2>::count, 2);
|
||||
BOOST_TEST_NOT(v3.get<Q<1>>().moved);
|
||||
Q<1> q4(0xBAD);
|
||||
BOOST_TEST_THROWS((V(q4)), std::bad_alloc);
|
||||
}
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
BOOST_TEST_EQ(Q<2>::count, 0);
|
||||
|
||||
// move ctor
|
||||
{
|
||||
using V = variant<Q<1>, Q<2>>;
|
||||
V v1(Q<1>{5});
|
||||
BOOST_TEST_EQ(v1.index(), 0);
|
||||
BOOST_TEST(v1.get<Q<1>>().moved);
|
||||
BOOST_TEST_EQ(v1, Q<1>{5});
|
||||
V v2(Q<2>{3});
|
||||
BOOST_TEST_EQ(v2.index(), 1);
|
||||
BOOST_TEST(v2.get<Q<2>>().moved);
|
||||
BOOST_TEST_EQ(v2, Q<2>{3});
|
||||
Q<1> q{4};
|
||||
V v3(q);
|
||||
BOOST_TEST_NOT(v3.get<Q<1>>().moved);
|
||||
V v4(std::move(v3));
|
||||
BOOST_TEST(v4.get<Q<1>>().moved);
|
||||
}
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
BOOST_TEST_EQ(Q<2>::count, 0);
|
||||
|
||||
// move assign
|
||||
{
|
||||
using V = variant<Q<1>, Q<2>>;
|
||||
V v;
|
||||
BOOST_TEST_EQ(v.index(), 0);
|
||||
BOOST_TEST_NOT(v.get<Q<1>>().moved);
|
||||
v = Q<1>{5};
|
||||
BOOST_TEST_EQ(v.index(), 0);
|
||||
BOOST_TEST_EQ(v, Q<1>{5});
|
||||
BOOST_TEST(v.get<Q<1>>().moved);
|
||||
BOOST_TEST_EQ(Q<1>::count, 1);
|
||||
BOOST_TEST_EQ(Q<2>::count, 0);
|
||||
v = Q<2>{3};
|
||||
BOOST_TEST_EQ(v.index(), 1);
|
||||
BOOST_TEST_EQ(v, Q<2>{3});
|
||||
BOOST_TEST(v.get<Q<2>>().moved);
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
BOOST_TEST_EQ(Q<2>::count, 1);
|
||||
}
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
BOOST_TEST_EQ(Q<2>::count, 0);
|
||||
|
||||
// copy assign
|
||||
{
|
||||
using V = variant<Q<1>, Q<2>, Q<3>>;
|
||||
V v;
|
||||
BOOST_TEST_EQ(v.index(), 0);
|
||||
Q<1> q{3};
|
||||
v = q;
|
||||
BOOST_TEST_EQ(v.index(), 0);
|
||||
BOOST_TEST_EQ(v, q);
|
||||
BOOST_TEST_NOT(v.get<Q<1>>().moved);
|
||||
BOOST_TEST_EQ(Q<1>::count, 2);
|
||||
BOOST_TEST_EQ(Q<2>::count, 0);
|
||||
Q<2> q2(5);
|
||||
v = q2;
|
||||
BOOST_TEST_EQ(v.index(), 1);
|
||||
BOOST_TEST_EQ(v, q2);
|
||||
BOOST_TEST_NOT(v.get<Q<2>>().moved);
|
||||
BOOST_TEST_EQ(Q<1>::count, 1);
|
||||
BOOST_TEST_EQ(Q<2>::count, 2);
|
||||
|
||||
BOOST_TEST_EQ(v.index(), 1);
|
||||
Q<3> q3(0xBAD);
|
||||
BOOST_TEST_THROWS(v = q3, std::bad_alloc);
|
||||
BOOST_TEST_EQ(v.index(), 0); // is now in default state
|
||||
}
|
||||
BOOST_TEST_EQ(Q<1>::count, 0);
|
||||
BOOST_TEST_EQ(Q<2>::count, 0);
|
||||
|
||||
// get
|
||||
{
|
||||
variant<Q<1>, Q<2>> v;
|
||||
v = Q<1>(1);
|
||||
BOOST_TEST_EQ(v.get<Q<1>>(), 1);
|
||||
BOOST_TEST_THROWS(v.get<Q<2>>(), std::runtime_error);
|
||||
|
||||
const auto& crv = v;
|
||||
BOOST_TEST_EQ(crv.get<Q<1>>(), 1);
|
||||
BOOST_TEST_THROWS(crv.get<Q<2>>(), std::runtime_error);
|
||||
|
||||
auto p1 = v.get_if<Q<1>>();
|
||||
BOOST_TEST(p1 && *p1 == 1);
|
||||
p1->data = 3;
|
||||
|
||||
auto p2 = crv.get_if<Q<1>>();
|
||||
BOOST_TEST(p2 && *p2 == 3);
|
||||
|
||||
BOOST_TEST_NOT(v.get_if<Q<2>>());
|
||||
BOOST_TEST_NOT(v.get_if<int>());
|
||||
BOOST_TEST_NOT(crv.get_if<Q<2>>());
|
||||
BOOST_TEST_NOT(crv.get_if<int>());
|
||||
}
|
||||
|
||||
// apply
|
||||
{
|
||||
variant<Q<1>, Q<2>> v;
|
||||
v = Q<1>(1);
|
||||
v.apply([](auto& x) {
|
||||
BOOST_TEST_EQ(x, 1);
|
||||
BOOST_TEST_TRAIT_SAME(decltype(x), Q<1>&);
|
||||
});
|
||||
v = Q<2>(2);
|
||||
const auto& crv = v;
|
||||
crv.apply([](const auto& x) {
|
||||
BOOST_TEST_EQ(x, 2);
|
||||
BOOST_TEST_TRAIT_SAME(decltype(x), const Q<2>&);
|
||||
});
|
||||
}
|
||||
|
||||
// ostream
|
||||
{
|
||||
std::ostringstream os;
|
||||
variant<Q<1>, Q<2>> v(Q<1>{3});
|
||||
os << v;
|
||||
BOOST_TEST_EQ(os.str(), "3"s);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
@ -4,7 +4,7 @@ if [ -z $GCOV ]; then
|
||||
GCOV=gcov
|
||||
fi
|
||||
|
||||
LCOV_VERSION="1.13"
|
||||
LCOV_VERSION="1.14"
|
||||
LCOV_DIR="tools/lcov-${LCOV_VERSION}"
|
||||
|
||||
if [ ! -e $LCOV_DIR ]; then
|
||||
|
Loading…
x
Reference in New Issue
Block a user