diff --git a/include/boost/histogram/axis/any.hpp b/include/boost/histogram/axis/any.hpp index c10f338a..9552f26a 100644 --- a/include/boost/histogram/axis/any.hpp +++ b/include/boost/histogram/axis/any.hpp @@ -7,10 +7,10 @@ #ifndef _BOOST_HISTOGRAM_AXIS_ANY_HPP_ #define _BOOST_HISTOGRAM_AXIS_ANY_HPP_ +#include #include #include #include -#include #include #include #include @@ -54,36 +54,47 @@ struct set_label : public static_visitor { template void operator()(A &a) const { a.label(label); } }; -template struct index : public static_visitor { - const T &t; - explicit index(const T &arg) : t(arg) {} +struct index : public static_visitor { + const double x; + explicit index(const double arg) : x(arg) {} template int operator()(const Axis &a) const { - return impl(std::is_convertible(), a); + return impl(std::is_convertible(), a); } template int impl(std::true_type, const Axis &a) const { - return a.index(t); + return a.index(x); } template int impl(std::false_type, const Axis &) const { throw std::runtime_error(::boost::histogram::detail::cat( - "fill argument not convertible to axis value type: ", - boost::typeindex::type_id().pretty_name(), ", ", - boost::typeindex::type_id().pretty_name())); + "cannot convert value_type ", + boost::typeindex::type_id().pretty_name(), + " of ", + boost::typeindex::type_id().pretty_name(), + " to double") + ); } }; -struct bin : public static_visitor> { - using double_interval = axis::interval; - const int i; - bin(const int v) : i(v) {} - template double_interval operator()(const A &a) const { - return impl(is_convertible(), - std::forward(a[i])); +struct eval : public static_visitor> { + template std::function operator()(const Axis &a) const { + return impl(std::integral_constant::value && + std::is_same< + typename Axis::bin_type, + interval_view + >::value) + >(), a); } - template double_interval impl(true_type, B &&b) const { - return b; + template std::function impl(std::true_type, const Axis &a) const { + return [&a](int i) -> double { return a[i].lower(); } ; } - template double_interval impl(false_type, B &&) const { - throw std::runtime_error("cannot convert bin_type to interval"); + template std::function impl(std::false_type, const Axis &) const { + throw std::runtime_error(::boost::histogram::detail::cat( + "cannot convert bin_type ", + boost::typeindex::type_id().pretty_name(), + " of ", + boost::typeindex::type_id().pretty_name(), + " to interval_view") + ); } }; } // namespace detail @@ -95,7 +106,7 @@ template class any : public make_variant_over::type { public: using types = typename base_type::types; using value_type = double; - using bin_type = interval; + using bin_type = interval_view; using const_iterator = iterator_over; using const_reverse_iterator = reverse_iterator_over; @@ -112,15 +123,15 @@ public: template ::value>::type> any &operator=(const T &t) { - // ugly workaround for compiler bug - return reinterpret_cast(base_type::operator=(t)); + base_type::operator=(t); + return *this; } template ::value>::type> any &operator=(T &&t) { - // ugly workaround for compiler bug - return reinterpret_cast(base_type::operator=(std::move(t))); + base_type::operator=(std::move(t)); + return *this; } int size() const { return apply_visitor(detail::size(), *this); } @@ -131,7 +142,7 @@ public: // note: this only works for axes with compatible value type int index(const value_type x) const { - return apply_visitor(detail::index(x), *this); + return apply_visitor(detail::index(x), *this); } string_view label() const { @@ -143,9 +154,10 @@ public: } // this only works for axes with compatible bin type - // and will raise an error otherwise + // and will throw a runtime_error otherwise bin_type operator[](const int i) const { - return apply_visitor(detail::bin(i), *this); + auto eval = apply_visitor(detail::eval(), *this); + return bin_type(i, eval); } bool operator==(const any &rhs) const { diff --git a/include/boost/histogram/axis/axis.hpp b/include/boost/histogram/axis/axis.hpp index 583221d0..783bcf27 100644 --- a/include/boost/histogram/axis/axis.hpp +++ b/include/boost/histogram/axis/axis.hpp @@ -9,9 +9,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -36,7 +36,7 @@ namespace axis { enum class uoflow { off = false, on = true }; -/// Base class for all axes, uses CRTP. +/// Base class for all axes, uses CRTP to inject iterator logic. template class axis_base { public: using const_iterator = iterator_over; @@ -102,7 +102,7 @@ private: template void serialize(Archive &, unsigned); }; -/// Base class for axes with overflow/underflow bins, uses CRTP. +/// Base class for axes with optional under-/overflow bins, uses CRTP. template class axis_base_uoflow : public axis_base { using base_type = axis_base; @@ -114,7 +114,7 @@ public: protected: axis_base_uoflow(unsigned n, string_view label, enum uoflow uo) - : base_type(n, label), shape_(n + 2u * static_cast(uo)) {} + : base_type(n, label), shape_(n + 2 * static_cast(uo)) {} axis_base_uoflow() = default; axis_base_uoflow(const axis_base_uoflow &) = default; @@ -202,7 +202,7 @@ class regular : public axis_base_uoflow>, public: using value_type = RealType; - using bin_type = interval; + using bin_type = interval_view; /** Construct axis with n bins over real range [lower, upper). * @@ -241,9 +241,9 @@ public: : -1; } - /// Returns the starting edge of the bin. + /// Returns the bin interval. bin_type operator[](int idx) const noexcept { - auto eval = [this](int i) { + return bin_type(idx, [this](int i) { const auto n = base_type::size(); if (i < 0) return this->inverse(-std::numeric_limits::infinity()); @@ -251,8 +251,7 @@ public: return this->inverse(std::numeric_limits::infinity()); const auto z = value_type(i) / n; return this->inverse((1.0 - z) * min_ + z * (min_ + delta_ * n)); - }; - return {eval(idx), eval(idx + 1)}; + }); } bool operator==(const regular &o) const noexcept { @@ -260,6 +259,7 @@ public: min_ == o.min_ && delta_ == o.delta_; } + /// Access properties of the transform. const Transform &transform() const noexcept { return static_cast(*this); } @@ -283,7 +283,7 @@ class circular : public axis_base> { public: using value_type = RealType; - using bin_type = interval; + using bin_type = interval_view; /** Constructor for n bins with an optional offset. * @@ -313,11 +313,10 @@ public: /// Returns the starting edge of the bin. bin_type operator[](int idx) const { - auto eval = [this](int i) { + return bin_type(idx, [this](int i) { const value_type z = value_type(i) / base_type::size(); return z * perimeter_ + phase_; - }; - return {eval(idx), eval(idx + 1)}; + }); } bool operator==(const circular &o) const noexcept { @@ -346,7 +345,7 @@ class variable : public axis_base_uoflow> { public: using value_type = RealType; - using bin_type = interval; + using bin_type = interval_view; /** Construct an axis from bin edges. * @@ -397,7 +396,7 @@ public: /// Returns the starting edge of the bin. bin_type operator[](int idx) const { - auto eval = [this](int i) { + return bin_type(idx, [this](int i) { if (i < 0) { return -std::numeric_limits::infinity(); } @@ -405,8 +404,7 @@ public: return std::numeric_limits::infinity(); } return x_[i]; - }; - return {eval(idx), eval(idx + 1)}; + }); } bool operator==(const variable &o) const noexcept { @@ -434,7 +432,7 @@ class integer : public axis_base_uoflow> { public: using value_type = IntType; - using bin_type = interval; + using bin_type = interval_view; /** Construct axis over a semi-open integer interval [lower, upper). * @@ -465,7 +463,7 @@ public: /// Returns the integer that is mapped to the bin index. bin_type operator[](int idx) const { - auto eval = [this](int i) { + return bin_type(idx, [this](int i) { if (i < 0) { return -std::numeric_limits::max(); } @@ -473,8 +471,7 @@ public: return std::numeric_limits::max(); } return min_ + i; - }; - return {eval(idx), eval(idx + 1)}; + }); } bool operator==(const integer &o) const noexcept { diff --git a/include/boost/histogram/axis/interval.hpp b/include/boost/histogram/axis/interval.hpp new file mode 100644 index 00000000..c5feed58 --- /dev/null +++ b/include/boost/histogram/axis/interval.hpp @@ -0,0 +1,46 @@ +// Copyright 2015-2017 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_AXIS_INTERVAL_HPP_ +#define _BOOST_HISTOGRAM_AXIS_INTERVAL_HPP_ + +#include +#include + +namespace boost { +namespace histogram { +namespace axis { + +template class interval_view { +public: + interval_view(int idx, std::function eval) : idx_(idx), eval_(eval) {} + + interval_view(const interval_view &) = default; + interval_view &operator=(const interval_view &) = default; + interval_view(interval_view &&) = default; + interval_view &operator=(interval_view &&) = default; + + T lower() const noexcept { return eval_(idx_); } + T upper() const noexcept { return eval_(idx_ + 1); } + T width() const noexcept { return upper() - lower(); } + + bool operator==(const interval_view &rhs) const noexcept { + return lower() == rhs.lower() && upper() == rhs.upper(); + } + bool operator!=(const interval_view &rhs) const noexcept { + return !operator==(rhs); + } + +private: + const int idx_; + const std::function eval_; +}; + +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index cbd9df58..50a6152f 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -10,8 +10,8 @@ #define _BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP_ #include +#include #include -#include #include #include @@ -26,6 +26,12 @@ inline string_view to_string(const transform::sqrt &) { return {"_sqrt", 5}; } inline string_view to_string(const transform::cos &) { return {"_cos", 4}; } } // namespace detail +template +inline std::ostream &operator<<(std::ostream &os, const interval_view &i) { + os << "[" << i.lower() << ", " << i.upper() << ")"; + return os; +} + template inline std::ostream &operator<<(std::ostream &os, const regular &a) { diff --git a/include/boost/histogram/detail/axis_visitor.hpp b/include/boost/histogram/detail/axis_visitor.hpp index 6618de12..38869368 100644 --- a/include/boost/histogram/detail/axis_visitor.hpp +++ b/include/boost/histogram/detail/axis_visitor.hpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/boost/histogram/interval.hpp b/include/boost/histogram/interval.hpp deleted file mode 100644 index c3113205..00000000 --- a/include/boost/histogram/interval.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015-2017 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_INTERVAL_HPP_ -#define _BOOST_HISTOGRAM_INTERVAL_HPP_ - -#include - -namespace boost { -namespace histogram { -namespace axis { - -template class interval { -public: - interval() = default; - interval(const interval &) = default; - interval &operator=(const interval &) = default; - interval(interval &&) = default; - interval &operator=(interval &&) = default; - - interval(const T &x, const T &y) : a(x), b(y) {} - interval(T &&x, T &&y) : a(std::move(x)), b(std::move(y)) {} - - template - interval(const interval &i) : a(i.lower()), b(i.upper()) {} - - const T &lower() const noexcept { return a; } - const T &upper() const noexcept { return b; } - - bool operator==(const interval &i) const noexcept { - return a == i.a && b == i.b; - } - bool operator!=(const interval &i) const noexcept { return !operator==(i); } - -private: - T a, b; -}; - -} // namespace axis -} // namespace histogram -} // namespace boost - -#endif diff --git a/src/python/axis.cpp b/src/python/axis.cpp index 450727a7..1467513b 100644 --- a/src/python/axis.cpp +++ b/src/python/axis.cpp @@ -55,25 +55,14 @@ generic_iterator make_generic_iterator(bp::object self) { } template -struct axis_interval_to_python +struct axis_interval_view_to_python { - static PyObject* convert(const bha::interval &i) + static PyObject* convert(const bha::interval_view &i) { return bp::incref(bp::make_tuple(i.lower(), i.upper()).ptr()); } }; -template -struct pair_int_axis_interval_to_python -{ - static PyObject* convert(const std::pair> &p) - { - return bp::incref(bp::make_tuple( - p.first, bp::make_tuple(p.second.lower(), p.second.upper()) - ).ptr()); - } -}; - bp::object variable_init(bp::tuple args, bp::dict kwargs) { bp::object self = args[0]; @@ -263,23 +252,13 @@ void register_axis_types() { docstring_options dopt(true, true, false); to_python_converter< - bha::interval, - axis_interval_to_python + bha::interval_view, + axis_interval_view_to_python >(); to_python_converter< - bha::interval, - axis_interval_to_python - >(); - - to_python_converter< - std::pair>, - pair_int_axis_interval_to_python - >(); - - to_python_converter< - std::pair>, - pair_int_axis_interval_to_python + bha::interval_view, + axis_interval_view_to_python >(); class_("generic_iterator", init()) diff --git a/test/axis_test.cpp b/test/axis_test.cpp index d34ca6c7..25513468 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -23,18 +23,6 @@ #define BOOST_TEST_NOT(expr) BOOST_TEST(!(expr)) #define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps) -namespace boost { -namespace histogram { -namespace axis { -template -std::ostream &operator<<(std::ostream &os, const interval &i) { - os << "[" << i.lower() << ", " << i.upper() << ")"; - return os; -} -} // namespace axis -} // namespace histogram -} // namespace boost - template void test_axis_iterator(const Axis &a, int begin, int end) { auto it = a.begin(); @@ -239,6 +227,7 @@ int main() { test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3); test_axis_iterator(any_axis_type(axis::regular<>(5, 0, 1)), 0, 5); + BOOST_TEST_THROWS(any_axis_type(axis::category<>({A, B, C}))[0], std::runtime_error); } // any_axis_type_copyable diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 517ba56b..02df5aac 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -882,8 +882,7 @@ int main() { { enum { A, B }; auto c = make_dynamic_histogram(axis::category<>({A, B})); - BOOST_TEST_THROWS(c.axis()[0].lower(), std::runtime_error); - BOOST_TEST_THROWS(c.axis()[0].upper(), std::runtime_error); + BOOST_TEST_THROWS(c.axis()[0], std::runtime_error); } // reduce