// Copyright 2015-2018 Hans Dembinski // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_HISTOGRAM_DETAIL_META_HPP #define BOOST_HISTOGRAM_DETAIL_META_HPP /* Most of the histogram code is generic and works for any number of axes. Buffers with a * fixed maximum capacity are used in some places, which have a size equal to the rank of * a histogram. The buffers are statically allocated to improve performance, which means * that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank * (you can nevertheless increase it here if necessary): the simplest non-trivial axis has * 2 bins; even if counters are used which need only a byte of storage per bin, this still * corresponds to 4 GB of storage. */ #ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT #define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32 #endif #include #if BOOST_WORKAROUND(BOOST_GCC, >= 60000) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnoexcept-type" #endif #include #include #if BOOST_WORKAROUND(BOOST_GCC, >= 60000) #pragma GCC diagnostic pop #endif #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace histogram { namespace detail { template using remove_cvref_t = std::remove_cv_t>; template using convert_integer = mp11::mp_if>, U, T>; // to be replaced by official version from mp11 template class F, class... Ts> using mp_eval_or = mp11::mp_eval_if_c::value), E, F, Ts...>; template using copy_qualifiers = mp11::mp_if< std::is_rvalue_reference, T2&&, mp11::mp_if, mp11::mp_if::type>, const T2&, T2&>, mp11::mp_if, const T2, T2>>>; template using mp_last = mp11::mp_at_c::value - 1)>; template > using args_type = mp11::mp_if, mp11::mp_pop_front, Args>; template using arg_type = typename mp11::mp_at_c, N>; template using return_type = typename boost::callable_traits::return_type::type; template >>> using visitor_return_type = decltype(std::declval()(std::declval())); template constexpr decltype(auto) static_if_c(T&& t, F&& f, Ts&&... ts) { return std::get<(B ? 0 : 1)>(std::forward_as_tuple( std::forward(t), std::forward(f)))(std::forward(ts)...); } template constexpr decltype(auto) static_if(Ts&&... ts) { return static_if_c(std::forward(ts)...); } template constexpr T lowest() { return std::numeric_limits::lowest(); } template <> constexpr double lowest() { return -std::numeric_limits::infinity(); } template <> constexpr float lowest() { return -std::numeric_limits::infinity(); } template constexpr T highest() { return std::numeric_limits::max(); } template <> constexpr double highest() { return std::numeric_limits::infinity(); } template <> constexpr float highest() { return std::numeric_limits::infinity(); } template decltype(auto) tuple_slice_impl(T&& t, mp11::index_sequence) { return std::forward_as_tuple(std::get<(N + I)>(std::forward(t))...); } template decltype(auto) tuple_slice(T&& t) { static_assert(I + N <= mp11::mp_size>::value, "I and N must describe a slice"); return tuple_slice_impl(std::forward(t), mp11::make_index_sequence{}); } #define BOOST_HISTOGRAM_DETECT(name, cond) \ template \ struct name##_impl {}; \ template \ using name = typename mp11::mp_valid #define BOOST_HISTOGRAM_DETECT_BINARY(name, cond) \ template \ struct name##_impl {}; \ template \ using name = typename mp11::mp_valid BOOST_HISTOGRAM_DETECT(has_method_metadata, (std::declval().metadata())); // resize has two overloads, trying to get pmf in this case always fails BOOST_HISTOGRAM_DETECT(has_method_resize, (std::declval().resize(0))); BOOST_HISTOGRAM_DETECT(has_method_size, &T::size); BOOST_HISTOGRAM_DETECT(has_method_clear, &T::clear); BOOST_HISTOGRAM_DETECT(has_method_lower, &T::lower); BOOST_HISTOGRAM_DETECT(has_method_value, &T::value); BOOST_HISTOGRAM_DETECT(has_method_update, (&T::update)); BOOST_HISTOGRAM_DETECT(has_method_reset, (std::declval().reset(0))); template using get_value_method_return_type_impl = decltype(std::declval().value(0)); template using has_method_value_with_convertible_return_type = typename std::is_convertible, R>::type; BOOST_HISTOGRAM_DETECT(has_method_options, (&T::options)); BOOST_HISTOGRAM_DETECT(has_allocator, &T::get_allocator); BOOST_HISTOGRAM_DETECT(is_indexable, (std::declval()[0])); BOOST_HISTOGRAM_DETECT(is_transform, (&T::forward, &T::inverse)); BOOST_HISTOGRAM_DETECT(is_indexable_container, (std::declval()[0], &T::size, std::begin(std::declval()), std::end(std::declval()))); BOOST_HISTOGRAM_DETECT(is_vector_like, (std::declval()[0], &T::size, std::declval().resize(0), std::begin(std::declval()), std::end(std::declval()))); BOOST_HISTOGRAM_DETECT(is_array_like, (std::declval()[0], &T::size, std::tuple_size::value, std::begin(std::declval()), std::end(std::declval()))); BOOST_HISTOGRAM_DETECT(is_map_like, (std::declval(), std::declval(), std::begin(std::declval()), std::end(std::declval()))); // ok: is_axis is false for axis::variant, operator() is templated BOOST_HISTOGRAM_DETECT(is_axis, (&T::size, &T::index)); BOOST_HISTOGRAM_DETECT(is_iterable, (std::begin(std::declval()), std::end(std::declval()))); BOOST_HISTOGRAM_DETECT(is_iterator, (typename std::iterator_traits::iterator_category())); BOOST_HISTOGRAM_DETECT(is_streamable, (std::declval() << std::declval())); BOOST_HISTOGRAM_DETECT(is_incrementable, (++std::declval())); BOOST_HISTOGRAM_DETECT(has_operator_preincrement, (++std::declval())); BOOST_HISTOGRAM_DETECT_BINARY(has_operator_equal, (std::declval() == std::declval())); BOOST_HISTOGRAM_DETECT_BINARY(has_operator_radd, (std::declval() += std::declval())); BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rsub, (std::declval() -= std::declval())); BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rmul, (std::declval() *= std::declval())); BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rdiv, (std::declval() /= std::declval())); template using is_storage = mp11::mp_bool<(is_indexable_container::value && has_method_reset::value)>; template struct is_tuple_impl : std::false_type {}; template struct is_tuple_impl> : std::true_type {}; template using is_tuple = typename is_tuple_impl::type; template struct is_axis_variant_impl : std::false_type {}; template struct is_axis_variant_impl> : std::true_type {}; template using is_axis_variant = typename is_axis_variant_impl::type; template using is_any_axis = mp11::mp_or, is_axis_variant>; template using is_sequence_of_axis = mp11::mp_and, is_axis>>; template using is_sequence_of_axis_variant = mp11::mp_and, is_axis_variant>>; template using is_sequence_of_any_axis = mp11::mp_and, is_any_axis>>; template struct is_weight_impl : std::false_type {}; template struct is_weight_impl> : std::true_type {}; template using is_weight = is_weight_impl>; template struct is_sample_impl : std::false_type {}; template struct is_sample_impl> : std::true_type {}; template using is_sample = is_sample_impl>; // poor-mans concept checks template >::value>> struct requires_iterator {}; template >::value>> struct requires_iterable {}; template >::value>> struct requires_axis {}; template >::value>> struct requires_any_axis {}; template >::value>> struct requires_sequence_of_axis {}; template >::value>> struct requires_sequence_of_axis_variant {}; template >::value>> struct requires_sequence_of_any_axis {}; template >>::value>> struct requires_axes {}; template ::value>> struct requires_convertible {}; template auto make_default(const T& t) { return static_if>([](const auto& t) { return T(t.get_allocator()); }, [](const auto&) { return T(); }, t); } template using tuple_size_t = typename std::tuple_size::type; template constexpr std::size_t get_size_impl(std::true_type, const T&) noexcept { return std::tuple_size::value; } template std::size_t get_size_impl(std::false_type, const T& t) noexcept { using std::begin; using std::end; return static_cast(std::distance(begin(t), end(t))); } template std::size_t get_size(const T& t) noexcept { return get_size_impl(mp11::mp_valid(), t); } template using buffer_size = mp_eval_or, tuple_size_t, T>; template class sub_array : public std::array { public: explicit sub_array(std::size_t s) : size_(s) {} sub_array(std::size_t s, T value) : size_(s) { std::array::fill(value); } // need to override both versions of std::array auto end() noexcept { return std::array::begin() + size_; } auto end() const noexcept { return std::array::begin() + size_; } auto size() const noexcept { return size_; } private: std::size_t size_ = N; }; template using stack_buffer = sub_array::value>; template auto make_stack_buffer(const T& t, Ts&&... ts) { return stack_buffer(get_size(t), std::forward(ts)...); } template constexpr bool relaxed_equal(const T& a, const T& b) noexcept { return static_if>( [](const auto& a, const auto& b) { return a == b; }, [](const auto&, const auto&) { return true; }, a, b); } template using get_scale_type_helper = typename T::value_type; template using get_scale_type = mp_eval_or; struct one_unit {}; template T operator*(T&& t, const one_unit&) { return std::forward(t); } template T operator/(T&& t, const one_unit&) { return std::forward(t); } template using get_unit_type_helper = typename T::unit_type; template using get_unit_type = mp_eval_or; template > R get_scale(const T& t) { return t / get_unit_type(); } template using replace_default = mp11::mp_if, Default, T>; template using is_convertible_helper = mp11::mp_apply>; template using is_convertible = mp_eval_or; template auto make_unsigned_impl(std::true_type, const T t) noexcept { return static_cast::type>(t); } template auto make_unsigned_impl(std::false_type, const T t) noexcept { return t; } template auto make_unsigned(const T t) noexcept { return make_unsigned_impl(std::is_integral{}, t); } } // namespace detail } // namespace histogram } // namespace boost #endif