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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 811 additions and 151 deletions

View File

@ -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

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

View File

@ -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 ;

View File

@ -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

View File

@ -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 {

View File

@ -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

View 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();
}

View 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>

View 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();
}

View File

@ -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