diff --git a/examples/getting_started_listing_02.cpp b/examples/getting_started_listing_02.cpp index bfb32e47..ea9e9783 100644 --- a/examples/getting_started_listing_02.cpp +++ b/examples/getting_started_listing_02.cpp @@ -10,42 +10,42 @@ namespace br = boost::random; namespace bh = boost::histogram; int main() { - /* - create a dynamic histogram with the factory `make_dynamic_histogram` - - axis can be passed directly just like for `make_static_histogram` - - in addition, the factory also accepts iterators over a sequence of - axis::any, the polymorphic type that can hold concrete axis types - */ - std::vector axes; - axes.emplace_back(bh::axis::category({"red", "blue"})); - axes.emplace_back(bh::axis::regular<>(5, -5, 5, "x")); - axes.emplace_back(bh::axis::regular<>(5, -5, 5, "y")); - auto h = bh::make_dynamic_histogram(axes.begin(), axes.end()); + /* + create a dynamic histogram with the factory `make_dynamic_histogram` + - axis can be passed directly just like for `make_static_histogram` + - in addition, the factory also accepts iterators over a sequence of + axis::any, the polymorphic type that can hold concrete axis types + */ + std::vector axes; + axes.emplace_back(bh::axis::category({"red", "blue"})); + axes.emplace_back(bh::axis::regular<>(5, -5, 5, "x")); + axes.emplace_back(bh::axis::regular<>(5, -5, 5, "y")); + auto h = bh::make_dynamic_histogram(axes.begin(), axes.end()); - // fill histogram with random numbers - br::mt19937 gen; - br::normal_distribution<> norm; - for (int i = 0; i < 1000; ++i) - h(i % 2 ? "red" : "blue", norm(gen), norm(gen)); + // fill histogram with random numbers + br::mt19937 gen; + br::normal_distribution<> norm; + for (int i = 0; i < 1000; ++i) + h(i % 2 ? "red" : "blue", norm(gen), norm(gen)); - /* - print dynamic histogram by iterating over bins - - for most axis types, the for loop looks just like for a static - histogram, except that we can pass runtime numbers, too - - if the [bin type] of the axis is not convertible to a - double interval, one needs to cast axis::any before looping; - this is here the case for the category axis - */ - using cas = bh::axis::category; - for (auto cbin : bh::axis::cast(h.axis(0))) { - std::printf("%s\n", cbin.value().c_str()); - for (auto ybin : h.axis(2)) { // rows - for (auto xbin : h.axis(1)) { // columns - std::printf("%3.0f ", h.at(cbin, xbin, ybin).value()); - } - std::printf("\n"); - } + /* + print dynamic histogram by iterating over bins + - for most axis types, the for loop looks just like for a static + histogram, except that we can pass runtime numbers, too + - if the [bin type] of the axis is not convertible to a + double interval, one needs to cast axis::any before looping; + this is here the case for the category axis + */ + using cas = bh::axis::category; + for (auto cbin : static_cast(h.axis(0))) { + std::printf("%s\n", cbin.value().c_str()); + for (auto ybin : h.axis(2)) { // rows + for (auto xbin : h.axis(1)) { // columns + std::printf("%3.0f ", h.at(cbin, xbin, ybin).value()); + } + std::printf("\n"); } + } } //] diff --git a/include/boost/histogram/axis/any.hpp b/include/boost/histogram/axis/any.hpp index 643a6e24..936579db 100644 --- a/include/boost/histogram/axis/any.hpp +++ b/include/boost/histogram/axis/any.hpp @@ -15,9 +15,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include @@ -32,16 +30,16 @@ class access; namespace boost { namespace histogram { namespace axis { + namespace detail { -// this is evil -struct offset_visitor : public boost::static_visitor { - const char* ptr; - template - offset_visitor(const T* t) : ptr(reinterpret_cast(t)) {} - template - int operator()(const Axis& a) const { - return reinterpret_cast(&a) - ptr; +// this visitation may be collapsed by the compiler almost into a direct cast, +// works with gcc-8 -O2 -DNDEBUG on Compiler Explorer at least +template +struct static_cast_visitor : public boost::static_visitor { + template + T operator()(A&& a) const { + return static_cast(std::forward(a)); } }; @@ -54,7 +52,7 @@ struct index_visitor : public boost::static_visitor { } template int impl(std::true_type, const Axis& a) const { - return a.index(static_cast(x)); + return a.index(x); } template int impl(std::false_type, const Axis&) const { @@ -75,7 +73,7 @@ struct lower_visitor : public boost::static_visitor { bool, (std::is_convertible::value && std::is_same>::value)>(), + interval_view>::value)>(), a); } template @@ -87,7 +85,7 @@ struct lower_visitor : public boost::static_visitor { throw std::runtime_error(boost::histogram::detail::cat( "cannot use ", boost::typeindex::type_id().pretty_name(), " with generic boost::histogram::axis::any interface, use" - " boost::histogram::axis::cast to access underlying axis type")); + " a static_cast to access the underlying axis type")); } }; @@ -131,42 +129,21 @@ struct assign_visitor : public boost::static_visitor { /// Polymorphic axis type template class any : public boost::variant { - static_assert(boost::histogram::detail::is_common_base::value, - "all bounded types of axis::any must derive from axis::base"); + using base_type = boost::variant; - public: +public: using types = mp11::mp_list; using value_type = double; using bin_type = interval_view; using const_iterator = iterator_over; using const_reverse_iterator = reverse_iterator_over; - private: - using base_type = boost::variant; - +private: template using requires_bounded_type = mp11::mp_if< - mp11::mp_contains>, - void>; + mp11::mp_contains>, void>; - const base& cast_to_base() const { - // evil implementation until proper support can be added to boost::variant - if (offset_ == -1) { - offset_ = boost::apply_visitor(detail::offset_visitor(this), *this); - } - return *reinterpret_cast(reinterpret_cast(this) + - offset_); - } - - base& cast_to_base() { - // evil implementation until proper support can be added to boost::variant - if (offset_ == -1) { - offset_ = boost::apply_visitor(detail::offset_visitor(this), *this); - } - return *reinterpret_cast(reinterpret_cast(this) + offset_); - } - - public: +public: any() = default; any(const any&) = default; any& operator=(const any&) = default; @@ -193,21 +170,23 @@ class any : public boost::variant { return *this; } - int size() const { return cast_to_base().size(); } + int size() const { return static_cast(*this).size(); } - int shape() const { return cast_to_base().shape(); } + int shape() const { return static_cast(*this).shape(); } - bool uoflow() const { return cast_to_base().uoflow(); } - - string_view label() const { return cast_to_base().label(); } - - void label(const string_view x) { cast_to_base().label(x); } + bool uoflow() const { return static_cast(*this).uoflow(); } // note: this only works for axes with compatible value type int index(const value_type x) const { return boost::apply_visitor(detail::index_visitor(x), *this); } + string_view label() const { + return static_cast(*this).label(); + } + + void label(const string_view x) { static_cast(*this).label(x); } + // this only works for axes with compatible bin type // and will throw a runtime_error otherwise double lower(int idx) const { @@ -227,14 +206,33 @@ class any : public boost::variant { template > bool operator==(const T& t) const { - // variant::operator==(T) is implemented, but only to fail, cannot use it - auto tp = boost::get>(this); + // variant::operator==(T) is implemented only to fail, we cannot use it + auto tp = boost::get(this); return tp && *tp == t; } template - bool operator!=(T&& t) const { - return !operator==(std::forward(t)); + bool operator!=(const T& t) const { + return !operator==(t); + } + + explicit operator const base&() const { + return boost::apply_visitor(detail::static_cast_visitor(), + *this); + } + + explicit operator base&() { + return boost::apply_visitor(detail::static_cast_visitor(), *this); + } + + template + explicit operator const T&() const { + return boost::strict_get(*this); + } + + template + explicit operator T&() { + return boost::strict_get(*this); } const_iterator begin() const { return const_iterator(*this, 0); } @@ -246,46 +244,12 @@ class any : public boost::variant { return const_reverse_iterator(*this, 0); } - private: - mutable int offset_ = -1; +private: friend class boost::serialization::access; template void serialize(Archive&, unsigned); }; -// dynamic casts -template -T&& cast(any&& x) { - return boost::strict_get(x); -} - -template -T& cast(any& x) { - return boost::strict_get(x); -} - -template -const T& cast(const any& x) { - return boost::strict_get(x); -} - -template -T* cast(any* x) { - return boost::strict_get(&x); -} - -template -const T* cast(const any* x) { - return boost::strict_get(&x); -} - -// pass-through for generic programming, to keep code workgin when -// you switch from dynamic to static histogram -template -auto cast(U&& u) -> decltype(std::forward(u)) { - return std::forward(u); -} - } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 696ca2f9..a9b9b2fd 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -117,11 +117,6 @@ using copy_qualifiers = mp11::mp_if< T2&>, mp11::mp_if, const T2, T2>>>; -template -using is_common_base = - mp11::mp_all, - mp11::mp_bind_front>>; - template using mp_set_union = mp11::mp_apply_q, L>; diff --git a/test/axis_test.cpp b/test/axis_test.cpp index bf9f18ad..4c534005 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -331,7 +331,7 @@ int main() { std::string a = "A", b = "B"; axis::any_std x = axis::category({a, b}, "category"); BOOST_TEST_THROWS(x.index(1.5), std::runtime_error); - const auto& cx = axis::cast>(x); + auto cx = static_cast&>(x); BOOST_TEST_EQ(cx.index(b), 1); } diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 1ded757a..0cce79fa 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -48,9 +48,7 @@ int expected_moved_from_dim(static_tag, int static_value) { return static_value; } -int expected_moved_from_dim(dynamic_tag, int) { - return 0; -} +int expected_moved_from_dim(dynamic_tag, int) { return 0; } template typename Histogram::element_type sum(const Histogram& h) { @@ -212,8 +210,8 @@ void run_tests() { BOOST_TEST_EQ(c.axis().index(B), 1); c.axis().label("foo"); BOOST_TEST_EQ(c.axis().label(), "foo"); - // need to cast here for this to work with Type == dynamic_tag - auto ca = axis::cast>(c.axis()); + // need to cast here for this to work with Type == dynamic_tag, too + auto ca = static_cast&>(c.axis()); BOOST_TEST_EQ(ca[0].value(), A); } @@ -827,8 +825,7 @@ void run_tests() { // STL compatibility { auto h = make_histogram(Type(), axis::integer<>(0, 3)); - for (int i = 0; i < 3; ++i) - h(i); + for (int i = 0; i < 3; ++i) h(i); auto a = std::vector>(); std::partial_sum(h.begin(), h.end(), std::back_inserter(a)); BOOST_TEST_EQ(a[0].value(), 1);