From 8c2f0736e9079ab6d6863e1113ef7b2b54f112ab Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sun, 14 Oct 2018 21:37:11 +0200 Subject: [PATCH 01/44] wip: overhauled axis types, axis::any -> axis::variant, axis::traits provide features --- examples/guide_make_dynamic_histogram.cpp | 15 +- examples/guide_make_static_histogram.cpp | 7 +- include/boost/histogram.hpp | 2 +- include/boost/histogram/axis/any.hpp | 275 -------- include/boost/histogram/axis/base.hpp | 161 +++-- .../histogram/axis/ostream_operators.hpp | 99 +-- include/boost/histogram/axis/traits.hpp | 49 ++ include/boost/histogram/axis/types.hpp | 606 +++++++++--------- include/boost/histogram/axis/variant.hpp | 232 +++++++ include/boost/histogram/detail/axes.hpp | 7 +- include/boost/histogram/detail/buffer.hpp | 2 + include/boost/histogram/detail/meta.hpp | 29 +- include/boost/histogram/histogram.hpp | 117 ++-- include/boost/histogram/histogram_fwd.hpp | 54 +- include/boost/histogram/ostream_operators.hpp | 4 +- 15 files changed, 846 insertions(+), 813 deletions(-) delete mode 100644 include/boost/histogram/axis/any.hpp create mode 100644 include/boost/histogram/axis/traits.hpp create mode 100644 include/boost/histogram/axis/variant.hpp diff --git a/examples/guide_make_dynamic_histogram.cpp b/examples/guide_make_dynamic_histogram.cpp index 6e090e6c..dc007c86 100644 --- a/examples/guide_make_dynamic_histogram.cpp +++ b/examples/guide_make_dynamic_histogram.cpp @@ -7,20 +7,19 @@ namespace bh = boost::histogram; int main() { // create vector of axes, axis::any is a polymorphic axis type - auto v = std::vector(); + auto v = std::vector, bh::axis::integer<> + >>(); v.push_back(bh::axis::regular<>(100, -1, 1)); v.push_back(bh::axis::integer<>(1, 7)); - // create dynamic histogram (make_static_histogram cannot be used with iterators) - auto h = bh::make_dynamic_histogram(v.begin(), v.end()); + // create dynamic histogram from iterator range + auto h = bh::make_histogram(v.begin(), v.end()); // do something with h - - - // make_dynamic_histogram copies axis objects; to instead move the whole axis - // vector into the histogram, create a histogram instance directly - auto h2 = bh::histogram(std::move(v)); + // create dynamic histogram by moving the vector (this avoids copies) + auto h2 = bh::make_histogram(std::move(v)); // do something with h2 } diff --git a/examples/guide_make_static_histogram.cpp b/examples/guide_make_static_histogram.cpp index 35ac0468..bc32a22b 100644 --- a/examples/guide_make_static_histogram.cpp +++ b/examples/guide_make_static_histogram.cpp @@ -6,11 +6,10 @@ namespace bh = boost::histogram; int main() { /* - create a 1d-histogram in default configuration which - covers the real line from -1 to 1 in 100 bins, the same - call with `make_dynamic_histogram` would also work + create a static 1d-histogram in default configuration + which covers the real line from -1 to 1 in 100 bins */ - auto h = bh::make_static_histogram(bh::axis::regular<>(100, -1, 1)); + auto h = bh::make_histogram(bh::axis::regular<>(100, -1, 1)); // do something with h } diff --git a/include/boost/histogram.hpp b/include/boost/histogram.hpp index 23470091..5e05664d 100644 --- a/include/boost/histogram.hpp +++ b/include/boost/histogram.hpp @@ -7,7 +7,7 @@ #ifndef BOOST_HISTOGRAM_HPP #define BOOST_HISTOGRAM_HPP -#include +#include #include #include #include diff --git a/include/boost/histogram/axis/any.hpp b/include/boost/histogram/axis/any.hpp deleted file mode 100644 index ac068f24..00000000 --- a/include/boost/histogram/axis/any.hpp +++ /dev/null @@ -1,275 +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_AXIS_ANY_HPP -#define BOOST_HISTOGRAM_AXIS_ANY_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// forward declaration for serialization -namespace boost { -namespace serialization { -class access; -} // namespace serialization -} // namespace boost - -namespace boost { -namespace histogram { -namespace axis { - -namespace detail { - -// 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)); - } -}; - -struct index_visitor : public boost::static_visitor { - const double x; - explicit index_visitor(const double arg) : x(arg) {} - template - int operator()(const Axis& a) const { - return impl(std::is_convertible(), a); - } - template - int impl(std::true_type, const Axis& a) const { - return a.index(static_cast(x)); - } - template - int impl(std::false_type, const Axis&) const { - throw std::runtime_error(boost::histogram::detail::cat( - "cannot convert double to ", - boost::typeindex::type_id().pretty_name(), " for ", - boost::typeindex::type_id().pretty_name())); - } -}; - -struct lower_visitor : public boost::static_visitor { - int idx; - lower_visitor(int i) : idx(i) {} - template - double operator()(const Axis& a) const { - return impl( - std::integral_constant< - bool, (std::is_convertible::value && - std::is_same>::value)>(), - a); - } - template - double impl(std::true_type, const Axis& a) const { - return a.lower(idx); - } - template - double impl(std::false_type, const Axis&) const { - throw std::runtime_error(boost::histogram::detail::cat( - "cannot use ", boost::typeindex::type_id().pretty_name(), - " with generic boost::histogram::axis::any interface, use" - " a static_cast to access the underlying axis type")); - } -}; - -struct bicmp_visitor : public boost::static_visitor { - template - bool operator()(const T&, const U&) const { - return false; - } - - template - bool operator()(const T& a, const T& b) const { - return a == b; - } -}; - -template -struct assign_visitor : public boost::static_visitor { - T& t; - assign_visitor(T& tt) : t(tt) {} - template - void operator()(const U& u) const { - impl(mp11::mp_contains(), u); - } - - template - void impl(mp11::mp_true, const U& u) const { - t = u; - } - - template - void impl(mp11::mp_false, const U&) const { - throw std::invalid_argument(boost::histogram::detail::cat( - "argument ", boost::typeindex::type_id().pretty_name(), - " is not a bounded type of ", boost::typeindex::type_id().pretty_name())); - } -}; - -struct get_label_visitor : public boost::static_visitor { - template - boost::string_view operator()(const T& t) const { - return t.label(); - } -}; - -struct set_label_visitor : public boost::static_visitor { - boost::string_view view; - set_label_visitor(boost::string_view v) : view(v) {} - template - void operator()(T& t) const { - t.label(view); - } -}; - -template -struct get_allocator_visitor : public boost::static_visitor { - template - U operator()(const T& t) const { - return t.get_allocator(); - } -}; - -} // namespace detail - -/// Polymorphic axis type -template -class any : public boost::variant { - using base_type = boost::variant; - -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: - template - using requires_bounded_type = - mp11::mp_if>, void>; - -public: - any() = default; - any(const any&) = default; - any& operator=(const any&) = default; - any(any&&) = default; - any& operator=(any&&) = default; - - template > - any(T&& t) : base_type(std::forward(t)) {} - - template > - any& operator=(T&& t) { - base_type::operator=(std::forward(t)); - return *this; - } - - template - any(const any& u) { - boost::apply_visitor(detail::assign_visitor(*this), u); - } - - template - any& operator=(const any& u) { - boost::apply_visitor(detail::assign_visitor(*this), u); - return *this; - } - - int size() const { return static_cast(*this).size(); } - - int shape() const { return static_cast(*this).shape(); } - - 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 boost::apply_visitor(detail::get_label_visitor(), *this); - } - - void label(const string_view x) { - boost::apply_visitor(detail::set_label_visitor(x), *this); - } - - // this only works for axes with compatible bin type - // and will throw a runtime_error otherwise - double lower(int idx) const { - return boost::apply_visitor(detail::lower_visitor(idx), *this); - } - - bin_type operator[](const int idx) const { return bin_type(idx, *this); } - - bool operator==(const any& rhs) const { - return base_type::operator==(static_cast(rhs)); - } - - template - bool operator==(const any& u) const { - return boost::apply_visitor(detail::bicmp_visitor(), *this, u); - } - - template > - bool operator==(const T& t) const { - // variant::operator==(T) is implemented only to fail, we cannot use it - auto tp = boost::get(this); - return tp && *tp == t; - } - - template - 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); } - const_iterator end() const { return const_iterator(*this, size()); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(*this, size()); } - const_reverse_iterator rend() const { return const_reverse_iterator(*this, 0); } - -private: - friend class boost::serialization::access; - template - void serialize(Archive&, unsigned); -}; - -} // namespace axis -} // namespace histogram -} // namespace boost - -#endif diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index 43dd60ea..699576c8 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Hans Dembinski +// Copyright 2015-2018 Hans Dembinski // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt @@ -7,107 +7,106 @@ #ifndef BOOST_HISTOGRAM_AXIS_BASE_HPP #define BOOST_HISTOGRAM_AXIS_BASE_HPP -#include -#include +#include #include -#include +#include +#include #include - -// forward declaration for serialization -namespace boost { -namespace serialization { -class access; -} // namespace serialization -} // namespace boost +#include +#include namespace boost { namespace histogram { namespace axis { -enum class uoflow_type { off = 0, oflow = 1, on = 2 }; - /// Base class for all axes -class base { +template +class base +{ + using metadata_type = MetaData; + struct data : metadata_type // empty base class optimization + { + int size = 0; + option_type opt = option_type::none; + + data() = default; + data(const data&) = default; + data& operator=(const data&) = default; + data(data&& rhs) + : metadata_type(std::move(rhs)) + , size(rhs.size), opt(rhs.opt) + { rhs.size = 0; rhs.opt = option_type::none; } + data& operator=(data&& rhs) { + if (this != &rhs) { + metadata_type::operator=(std::move(rhs)); + size = rhs.size; + opt = rhs.opt; + rhs.size = 0; + rhs.opt = option_type::none; + } + return *this; + } + data(const metadata_type& m, int s, option_type o) + : metadata_type(m), size(s), opt(o) {} + + bool operator==(const data& rhs) const noexcept { + return size == rhs.size && opt == rhs.opt && + equal_impl(detail::is_equal_comparable(), rhs); + } + + bool equal_impl(std::true_type, const metadata_type& rhs) const noexcept { + return static_cast(*this) == rhs; + } + + bool equal_impl(std::false_type, const metadata_type&) const noexcept { + return true; + } + }; + public: - /// Returns the number of bins without overflow/underflow. - int size() const noexcept { return size_; } - /// Returns the number of bins, including overflow/underflow if enabled. - int shape() const noexcept { return shape_; } - /// Returns number of extra bins to count under- or overflow. - int uoflow() const noexcept { return shape_ - size_; } + /// Returns the number of bins, without extra bins. + unsigned size() const noexcept { return data_.size; } + /// Returns the options. + option_type options() const noexcept { return data_.opt; } + /// Returns the metadata. + metadata_type& metadata() noexcept { return static_cast(data_); } + /// Returns the metadata (const version). + const metadata_type& metadata() const noexcept { return static_cast(data_); } + + template + void serialize(Archive&, unsigned); + + friend void swap(base& a, base& b) noexcept // ADL works with friend functions + { + std::swap(static_cast(a), static_cast(b)); + std::swap(a.data_.size, b.data_.size); + std::swap(a.data_.opt, b.data_.opt); + } protected: - base(unsigned size, axis::uoflow_type uo) - : size_(size), shape_(size + static_cast(uo)) { - if (size_ == 0) { throw std::invalid_argument("bins > 0 required"); } + base(unsigned size, const metadata_type& m, option_type opt) + : data_(m, size, opt) + { + if (size == 0) { throw std::invalid_argument("bins > 0 required"); } + const auto max_index = std::numeric_limits::max() + - static_cast(data_.opt); + if (size > max_index) + throw std::invalid_argument( + detail::cat("bins <= ", max_index, " required") + ); } base() = default; - base(const base&) = default; - base& operator=(const base&) = default; - base(base&& rhs) : size_(rhs.size_), shape_(rhs.shape_) { - rhs.size_ = 0; - rhs.shape_ = 0; - } - base& operator=(base&& rhs) { - if (this != &rhs) { - size_ = rhs.size_; - shape_ = rhs.shape_; - rhs.size_ = 0; - rhs.shape_ = 0; - } - return *this; - } bool operator==(const base& rhs) const noexcept { - return size_ == rhs.size_ && shape_ == rhs.shape_; + return data_ == rhs.data_; } private: - int size_ = 0, shape_ = 0; - - friend class ::boost::serialization::access; - template - void serialize(Archive&, unsigned); + data data_; }; -/// Base class with a label -template -class labeled_base : public base { -public: - using allocator_type = Allocator; - - allocator_type get_allocator() const { return label_.get_allocator(); } - - /// Returns the axis label, which is a name or description. - boost::string_view label() const noexcept { return label_; } - /// Change the label of an axis. - void label(boost::string_view label) { label_.assign(label.begin(), label.end()); } - - bool operator==(const labeled_base& rhs) const noexcept { - return base::operator==(rhs) && label_ == rhs.label_; - } - -protected: - labeled_base() = default; - labeled_base(const labeled_base&) = default; - labeled_base& operator=(const labeled_base&) = default; - labeled_base(labeled_base&& rhs) = default; - labeled_base& operator=(labeled_base&& rhs) = default; - - labeled_base(unsigned size, axis::uoflow_type uo, string_view label, - const allocator_type& a) - : base(size, uo), label_(label.begin(), label.end(), a) {} - -private: - boost::container::basic_string, allocator_type> label_; - - friend class ::boost::serialization::access; - template - void serialize(Archive&, unsigned); -}; - -/// Iterator mixin, uses CRTP to inject iterator logic into Derived. +/// Uses CRTP to inject iterator logic into Derived. template class iterator_mixin { public: diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index d5f6ba37..a4128ab2 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace boost { @@ -19,12 +20,12 @@ namespace histogram { namespace axis { namespace detail { -inline string_view to_string(const transform::identity&) { return {}; } -inline string_view to_string(const transform::log&) { return {"_log", 4}; } -inline string_view to_string(const transform::sqrt&) { return {"_sqrt", 5}; } +inline ::boost::string_view to_string(const transform::identity&) { return {}; } +inline ::boost::string_view to_string(const transform::log&) { return {"_log", 4}; } +inline ::boost::string_view to_string(const transform::sqrt&) { return {"_sqrt", 5}; } template -void escape_string(OStream& os, const string_view s) { +void escape_string(OStream& os, const ::boost::string_view s) { os << '\''; for (auto sit = s.begin(); sit != s.end(); ++sit) { if (*sit == '\'' && (sit == s.begin() || *(sit - 1) != '\\')) { @@ -51,30 +52,30 @@ std::basic_ostream& operator<<(std::basic_ostream& return os; } -template +template std::basic_ostream& operator<<(std::basic_ostream& os, - const regular& a) { + const regular& a) { os << "regular" << detail::to_string(a.transform()) << "(" << a.size() << ", " - << a[0].lower() << ", " << a[a.size()].lower(); - if (!a.label().empty()) { - os << ", label="; - detail::escape_string(os, a.label()); - } - if (!a.uoflow()) { os << ", uoflow=False"; } + << a.lower(0) << ", " << a.lower(a.size()); + // if (!a.label().empty()) { + // os << ", label="; + // detail::escape_string(os, a.label()); + // } + // if (!a.uoflow()) { os << ", uoflow=False"; } os << ")"; return os; } -template +template std::basic_ostream& operator<<( - std::basic_ostream& os, const regular& a) { - os << "regular_pow(" << a.size() << ", " << a[0].lower() << ", " << a[a.size()].lower() + std::basic_ostream& os, const regular, M>& a) { + os << "regular_pow(" << a.size() << ", " << a.lower(0) << ", " << a.lower(a.size()) << ", " << a.transform().power; - if (!a.label().empty()) { - os << ", label="; - detail::escape_string(os, a.label()); - } - if (!a.uoflow()) { os << ", uoflow=False"; } + // if (!a.label().empty()) { + // os << ", label="; + // detail::escape_string(os, a.label()); + // } + // if (!a.uoflow()) { os << ", uoflow=False"; } os << ")"; return os; } @@ -83,14 +84,16 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, const circular& a) { os << "circular(" << a.size(); - if (a.phase() != 0.0) { os << ", phase=" << a.phase(); } - if (a.perimeter() != circular::two_pi()) { - os << ", perimeter=" << a.perimeter(); - } - if (!a.label().empty()) { - os << ", label="; - detail::escape_string(os, a.label()); + const auto phase = a.lower(0); + const auto perimeter = a.lower(a.size()) - a.lower(0); + if (phase != 0.0) { os << ", phase=" << phase; } + if (perimeter != circular::two_pi()) { + os << ", perimeter=" << perimeter; } + // if (!a.label().empty()) { + // os << ", label="; + // detail::escape_string(os, a.label()); + // } os << ")"; return os; } @@ -98,13 +101,13 @@ std::basic_ostream& operator<<(std::basic_ostream& template std::basic_ostream& operator<<(std::basic_ostream& os, const variable& a) { - os << "variable(" << a[0].lower(); - for (int i = 1; i <= a.size(); ++i) { os << ", " << a[i].lower(); } - if (!a.label().empty()) { - os << ", label="; - detail::escape_string(os, a.label()); - } - if (!a.uoflow()) { os << ", uoflow=False"; } + os << "variable(" << a.lower(0); + for (int i = 1; i <= a.size(); ++i) { os << ", " << a.lower(i); } + // if (!a.label().empty()) { + // os << ", label="; + // detail::escape_string(os, a.label()); + // } + // if (!a.uoflow()) { os << ", uoflow=False"; } os << ")"; return os; } @@ -112,12 +115,12 @@ std::basic_ostream& operator<<(std::basic_ostream& template std::basic_ostream& operator<<(std::basic_ostream& os, const integer& a) { - os << "integer(" << a[0].lower() << ", " << a[a.size()].lower(); - if (!a.label().empty()) { - os << ", label="; - detail::escape_string(os, a.label()); - } - if (!a.uoflow()) { os << ", uoflow=False"; } + os << "integer(" << a.lower(0) << ", " << a.lower(a.size()); + // if (!a.label().empty()) { + // os << ", label="; + // detail::escape_string(os, a.label()); + // } + // if (!a.uoflow()) { os << ", uoflow=False"; } os << ")"; return os; } @@ -127,10 +130,10 @@ std::basic_ostream& operator<<(std::basic_ostream& const category& a) { os << "category("; for (int i = 0; i < a.size(); ++i) { os << a[i] << (i == (a.size() - 1) ? "" : ", "); } - if (!a.label().empty()) { - os << ", label="; - detail::escape_string(os, a.label()); - } + // if (!a.label().empty()) { + // os << ", label="; + // detail::escape_string(os, a.label()); + // } os << ")"; return os; } @@ -143,10 +146,10 @@ inline std::basic_ostream& operator<<( detail::escape_string(os, a.value(i)); os << (i == (a.size() - 1) ? "" : ", "); } - if (!a.label().empty()) { - os << ", label="; - detail::escape_string(os, a.label()); - } + // if (!a.label().empty()) { + // os << ", label="; + // detail::escape_string(os, a.label()); + // } os << ")"; return os; } diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp new file mode 100644 index 00000000..f8410fb7 --- /dev/null +++ b/include/boost/histogram/axis/traits.hpp @@ -0,0 +1,49 @@ +// 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_AXIS_TRAITS_HPP +#define BOOST_HISTOGRAM_AXIS_TRAITS_HPP + +#include +#include +#include +#include + +namespace boost { +namespace histogram { + +namespace detail { +template +axis::option_type axis_traits_options_impl(std::true_type, const T& t) { + return t.options(); +} + +template +axis::option_type axis_traits_options_impl(std::false_type, const T&) { + return axis::option_type::none; +} +} // namespace detail + +namespace axis { + +template +struct traits { + using call_args = boost::callable_traits::args_t; + + static option_type options(const T& t) { + return detail::axis_traits_options_impl(detail::has_method_options(), t); + } + + static unsigned extend(const T& t) { + return t.size() + static_cast(options(t)); + } +}; + +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index d0b1ec39..d6be8f5f 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -14,85 +14,64 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -// forward declaration for serialization -namespace boost { -namespace serialization { -class access; -} // namespace serialization -} // namespace boost - namespace boost { namespace histogram { + namespace axis { namespace transform { -namespace detail { -struct stateless { - bool operator==(const stateless&) const noexcept { return true; } + +template +struct identity { + T forward(T x) { return x; } + T inverse(T x) { return x; } + + constexpr bool operator==(const identity&) const noexcept { return true; } template void serialize(Archive&, unsigned) {} }; -} // namespace detail -struct identity : public detail::stateless { - template - static T&& forward(T&& v) { - return std::forward(v); - } - template - static T&& inverse(T&& v) { - return std::forward(v); - } +template +struct log : public identity { + T forward(T x) { return std::log(x); } + T inverse(T x) { return std::exp(x); } }; -struct log : public detail::stateless { - template - static T forward(T v) { - return std::log(v); - } - template - static T inverse(T v) { - return std::exp(v); - } +template +struct sqrt : public identity { + T forward(T x) { return std::sqrt(x); } + T inverse(T x) { return x * x; } }; -struct sqrt : public detail::stateless { - template - static T forward(T v) { - return std::sqrt(v); - } - template - static T inverse(T v) { - return v * v; - } +template +struct unit { + T unit; + using U = decltype(std::declval() / std::declval()); + + U forward(T x) { return x / unit; } + T inverse(U x) { return x * unit; } }; -// struct cos : public detail::stateless { -// template static T forward(T v) { return std::cos(v); } -// template static T inverse(T v) { return std::acos(v); } -// }; - +template struct pow { - double power = 1.0; + T power = 1.0; pow() = default; - pow(double p) : power(p) {} - template - T forward(T v) const { - return std::pow(v, power); - } - template - T inverse(T v) const { - return std::pow(v, 1.0 / power); - } - bool operator==(const pow& other) const noexcept { return power == other.power; } + pow(T p) : power(p) {} + T forward(T v) const { return std::pow(v, power); } + T inverse(T v) const { return std::pow(v, 1.0 / power); } + + bool operator==(const pow& other) const noexcept { return power == other.power; } template void serialize(Archive&, unsigned); }; @@ -103,93 +82,107 @@ struct pow { * The most common binning strategy. * Very fast. Binning is a O(1) operation. */ -// private inheritance from Transform wastes no space if it is stateless -template -class regular : public labeled_base, - public iterator_mixin>, - Transform { - using base_type = labeled_base; - -public: - using allocator_type = typename base_type::allocator_type; +template +class regular : public base, + public iterator_mixin> +{ + using base_type = base; using transform_type = Transform; - using value_type = RealType; + using value_type = boost::mp11::mp_first< + boost::callable_traits::args_t< + decltype(&transform_type::forward) + > + >; + using internal_type = boost::callable_traits::return_type< + decltype(&transform_type::forward) + >; + static_assert(std::is_floating_point::value, + "type returned by forward transform must be floating point"); + using metadata_type = MetaData; + struct data : transform_type // empty base class optimization + { + internal_type min = 0, delta = 1; + + data(const transform_type& t, unsigned n, value_type b, value_type e) + : transform_type(t) + , min(forward(b)) + , delta((forward(e) - forward(b))/n) + {} + + data() = default; + + bool operator==(const data& rhs) const noexcept { + return transform_type::operator==(rhs) && min == rhs.min && + delta == rhs.delta; + } + }; using bin_type = interval_view; - static_assert(std::is_floating_point::value, - "type returned by forward transform must be floating point"); - - /** Construct axis with n bins over real range [lower, upper). +public: + /** Construct axis with n bins over real range [begin, end). * - * \param n number of bins. - * \param lower low edge of first bin. - * \param upper high edge of last bin. - * \param label description of the axis. - * \param uoflow whether to add under-/overflow bins. - * \param trans arguments passed to the transform. + * \param n number of bins. + * \param begin low edge of first bin. + * \param end high edge of last bin. + * \param metadata description of the axis. + * \param options extra bin options. + * \param trans transform instance to use. */ - regular(unsigned n, value_type lower, value_type upper, string_view label = {}, - uoflow_type uo = uoflow_type::on, transform_type trans = transform_type(), - const allocator_type& a = allocator_type()) - : base_type(n, uo, label, a) - , transform_type(std::move(trans)) - , min_(transform_type::forward(lower)) - , delta_((transform_type::forward(upper) - transform_type::forward(lower)) / n) { - if (lower < upper) { - BOOST_ASSERT(!std::isnan(min_)); - BOOST_ASSERT(!std::isnan(delta_)); - } else { - throw std::invalid_argument("lower < upper required"); - } + regular(unsigned n, value_type begin, value_type end, + const metadata_type& m = metadata_type(), + const option_type o = option_type::underflow_and_overflow, + const transform_type& trans = transform_type()) + : base_type(n, m, o) + , data_(trans, n, begin, end) + { + if (!std::isnormal(data_.min) || !std::isnormal(data_.delta)) + throw std::invalid_argument("forward transform of lower or upper invalid"); } regular() = default; - regular(const regular&) = default; - regular& operator=(const regular&) = default; - regular(regular&&) = default; - regular& operator=(regular&&) = default; /// Returns the bin index for the passed argument. - int index(value_type x) const noexcept { - // Optimized code, measure impact of changes - const value_type z = (transform_type::forward(x) - min_) / delta_; - return z < base_type::size() ? (z >= 0.0 ? static_cast(z) : -1) - : base_type::size(); + int operator()(value_type x) const noexcept { + // Runs in hot loop, please measure impact of changes + const auto z = (data_.forward(x) - data_.min) / data_.delta; + if (z < base_type::size()) { + if (z >= 0) + return static_cast(z); + else + return -1; + } + return base_type::size(); // also returned if z is NaN + + // const auto lt_max = z < base_type::size(); + // const auto ge_zero = z >= 0; + // return lt_max * (ge_zero * static_cast(z) - !ge_zero) + !lt_max * base_type::size(); } /// Returns lower edge of bin. - value_type lower(int i) const noexcept { - const auto n = base_type::size(); + value_type lower(int idx) const noexcept { + const auto z = internal_type(idx) / base_type::size(); value_type x; - if (i < 0) + if (z < 0) x = -std::numeric_limits::infinity(); - else if (i > n) + else if (z > 1) x = std::numeric_limits::infinity(); else { - const auto z = value_type(i) / n; - x = (1.0 - z) * min_ + z * (min_ + delta_ * n); + x = (1 - z) * data_.min + z * (data_.min + data_.delta * base_type::size()); } - return transform_type::inverse(x); + return data_.inverse(x); } bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } bool operator==(const regular& o) const noexcept { - return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ && - delta_ == o.delta_; + return base_type::operator==(o) && data_ == o.data_; } - /// Access properties of the transform. - const transform_type& transform() const noexcept { - return static_cast(*this); - } - -private: - value_type min_ = 0, delta_ = 1; - - friend class ::boost::serialization::access; template void serialize(Archive&, unsigned); + +private: + data data_; }; /** Axis for real values on a circle. @@ -198,66 +191,66 @@ private: * perimeter value. Therefore, there are no overflow/underflow * bins for this axis. Binning is a O(1) operation. */ -template -class circular : public labeled_base, - public iterator_mixin> { - using base_type = labeled_base; - -public: - using allocator_type = typename base_type::allocator_type; +template +class circular : public base, + public iterator_mixin> { + using base_type = base; using value_type = RealType; + using metadata_type = MetaData; using bin_type = interval_view; - +public: // two_pi can be found in boost/math, but it is defined here to reduce deps - static value_type two_pi() { return 6.283185307179586; } + static constexpr value_type two_pi() { return 6.283185307179586; } /** Constructor for n bins with an optional offset. * * \param n number of bins. * \param phase starting phase. * \param perimeter range after which value wraps around. - * \param label description of the axis. + * \param metadata description of the axis. + * \param options extra bin options. */ - explicit circular(unsigned n, value_type phase = 0.0, value_type perimeter = two_pi(), - string_view label = {}, const allocator_type& a = allocator_type()) - : base_type(n, uoflow_type::off, label, a), phase_(phase), perimeter_(perimeter) { - if (perimeter <= 0) throw std::invalid_argument("perimeter must be positive"); + explicit circular(unsigned n, value_type phase = 0.0, + value_type perimeter = two_pi(), + const metadata_type& m = metadata_type(), + const option_type o = option_type::overflow) + : base_type(n, m, o == option_type::underflow_and_overflow ? + option_type::overflow : o) + , phase_(phase), delta_(perimeter / n) + { + if (!std::isnormal(phase) || !(perimeter > 0)) + throw std::invalid_argument("invalid phase or perimeter"); } circular() = default; - circular(const circular&) = default; - circular& operator=(const circular&) = default; - circular(circular&&) = default; - circular& operator=(circular&&) = default; /// Returns the bin index for the passed argument. - int index(value_type x) const noexcept { - const value_type z = (x - phase_) / perimeter_; - const int i = static_cast(std::floor(z * base_type::size())) % base_type::size(); - return i + (i < 0) * base_type::size(); + int operator()(value_type x) const noexcept { + const auto z = std::floor((x - phase_) / delta_); + if (std::isnormal(z)) { + const auto i = static_cast(z) % base_type::size(); + return i + (i < 0) * base_type::size(); + } + return base_type::size(); } /// Returns lower edge of bin. value_type lower(int i) const noexcept { - const value_type z = value_type(i) / base_type::size(); - return z * perimeter_ + phase_; + return phase_ + i * delta_; } bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } bool operator==(const circular& o) const noexcept { - return base_type::operator==(o) && phase_ == o.phase_ && perimeter_ == o.perimeter_; + return base_type::operator==(o) && + phase_ == o.phase_ && delta_ == o.delta_; } - value_type perimeter() const { return perimeter_; } - value_type phase() const { return phase_; } - -private: - value_type phase_ = 0.0, perimeter_ = 1.0; - - friend class ::boost::serialization::access; template void serialize(Archive&, unsigned); + +private: + value_type phase_ = 0.0, delta_ = 1.0; }; /** Axis for non-equidistant bins on the real line. @@ -265,129 +258,145 @@ private: * Binning is a O(log(N)) operation. If speed matters and the problem * domain allows it, prefer a regular axis, possibly with a transform. */ -template -class variable : public labeled_base, - public iterator_mixin> { - using base_type = labeled_base; - -public: - using allocator_type = typename base_type::allocator_type; +template +class variable : public base, + public iterator_mixin> { + using base_type = base; using value_type = RealType; + using allocator_type = Allocator; + using metadata_type = MetaData; using bin_type = interval_view; -private: - using value_allocator_type = - typename std::allocator_traits::template rebind_alloc; - using value_pointer_type = - typename std::allocator_traits::pointer; + struct data : allocator_type // empty base class optimization + { + typename std::allocator_traits::pointer x = nullptr; + using allocator_type::allocator_type; + }; public: - /** Construct an axis from bin edges. + /** Construct an axis from iterator range of bin edges. * - * \param x sequence of bin edges. - * \param label description of the axis. - * \param uoflow whether to add under-/overflow bins. + * \param begin begin of edge sequence. + * \param end end of edge sequence. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. */ - variable(std::initializer_list x, string_view label = {}, - uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type()) - : variable(x.begin(), x.end(), label, uo, a) {} - template > - variable(Iterator begin, Iterator end, string_view label = {}, - uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type()) - : base_type(begin == end ? 0 : std::distance(begin, end) - 1, uo, label, a) { - value_allocator_type a2(a); - using AT = std::allocator_traits; - x_ = AT::allocate(a2, nx()); - auto xit = x_; + typename = detail::requires_iterator> + variable(Iterator begin, Iterator end, + const metadata_type& m = metadata_type(), + const option_type o = option_type::underflow_and_overflow, + const allocator_type& a = allocator_type()) + : base_type(begin == end ? 0 : std::distance(begin, end) - 1, m, o) + , data_(a) + { + using AT = std::allocator_traits; + data_.x = AT::allocate(data_, nx()); try { - AT::construct(a2, xit, *begin++); - while (begin != end) { - if (*begin <= *xit) { - ++xit; // to make sure catch code works - throw std::invalid_argument("input sequence must be strictly ascending"); + auto xit = data_.x; + try { + AT::construct(data_, xit, *begin++); + while (begin != end) { + if (*begin <= *xit) { + ++xit; // to make sure catch code works + throw std::invalid_argument("input sequence must be strictly ascending"); + } + ++xit; + AT::construct(data_, xit, *begin++); } - ++xit; - AT::construct(a2, xit, *begin++); + } catch (...) { + // release resources that were already acquired before rethrowing + while (xit != data_.x) AT::destroy(data_, --xit); + throw; } } catch (...) { // release resources that were already acquired before rethrowing - while (xit != x_) AT::destroy(a2, --xit); - AT::deallocate(a2, x_, nx()); + AT::deallocate(data_, data_.x, nx()); throw; } } + /** Construct an axis from iterable range of bin edges. + * + * \param iterable iterable range of bin edges. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template > + variable(T iterable, + const metadata_type& m = metadata_type(), + const option_type o = option_type::underflow_and_overflow, + const allocator_type& a = allocator_type()) + : variable(std::begin(iterable), std::end(iterable), m, o, a) {} + variable() = default; - variable(const variable& o) : base_type(o) { - value_allocator_type a(o.get_allocator()); - x_ = boost::histogram::detail::create_buffer_from_iter(a, nx(), o.x_); + variable(const variable& o) : base_type(o), data_(o.data_) { + data_.x = detail::create_buffer_from_iter(data_, nx(), o.data_.x); } variable& operator=(const variable& o) { if (this != &o) { if (base_type::size() != o.size()) { - this->~variable(); - base::operator=(o); - value_allocator_type a(base_type::get_allocator()); - x_ = boost::histogram::detail::create_buffer_from_iter(a, nx(), o.x_); + detail::destroy_buffer(data_, data_.x, nx()); + data_ = o.data_; + base_type::operator=(o); + data_.x = detail::create_buffer_from_iter(data_, nx(), o.data_.x); } else { - base::operator=(o); - std::copy(o.x_, o.x_ + o.nx(), x_); + base_type::operator=(o); + std::copy(o.data_.x, o.data_.x + nx(), data_.x); } } return *this; } - variable(variable&& o) : base_type(std::move(o)) { - x_ = o.x_; - o.x_ = nullptr; + variable(variable&& o) + : base_type(std::move(o)) + , data_(std::move(o.data_)) + { + o.data_.x = nullptr; } variable& operator=(variable&& o) { - this->~variable(); - base::operator=(std::move(o)); - x_ = o.x_; - o.x_ = nullptr; + if (this != &o) { + std::swap(static_cast(*this), o); + std::swap(static_cast(data_), o.data_); + std::swap(data_.x, o.data_.x); + } return *this; } ~variable() { - if (x_) { // nothing to do for empty state - value_allocator_type a(base_type::get_allocator()); - boost::histogram::detail::destroy_buffer(a, x_, nx()); - } + detail::destroy_buffer(data_, data_.x, nx()); } /// Returns the bin index for the passed argument. - int index(value_type x) const noexcept { - return std::upper_bound(x_, x_ + nx(), x) - x_ - 1; + int operator()(value_type x) const noexcept { + return std::upper_bound(data_.x, data_.x + nx(), x) - data_.x - 1; } /// Returns the starting edge of the bin. value_type lower(int i) const noexcept { if (i < 0) { return -std::numeric_limits::infinity(); } if (i > base_type::size()) { return std::numeric_limits::infinity(); } - return x_[i]; + return data_.x[i]; } bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } bool operator==(const variable& o) const noexcept { - if (!base::operator==(o)) { return false; } - return std::equal(x_, x_ + nx(), o.x_); + return base_type::operator==(o) && + std::equal(data_.x, data_.x + nx(), o.data_.x); } + template + void serialize(Archive&, unsigned); + private: int nx() const { return base_type::size() + 1; } - - value_pointer_type x_ = nullptr; - - friend class ::boost::serialization::access; - template - void serialize(Archive&, unsigned); + data data_; }; /** Axis for an interval of integral values with unit steps. @@ -395,27 +404,27 @@ private: * Binning is a O(1) operation. This axis operates * faster than a regular. */ -template -class integer : public labeled_base, - public iterator_mixin> { - using base_type = labeled_base; - -public: - using allocator_type = typename base_type::allocator_type; +template +class integer : public base, + public iterator_mixin> { + using base_type = base; using value_type = IntType; + using metadata_type = MetaData; using bin_type = interval_view; +public: - /** Construct axis over a semi-open integer interval [lower, upper). + /** Construct axis over a semi-open integer interval [begin, end). * - * \param lower smallest integer of the covered range. - * \param upper largest integer of the covered range. - * \param label description of the axis. - * \param uoflow whether to add under-/overflow bins. + * \param begin first integer of covered range. + * \param end one past last integer of covered range. + * \param metadata description of the axis. + * \param options extra bin options. */ - integer(value_type lower, value_type upper, string_view label = {}, - uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type()) - : base_type(upper - lower, uo, label, a), min_(lower) { - if (!(lower < upper)) { throw std::invalid_argument("lower < upper required"); } + integer(value_type begin, value_type end, + const metadata_type& m = metadata_type(), + const option_type o = option_type::underflow_and_overflow) + : base_type(end - begin, m, o), min_(begin) { + if (begin >= end) { throw std::invalid_argument("begin < end required"); } } integer() = default; @@ -425,14 +434,14 @@ public: integer& operator=(integer&&) = default; /// Returns the bin index for the passed argument. - int index(value_type x) const noexcept { + int operator()(value_type x) const noexcept { const int z = x - min_; return z >= 0 ? (z > base_type::size() ? base_type::size() : z) : -1; } /// Returns lower edge of the integral bin. value_type lower(int i) const noexcept { - if (i < 0) { return -std::numeric_limits::max(); } + if (i < 0) { return std::numeric_limits::min(); } if (i > base_type::size()) { return std::numeric_limits::max(); } return min_ + i; } @@ -443,12 +452,11 @@ public: return base_type::operator==(o) && min_ == o.min_; } -private: - value_type min_ = 0; - - friend class ::boost::serialization::access; template void serialize(Archive&, unsigned); + +private: + value_type min_ = 0; }; /** Axis which maps unique values to bins (one on one). @@ -459,117 +467,121 @@ private: * Binning is a O(n) operation for n values in the worst case and O(1) in * the best case. The value types must be equal-comparable. */ -template -class category : public labeled_base, - public iterator_mixin> { - using base_type = labeled_base; - -public: - using allocator_type = typename base_type::allocator_type; - using value_type = T; +template +class category : public base, + public iterator_mixin> { + using base_type = base; + using metadata_type = MetaData; + using value_type = ValueType; + using allocator_type = Allocator; using bin_type = value_view; -private: - using value_allocator_type = - typename std::allocator_traits::template rebind_alloc; - using value_pointer_type = - typename std::allocator_traits::pointer; + struct data : allocator_type { + typename std::allocator_traits::pointer x = nullptr; + using allocator_type::allocator_type; + }; public: + /** Construct an axis from iterator range of categories. + * + * \param begin begin of category range of unique values. + * \param end end of category range of unique values. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template > + category(Iterator begin, Iterator end, + const metadata_type& m = metadata_type(), + const option_type o = option_type::overflow, + const allocator_type& a = allocator_type()) + : base_type(std::distance(begin, end), m, o) + , data_(a) + { + data_.x = detail::create_buffer_from_iter(data_, base_type::size(), begin); + } + /** Construct from an initializer list of strings. * * \param seq sequence of unique values. - * \param label description of the axis. - * \param uoflow whether to add under-/overflow bins. + * \param metadata description of the axis. */ - category(std::initializer_list seq, string_view label = {}, - uoflow_type uo = uoflow_type::oflow, + template > + category(T iterable, + const metadata_type& m = metadata_type(), + const option_type o = option_type::overflow, const allocator_type& a = allocator_type()) - : category(seq.begin(), seq.end(), label, uo, a) {} - - template > - category(Iterator begin, Iterator end, string_view label = {}, - uoflow_type uo = uoflow_type::oflow, - const allocator_type& a = allocator_type()) - : base_type(std::distance(begin, end), - uo == uoflow_type::on ? uoflow_type::oflow : uo, label, a) { - value_allocator_type a2(a); - x_ = boost::histogram::detail::create_buffer_from_iter(a2, nx(), begin); - } + : category(std::begin(iterable), std::end(iterable), m, o, a) {} category() = default; - category(const category& o) : base_type(o) { - value_allocator_type a(o.get_allocator()); - x_ = boost::histogram::detail::create_buffer_from_iter(a, o.nx(), o.x_); + category(const category& o) + : base_type(o) + , data_(o.data_) + { + data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); } category& operator=(const category& o) { if (this != &o) { if (base_type::size() != o.size()) { - this->~category(); + detail::destroy_buffer(data_, data_.x, base_type::size()); base_type::operator=(o); - value_allocator_type a(base_type::get_allocator()); - x_ = boost::histogram::detail::create_buffer_from_iter(a, nx(), o.x_); + data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); } else { base_type::operator=(o); - std::copy(o.x_, o.x_ + o.nx(), x_); + std::copy(o.data_.x, o.data_.x + base_type::size(), data_.x); } } return *this; } - category(category&& o) : base_type(std::move(o)) { - x_ = o.x_; - o.x_ = nullptr; + category(category&& o) + : base_type(std::move(o)) + , data_(std::move(o)) { + o.data_.x = nullptr; } category& operator=(category&& o) { - this->~category(); - base_type::operator=(std::move(o)); - x_ = o.x_; - o.x_ = nullptr; + if (this != &o) { + std::swap(static_cast(*this), o); + std::swap(static_cast(data_), o.data_); + std::swap(data_.x, o.data_.x); + } return *this; } ~category() { - if (x_) { // nothing to do for empty state - value_allocator_type a(base_type::get_allocator()); - boost::histogram::detail::destroy_buffer(a, x_, nx()); - } + detail::destroy_buffer(data_, data_.x, base_type::size()); } /// Returns the bin index for the passed argument. - int index(const value_type& x) const noexcept { - if (last_ < nx() && x_[last_] == x) return last_; - last_ = 0; - for (auto xit = x_, xe = x_ + nx(); xit != xe && !(*xit == x); ++xit) ++last_; - return last_; + int operator()(const value_type& x) const noexcept { + const auto begin = data_.x; + const auto end = begin + base_type::size(); + return std::distance(begin, std::find(begin, end, x)); } /// Returns the value for the bin index (performs a range check). const value_type& value(int idx) const { if (idx < 0 || idx >= base_type::size()) throw std::out_of_range("category index out of range"); - return x_[idx]; + return data_.x[idx]; } bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } bool operator==(const category& o) const noexcept { - return base_type::operator==(o) && std::equal(x_, x_ + nx(), o.x_); + return base_type::operator==(o) && + std::equal(data_.x, data_.x + base_type::size(), o.data_.x); } -private: - int nx() const { return base_type::size(); } - - value_pointer_type x_ = nullptr; - mutable int last_ = 0; - - friend class ::boost::serialization::access; template void serialize(Archive&, unsigned); + +private: + data data_; }; } // namespace axis } // namespace histogram diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp new file mode 100644 index 00000000..1963ff1c --- /dev/null +++ b/include/boost/histogram/axis/variant.hpp @@ -0,0 +1,232 @@ +// 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_VARIANT_HPP +#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { + +namespace detail { + +// struct call_visitor : public boost::static_visitor { +// const double x; +// explicit call_visitor(const double arg) : x(arg) {} +// template +// int operator()(const T& t) const { +// using args = typename axis::traits::call_args; +// return impl(mp11::mp_bool< +// (mp11::mp_size::value == 1 && +// std::is_convertible>::value) +// >(), t); +// } +// template +// int impl(std::true_type, const T& a) const { +// using U = mp11::mp_first::call_args>; +// return a(static_cast(x)); +// } +// template +// int impl(std::false_type, const T&) const { +// using args = typename axis::traits::call_args; +// throw std::runtime_error(boost::histogram::detail::cat( +// "cannot convert double to ", +// boost::core::demangled_name( BOOST_CORE_TYPEID(args) ), +// " for ", +// boost::core::demangled_name( BOOST_CORE_TYPEID(T) ) +// )); +// } +// }; + +struct size_visitor : public boost::static_visitor { + template + unsigned operator()(const T& a) const { + return a.size(); + } +}; + +struct options_visitor : public boost::static_visitor { + template + axis::option_type operator()(const T& t) const { + return axis::traits::options(t); + } +}; + +struct lower_visitor : public boost::static_visitor { + int idx; + lower_visitor(int i) : idx(i) {} + template + double operator()(const T& a) const { + return impl(detail::has_method_lower(), a); + } + template + double impl(std::true_type, const T& a) const { + return a.lower(idx); + } + template + double impl(std::false_type, const T&) const { + throw std::runtime_error(boost::histogram::detail::cat( + "cannot use ", boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " with generic boost::histogram::axis::variant interface, use" + " static_cast to access the underlying axis type")); + } +}; + +struct bicmp_visitor : public boost::static_visitor { + template + bool operator()(const T&, const U&) const { + return false; + } + + template + bool operator()(const T& a, const T& b) const { + return a == b; + } +}; + +template +struct assign_visitor : public boost::static_visitor { + T& t; + assign_visitor(T& tt) : t(tt) {} + template + void operator()(const U& u) const { + impl(mp11::mp_contains(), u); + } + + template + void impl(std::true_type, const U& u) const { + t = u; + } + + template + void impl(std::false_type, const U&) const { + throw std::invalid_argument(boost::histogram::detail::cat( + "argument ", boost::typeindex::type_id().pretty_name(), + " is not a bounded type of ", boost::typeindex::type_id().pretty_name())); + } +}; +} // namespace detail + +namespace axis { + +/// Polymorphic axis type +template +class variant + : public iterator_mixin> + , private boost::variant +{ + using base_type = boost::variant; + using bin_type = interval_view; + + template + using requires_bounded_type = + mp11::mp_if>, void>; + +public: + using types = mp11::mp_list; + + variant() = default; + + template > + variant(T&& t) : base_type(std::forward(t)) {} + + template > + variant& operator=(T&& t) { + base_type::operator=(std::forward(t)); + return *this; + } + + template + variant(const variant& u) { + boost::apply_visitor(detail::assign_visitor(*this), u); + } + + template + variant& operator=(const variant& u) { + boost::apply_visitor(detail::assign_visitor(*this), u); + return *this; + } + + unsigned size() const { + return boost::apply_visitor(detail::size_visitor(), *this); + } + + option_type options() const { + return boost::apply_visitor(detail::options_visitor(), *this); + } + + // // this only works for axes with compatible call signature + // // and will throw a runtime_error otherwise + // int operator()(double x) const { + // return boost::apply_visitor(detail::call_visitor(x), *this); + // } + + // this only works for axes with a lower method + // and will throw a runtime_error otherwise + double lower(int idx) const { + return boost::apply_visitor(detail::lower_visitor(idx), *this); + } + + // this only works for axes with compatible bin type + // and will throw a runtime_error otherwise + bin_type operator[](const int idx) const { return bin_type(idx, *this); } + + bool operator==(const variant& rhs) const { + return base_type::operator==(static_cast(rhs)); + } + + template + bool operator==(const variant& u) const { + return boost::apply_visitor(detail::bicmp_visitor(), *this, u); + } + + template > + bool operator==(const T& t) const { + // variant::operator==(T) is implemented only to fail, we cannot use it + auto tp = boost::get(this); + return tp && *tp == t; + } + + template + bool operator!=(const T& t) const { + return !operator==(t); + } + + template + explicit operator const T&() const { + return boost::strict_get(*this); + } + + template + explicit operator T&() { + return boost::strict_get(*this); + } + + template + void serialize(Archive&, unsigned); +}; + +// TODO add something like std::visit + +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index 0d94f7f1..a7d94774 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -10,7 +10,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -217,7 +218,7 @@ struct field_counter { std::size_t value = 1; template void operator()(const T& t) { - value *= t.shape(); + value *= axis::traits::extend(t); } }; } @@ -257,7 +258,7 @@ struct shape_collector { shape_collector(std::vector::iterator i) : iter(i) {} template void operator()(const T& a) { - *iter++ = a.shape(); + *iter++ = axis::traits::extend(a); } }; diff --git a/include/boost/histogram/detail/buffer.hpp b/include/boost/histogram/detail/buffer.hpp index a81f6c6e..6e49faf8 100644 --- a/include/boost/histogram/detail/buffer.hpp +++ b/include/boost/histogram/detail/buffer.hpp @@ -91,6 +91,8 @@ typename std::allocator_traits::pointer create_buffer_from_iter( template void destroy_buffer(Allocator& a, typename std::allocator_traits::pointer p, std::size_t n) { + if (!p) + return; using AT = std::allocator_traits; auto it = p + n; const auto end = p; diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index ef807717..6d39592b 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -7,6 +7,7 @@ #ifndef BOOST_HISTOGRAM_DETAIL_META_HPP #define BOOST_HISTOGRAM_DETAIL_META_HPP +#include #include #include #include @@ -38,13 +39,19 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support, BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, (std::declval().lower(0))); +BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, + (static_cast(std::declval().options()))); + +BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval().metadata())); + BOOST_HISTOGRAM_MAKE_SFINAE(is_dynamic_container, (std::begin(std::declval()))); BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, (std::get<0>(std::declval()))); BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, (static_cast(std::declval()))); -BOOST_HISTOGRAM_MAKE_SFINAE(is_string, (std::declval().c_str())); +BOOST_HISTOGRAM_MAKE_SFINAE(is_equal_comparable, + (std::declval() == std::declval())); struct static_container_tag {}; struct dynamic_container_tag {}; @@ -53,9 +60,7 @@ struct no_container_tag {}; template using classify_container = typename std::conditional< is_static_container::value, static_container_tag, - typename std::conditional<(is_dynamic_container::value && - !std::is_convertible::value && - !is_string::value), + typename std::conditional::value, dynamic_container_tag, no_container_tag>::type>::type; template (), ++std::declval())> struct requires_iterator {}; +template ()))> +struct requires_iterable {}; + template ()[0])> struct requires_vector {}; template (std::declval()))> struct requires_tuple {}; -template -using requires_axis = decltype(std::declval().size(), std::declval().shape(), - std::declval().uoflow(), std::declval().label(), - std::declval()[0]); +template ().size(), &T::operator())> +struct requires_axis {}; namespace { struct bool_mask_impl { @@ -118,6 +125,12 @@ using mp_set_union = mp11::mp_apply_q using mp_last = mp11::mp_at_c::value - 1)>; +template +using container_element_type = rm_cv_ref()))>; + +template +using iterator_value_type = rm_cv_ref())>; + } // namespace detail } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index 95a65821..61f8e1a4 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,13 +24,6 @@ #include #include -// forward declaration for serialization -namespace boost { -namespace serialization { -class access; -} -} - namespace boost { namespace histogram { @@ -104,7 +98,7 @@ public: } /// Number of axes (dimensions) of histogram - std::size_t dim() const noexcept { return detail::axes_size(axes_); } + std::size_t rank() const noexcept { return detail::axes_size(axes_); } /// Total number of bins in the histogram (including underflow/overflow) std::size_t size() const noexcept { return storage_.size(); } @@ -215,8 +209,8 @@ public: using HR = histogram; auto sub_axes = detail::make_sub_axes(axes_, N(), Ns()...); auto hr = HR(std::move(sub_axes), storage_type(storage_.get_allocator())); - const auto b = detail::bool_mask(dim(), true); - std::vector shape(dim()); + const auto b = detail::bool_mask(rank(), true); + std::vector shape(rank()); for_each_axis(detail::shape_collector(shape.begin())); detail::index_mapper m(shape, b); do { hr.storage_.add(m.second, storage_[m.first]); } while (m.next()); @@ -231,17 +225,17 @@ public: histogram reduce_to(Iterator begin, Iterator end) const { BOOST_ASSERT_MSG(std::is_sorted(begin, end, std::less_equal()), "integer sequence must be strictly ascending"); - BOOST_ASSERT_MSG(begin == end || static_cast(*(end - 1)) < dim(), + BOOST_ASSERT_MSG(begin == end || static_cast(*(end - 1)) < rank(), "index out of range"); auto sub_axes = histogram::axes_type(axes_.get_allocator()); sub_axes.reserve(std::distance(begin, end)); - auto b = std::vector(dim(), false); + auto b = std::vector(rank(), false); for (auto it = begin; it != end; ++it) { sub_axes.push_back(axes_[*it]); b[*it] = true; } auto hr = histogram(std::move(sub_axes), storage_type(storage_.get_allocator())); - std::vector shape(dim()); + std::vector shape(rank()); for_each_axis(detail::shape_collector(shape.begin())); detail::index_mapper m(shape, b); do { hr.storage_.add(m.second, storage_[m.first]); } while (m.next()); @@ -252,6 +246,9 @@ public: const_iterator end() const noexcept { return const_iterator(*this, size()); } + template + void serialize(Archive&, unsigned); + private: axes_type axes_; Storage storage_; @@ -260,76 +257,66 @@ private: friend class histogram; template friend class iterator_over; - friend class python_access; - friend class ::boost::serialization::access; - template - void serialize(Archive&, unsigned); }; /// static type factory with custom storage type -template -histogram...>, detail::rm_cv_ref> -make_static_histogram_with(Storage&& s, Ts&&... axis) { - using H = histogram...>, detail::rm_cv_ref>; - auto axes = typename H::axes_type(std::forward(axis)...); - return H(std::move(axes), std::forward(s)); +template > +histogram< + std::tuple, detail::rm_cv_ref...>, + detail::rm_cv_ref +> +make_histogram_with(S&& s, T&& axis0, Ts&&... axis) { + auto axes = std::make_tuple(std::forward(axis0), std::forward(axis)...); + return histogram>( + std::move(axes), std::forward(s) + ); } /// static type factory with standard storage type -template -histogram...>> make_static_histogram(Ts&&... axis) { - using S = typename histogram...>>::storage_type; - return make_static_histogram_with(S(), std::forward(axis)...); +template > +auto make_histogram(T&& axis0, Ts&&... axis) + -> decltype(make_histogram_with(default_storage(), + std::forward(axis0), + std::forward(axis)...)) +{ + return make_histogram_with(default_storage(), + std::forward(axis0), + std::forward(axis)...); } -namespace detail { -template -using srebind = typename std::allocator_traits< - typename rm_cv_ref::allocator_type>::template rebind_alloc; +/// dynamic type factory from vector-like with custom storage type +template > +histogram, detail::rm_cv_ref> +make_histogram_with(S&& s, T&& t) { + return histogram, detail::rm_cv_ref>( + std::forward(t), std::forward(s) + ); } -/// dynamic type factory with custom storage type -template -histogram>, detail::rm_cv_ref> -make_dynamic_histogram_with(Storage&& s, T&& axis0, Ts&&... axis) { - using H = histogram>, - detail::rm_cv_ref>; - auto axes = typename H::axes_type( - {Any(std::forward(axis0)), Any(std::forward(axis))...}, s.get_allocator()); - return H(std::move(axes), std::forward(s)); +/// dynamic type factory from vector-like with standard storage type +template > +auto make_histogram(T&& t) + -> decltype(make_histogram_with(default_storage(), std::forward(t))) +{ + return make_histogram_with(default_storage(), std::forward(t)); } -/// dynamic type factory with standard storage type -template -histogram> make_dynamic_histogram(T&& axis0, Ts&&... axis) { - using S = typename histogram>::storage_type; - return make_dynamic_histogram_with(S(), std::forward(axis0), - std::forward(axis)...); -} - -/// dynamic type factory with custom storage type +/// dynamic type factory from iterator range with custom storage type template > -histogram>, +histogram>, detail::rm_cv_ref> -make_dynamic_histogram_with(Storage&& s, Iterator begin, Iterator end) { - using H = - histogram>, - detail::rm_cv_ref>; - auto axes = typename H::axes_type(s.get_allocator()); - axes.reserve(std::distance(begin, end)); - while (begin != end) axes.emplace_back(*begin++); - return H(std::move(axes), std::forward(s)); +make_histogram_with(Storage&& s, Iterator begin, Iterator end) { + auto axes = std::vector>(begin, end); + return make_histogram_with(std::forward(s), std::move(axes)); } -/// dynamic type factory with standard storage type +/// dynamic type factory from iterator range with standard storage type template > -histogram> make_dynamic_histogram( - Iterator begin, Iterator end) { - using S = typename histogram>::storage_type; - return make_dynamic_histogram_with(S(), begin, end); +auto make_histogram(Iterator begin, Iterator end) + -> decltype(make_histogram_with(default_storage(), begin, end)) +{ + return make_histogram_with(default_storage(), begin, end); } } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 2a67158a..d22466cd 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -9,51 +9,63 @@ #include // for std::allocator #include -#include +#include namespace boost { namespace histogram { namespace axis { +enum class option_type { + none = 0, + overflow = 1, + underflow_and_overflow = 2, +}; + namespace transform { -struct identity; -struct log; -struct sqrt; -struct pow; +template struct identity; +template struct log; +template struct sqrt; +template struct unit; +template struct pow; } // namespace transform -template > +template , + typename MetaData = boost::container::string> class regular; -template > + +template class circular; -template > + +template , + typename MetaData = boost::container::string> class variable; -template > + +template class integer; -template > + +template , + typename MetaData = boost::container::string> class category; template -class any; -using any_std = - any>, - regular>, - regular>, - regular>, - circular>, variable>, - integer>, category>, - category>>; +class variant; } // namespace axis template > struct adaptive_storage; + template > struct array_storage; -template , class Storage = adaptive_storage<>> +using default_storage = adaptive_storage<>; + +template class histogram; } // namespace histogram diff --git a/include/boost/histogram/ostream_operators.hpp b/include/boost/histogram/ostream_operators.hpp index c54e9f7a..734728df 100644 --- a/include/boost/histogram/ostream_operators.hpp +++ b/include/boost/histogram/ostream_operators.hpp @@ -20,8 +20,8 @@ template struct axis_ostream_visitor { OStream& os_; explicit axis_ostream_visitor(OStream& os) : os_(os) {} - template - void operator()(const Axis& a) const { + template + void operator()(const T& a) const { os_ << "\n " << a << ","; } }; From 61aea9e6997a6079f2a53d65579a4e83ad2de5b7 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Mon, 15 Oct 2018 01:11:18 +0200 Subject: [PATCH 02/44] wip: fixing tests, removed weight_counter from adaptive_storage, storage scaling with double --- examples/guide_custom_axis.cpp | 4 +- examples/guide_custom_storage.cpp | 4 +- examples/guide_fill_histogram.cpp | 24 ++- examples/guide_histogram_operators.cpp | 43 ++-- examples/guide_make_dynamic_histogram.cpp | 7 +- examples/guide_make_static_histogram.cpp | 4 +- include/boost/histogram/axis/base.hpp | 4 +- include/boost/histogram/axis/types.hpp | 38 ++-- include/boost/histogram/detail/axes.hpp | 57 ++--- include/boost/histogram/detail/meta.hpp | 17 +- include/boost/histogram/histogram.hpp | 35 ++-- include/boost/histogram/histogram_fwd.hpp | 4 +- .../histogram/storage/adaptive_storage.hpp | 198 ++++++++++++------ .../boost/histogram/storage/array_storage.hpp | 5 +- .../histogram/storage/weight_counter.hpp | 2 +- include/boost/histogram/weight.hpp | 20 +- test/adaptive_storage_test.cpp | 118 +++++------ 17 files changed, 323 insertions(+), 261 deletions(-) diff --git a/examples/guide_custom_axis.cpp b/examples/guide_custom_axis.cpp index f037286e..67332fab 100644 --- a/examples/guide_custom_axis.cpp +++ b/examples/guide_custom_axis.cpp @@ -14,11 +14,11 @@ struct custom_axis : public bh::axis::integer<> { // the customization point // - accept const char* and convert to int // - then call index method of base class - int index(value_type s) const { return integer::index(std::atoi(s)); } + int operator()(value_type s) const { return integer::operator()(std::atoi(s)); } }; int main() { - auto h = bh::make_static_histogram(custom_axis(0, 3)); + auto h = bh::make_histogram(custom_axis(0, 3)); h("-10"); h("0"); h("1"); diff --git a/examples/guide_custom_storage.cpp b/examples/guide_custom_storage.cpp index 2114b19d..4649f75e 100644 --- a/examples/guide_custom_storage.cpp +++ b/examples/guide_custom_storage.cpp @@ -7,8 +7,8 @@ namespace bh = boost::histogram; int main() { // create static histogram with array_storage, using int as counter type - auto h = bh::make_static_histogram_with(bh::array_storage(), - bh::axis::regular<>(10, 0, 1)); + auto h = bh::make_histogram_with(bh::array_storage(), + bh::axis::regular<>(10, 0, 1)); // do something with h } diff --git a/examples/guide_fill_histogram.cpp b/examples/guide_fill_histogram.cpp index 625c0914..0d325d9d 100644 --- a/examples/guide_fill_histogram.cpp +++ b/examples/guide_fill_histogram.cpp @@ -3,12 +3,15 @@ #include #include #include +#include +#include +#include namespace bh = boost::histogram; int main() { - auto h = bh::make_static_histogram(bh::axis::regular<>(8, 0, 4), - bh::axis::regular<>(10, 0, 5)); + auto h = bh::make_histogram(bh::axis::regular<>(8, 0, 4), + bh::axis::regular<>(10, 0, 5)); // fill histogram, number of arguments must be equal to number of axes h(0, 1.1); // increases bin counter by one @@ -24,14 +27,17 @@ int main() { // functional-style processing is also supported std::vector> input_data{ {0, 1.2}, {2, 3.4}, {4, 5.6}}; - // std::for_each takes the functor by value, thus it potentially makes - // expensive copies of the histogram, but modern compilers are usually smart - // enough to avoid the superfluous copies - auto h2 = - std::for_each(input_data.begin(), input_data.end(), - bh::make_static_histogram(bh::axis::regular<>(8, 0, 4), - bh::axis::regular<>(10, 0, 5))); + + // std::for_each takes the functor by value, we use a reference wrapper + // to avoid costly copies + auto h2 = bh::make_histogram(bh::axis::regular<>(8, 0, 4), + bh::axis::regular<>(10, 0, 5)); + std::for_each(input_data.begin(), input_data.end(), + std::ref(h2)); + // h2 is filled + const double sum = std::accumulate(h2.begin(), h2.end(), 0.0); + assert(sum == 3); } //] diff --git a/examples/guide_histogram_operators.cpp b/examples/guide_histogram_operators.cpp index 1d0acb92..fb7ff54c 100644 --- a/examples/guide_histogram_operators.cpp +++ b/examples/guide_histogram_operators.cpp @@ -1,14 +1,14 @@ //[ guide_histogram_operators #include -#include +#include namespace bh = boost::histogram; int main() { // make two histograms - auto h1 = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1)); - auto h2 = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1)); + auto h1 = bh::make_histogram(bh::axis::regular<>(2, -1, 1)); + auto h2 = bh::make_histogram(bh::axis::regular<>(2, -1, 1)); h1(-0.5); // counts are: 1 0 h2(0.5); // counts are: 0 1 @@ -22,8 +22,7 @@ int main() { // accept and return rvalue references where possible auto h4 = h1 + h2 + h3; // counts are: 2 2 - std::cout << h4.at(0).value() << " " << h4.at(1).value() << std::endl; - // prints: 2 2 + assert(h4.at(0) == 2 && h4.at(1) == 2); // multiply by number h4 *= 2; // counts are: 4 4 @@ -31,27 +30,23 @@ int main() { // divide by number auto h5 = h4 / 4; // counts are: 1 1 - std::cout << h5.at(0).value() << " " << h5.at(1).value() << std::endl; - // prints: 1 1 + assert(h5.at(0) == 1 && h5.at(1) == 1); + assert(h4 != h5 && h4 == 4 * h5); - // compare histograms - std::cout << (h4 == 4 * h5) << " " << (h4 != h5) << std::endl; - // prints: 1 1 - - // note: special effect of multiplication on counter variance - auto h = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1)); + // note special effect of multiplication on weight_counter variance + auto h = bh::make_histogram_with(bh::array_storage>(), + bh::axis::regular<>(2, -1, 1)); h(-0.5); // counts are: 1 0 - std::cout << "value " << (2 * h).at(0).value() << " " - << (h + h).at(0).value() << "\n" - << "variance " << (2 * h).at(0).variance() << " " - << (h + h).at(0).variance() << std::endl; - // equality operator also checks variances, so the statement is false - std::cout << (h + h == 2 * h) << std::endl; - /* prints: - value 2 2 - variance 4 2 - 0 - */ + + auto h_sum = h + h; + auto h_mul = 2 * h; + + // equality operator checks variances, so following statement is false + assert(h_sum != h_mul); + + // variance is different when histograms are scaled + assert(h_sum.at(0).value() == 2 && h_mul.at(0).value() == 2); + assert(h_sum.at(0).variance() == 2 && h_mul.at(0).variance() == 4); } //] diff --git a/examples/guide_make_dynamic_histogram.cpp b/examples/guide_make_dynamic_histogram.cpp index dc007c86..508e006a 100644 --- a/examples/guide_make_dynamic_histogram.cpp +++ b/examples/guide_make_dynamic_histogram.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace bh = boost::histogram; @@ -15,13 +16,11 @@ int main() { // create dynamic histogram from iterator range auto h = bh::make_histogram(v.begin(), v.end()); - - // do something with h + assert(h.rank() == 2); // create dynamic histogram by moving the vector (this avoids copies) auto h2 = bh::make_histogram(std::move(v)); - - // do something with h2 + assert(h2.rank() == 2); } //] diff --git a/examples/guide_make_static_histogram.cpp b/examples/guide_make_static_histogram.cpp index bc32a22b..6a940f69 100644 --- a/examples/guide_make_static_histogram.cpp +++ b/examples/guide_make_static_histogram.cpp @@ -1,6 +1,7 @@ //[ guide_make_static_histogram #include +#include namespace bh = boost::histogram; @@ -10,8 +11,7 @@ int main() { which covers the real line from -1 to 1 in 100 bins */ auto h = bh::make_histogram(bh::axis::regular<>(100, -1, 1)); - - // do something with h + assert(h.rank() == 1); } //] diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index 699576c8..28bb3033 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -88,8 +88,8 @@ protected: : data_(m, size, opt) { if (size == 0) { throw std::invalid_argument("bins > 0 required"); } - const auto max_index = std::numeric_limits::max() - - static_cast(data_.opt); + const auto max_index = static_cast(std::numeric_limits::max() + - static_cast(data_.opt)); if (size > max_index) throw std::invalid_argument( detail::cat("bins <= ", max_index, " required") diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index d6be8f5f..5b39fb2e 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -15,8 +15,6 @@ #include #include #include -#include -#include #include #include #include @@ -32,8 +30,8 @@ namespace transform { template struct identity { - T forward(T x) { return x; } - T inverse(T x) { return x; } + static T forward(T x) { return x; } + static T inverse(T x) { return x; } constexpr bool operator==(const identity&) const noexcept { return true; } template @@ -42,23 +40,27 @@ struct identity { template struct log : public identity { - T forward(T x) { return std::log(x); } - T inverse(T x) { return std::exp(x); } + static T forward(T x) { return std::log(x); } + static T inverse(T x) { return std::exp(x); } }; template struct sqrt : public identity { - T forward(T x) { return std::sqrt(x); } - T inverse(T x) { return x * x; } + static T forward(T x) { return std::sqrt(x); } + static T inverse(T x) { return x * x; } }; template -struct unit { +struct quantity { T unit; using U = decltype(std::declval() / std::declval()); - U forward(T x) { return x / unit; } - T inverse(U x) { return x * unit; } + U forward(T x) const { return x / unit; } + T inverse(U x) const { return x * unit; } + + bool operator==(const quantity& o) const noexcept { return unit == o.unit; } + template + void serialize(Archive&, unsigned); }; template @@ -71,7 +73,7 @@ struct pow { T forward(T v) const { return std::pow(v, power); } T inverse(T v) const { return std::pow(v, 1.0 / power); } - bool operator==(const pow& other) const noexcept { return power == other.power; } + bool operator==(const pow& o) const noexcept { return power == o.power; } template void serialize(Archive&, unsigned); }; @@ -88,12 +90,8 @@ class regular : public base, { using base_type = base; using transform_type = Transform; - using value_type = boost::mp11::mp_first< - boost::callable_traits::args_t< - decltype(&transform_type::forward) - > - >; - using internal_type = boost::callable_traits::return_type< + using value_type = detail::arg_type<-1, decltype(&transform_type::forward)>; + using internal_type = detail::return_type< decltype(&transform_type::forward) >; static_assert(std::is_floating_point::value, @@ -105,8 +103,8 @@ class regular : public base, data(const transform_type& t, unsigned n, value_type b, value_type e) : transform_type(t) - , min(forward(b)) - , delta((forward(e) - forward(b))/n) + , min(this->forward(b)) + , delta((this->forward(e) - this->forward(b))/n) {} data() = default; diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index a7d94774..f03f611a 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -28,6 +28,11 @@ namespace detail { namespace { +template +unsigned extend(const T& t) { + return axis::traits::extend(t); +} + template struct axes_equal_static_dynamic_impl { bool& equal; @@ -160,9 +165,9 @@ struct axis_at_impl> { using type = mp11::mp_at_c, N>; }; -template -struct axis_at_impl> { - using type = Any; +template +struct axis_at_impl> { + using type = T; }; } @@ -179,13 +184,13 @@ auto axis_get(const std::tuple& axes) -> const axis_at(axes); } -template -Any& axis_get(std::vector& axes) { +template +T& axis_get(std::vector& axes) { return axes[N]; } -template -const Any& axis_get(const std::vector& axes) { +template +const T& axis_get(const std::vector& axes) { return axes[N]; } @@ -218,7 +223,7 @@ struct field_counter { std::size_t value = 1; template void operator()(const T& t) { - value *= axis::traits::extend(t); + value *= extend(t); } }; } @@ -257,8 +262,8 @@ struct shape_collector { std::vector::iterator iter; shape_collector(std::vector::iterator i) : iter(i) {} template - void operator()(const T& a) { - *iter++ = axis::traits::extend(a); + void operator()(const T& t) { + *iter++ = extend(t); } }; @@ -360,8 +365,8 @@ template void indices_to_index(optional_index& idx, const Axes& axes, const int j, const Us... us) { const auto& a = axis_get(axes); - const auto a_size = a.size(); - const auto a_shape = a.shape(); + const auto a_size = static_cast(a.size()); + const auto a_shape = static_cast(extend(a)); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); indices_to_index<(D + 1)>(idx, axes, us...); @@ -377,7 +382,7 @@ void indices_to_index_iter(mp11::mp_size_t, optional_index& idx, constexpr auto D = mp11::mp_size_t() - N; const auto& a = std::get(axes); const auto a_size = a.size(); - const auto a_shape = a.shape(); + const auto a_shape = extend(a); const auto j = static_cast(*iter); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -389,7 +394,7 @@ void indices_to_index_iter(optional_index& idx, const std::vector& axes, Iterator iter) { for (const auto& a : axes) { const auto a_size = a.size(); - const auto a_shape = a.shape(); + const auto a_shape = extend(a); const auto j = static_cast(*iter++); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -405,7 +410,7 @@ void indices_to_index_get(mp11::mp_size_t, optional_index& idx, const Axes& a constexpr std::size_t D = mp_size() - N; const auto& a = axis_get(axes); const auto a_size = a.size(); - const auto a_shape = a.shape(); + const auto a_shape = extend(a); const auto j = static_cast(std::get(t)); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -418,9 +423,10 @@ void args_to_index(optional_index&, const std::tuple&) noexcept {} template void args_to_index(optional_index& idx, const std::tuple& axes, const U& u, const Us&... us) { - const auto a_size = std::get(axes).size(); - const auto a_shape = std::get(axes).shape(); - const auto j = std::get(axes).index(u); + const auto& a = std::get(axes); + const auto a_size = a.size(); + const auto a_shape = extend(a); + const auto j = a(u); linearize(idx, a_size, a_shape, j); args_to_index<(D + 1)>(idx, axes, us...); } @@ -435,8 +441,8 @@ void args_to_index_iter(mp11::mp_size_t, optional_index& idx, constexpr std::size_t D = sizeof...(Ts)-N; const auto& a = axis_get(axes); const auto a_size = a.size(); - const auto a_shape = a.shape(); - const auto j = a.index(*iter); + const auto a_shape = extend(a); + const auto j = a(*iter); linearize(idx, a_size, a_shape, j); args_to_index_iter(mp11::mp_size_t<(N - 1)>(), idx, axes, ++iter); } @@ -449,9 +455,10 @@ template void args_to_index_get(mp11::mp_size_t, optional_index& idx, const std::tuple& axes, const T& t) { constexpr std::size_t D = mp_size::value - N; - const auto a_size = std::get(axes).size(); - const auto a_shape = std::get(axes).shape(); - const auto j = std::get(axes).index(std::get(t)); + const auto& a = std::get(axes); + const auto a_size = a.size(); + const auto a_shape = extend(a); + const auto j = a(std::get(t)); linearize(idx, a_size, a_shape, j); args_to_index_get(mp11::mp_size_t<(N - 1)>(), idx, axes, t); } @@ -470,8 +477,8 @@ struct args_to_index_visitor : public boost::static_visitor { template void impl(std::true_type, const Axis& a) const { const auto a_size = a.size(); - const auto a_shape = a.shape(); - const auto j = a.index(static_cast(val)); + const auto a_shape = extend(a); + const auto j = a(static_cast(val)); linearize(idx, a_size, a_shape, j); } diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 6d39592b..7aedd14e 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -108,8 +110,8 @@ using rm_cv_ref = typename std::remove_cv::typ template using mp_size = mp11::mp_size>; -template -using mp_at_c = mp11::mp_at_c, D>; +template +using mp_at_c = mp11::mp_at_c, N>; template using copy_qualifiers = mp11::mp_if< @@ -126,11 +128,20 @@ template using mp_last = mp11::mp_at_c::value - 1)>; template -using container_element_type = rm_cv_ref()))>; +using container_element_type = mp11::mp_first; template using iterator_value_type = rm_cv_ref())>; +template +using return_type = typename boost::callable_traits::return_type::type; + +template +using args_type = boost::callable_traits::args_t; + +template +using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size>::value + N : N)>; + } // namespace detail } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index 61f8e1a4..fb909a6d 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -36,7 +36,6 @@ public: using storage_type = Storage; using element_type = typename storage_type::element_type; using const_reference = typename storage_type::const_reference; - using scale_type = typename storage_type::scale_type; using const_iterator = iterator_over; histogram() = default; @@ -85,15 +84,13 @@ public: return *this; } - histogram& operator*=(const scale_type& rhs) { - storage_ *= rhs; + histogram& operator*=(const double x) { + storage_ *= x; return *this; } - histogram& operator/=(const scale_type& rhs) { - static_assert(std::is_floating_point::value, - "division requires a floating point type"); - storage_ *= scale_type(1) / rhs; + histogram& operator/=(const double x) { + storage_ *= 1.0 / x; return *this; } @@ -156,7 +153,7 @@ public: /// Fill histogram with a weight and a value tuple template - void operator()(detail::weight_type&& w, const Ts&... ts) { + void operator()(weight_type&& w, const Ts&... ts) { // case with one argument needs special treatment, specialized below const auto index = detail::call_impl(detail::no_container_tag(), axes_, ts...); if (index) storage_.add(*index, w); @@ -179,7 +176,7 @@ public: } template - void operator()(detail::weight_type&& w, const T& t) { + void operator()(weight_type&& w, const T& t) { // check whether we need to unpack argument const auto index = detail::call_impl(detail::classify_container(), axes_, t); if (index) storage_.add(*index, w); @@ -285,7 +282,8 @@ auto make_histogram(T&& axis0, Ts&&... axis) } /// dynamic type factory from vector-like with custom storage type -template > +template , + typename = detail::requires_axis>> histogram, detail::rm_cv_ref> make_histogram_with(S&& s, T&& t) { return histogram, detail::rm_cv_ref>( @@ -294,7 +292,8 @@ make_histogram_with(S&& s, T&& t) { } /// dynamic type factory from vector-like with standard storage type -template > +template , + typename = detail::requires_axis>> auto make_histogram(T&& t) -> decltype(make_histogram_with(default_storage(), std::forward(t))) { @@ -311,13 +310,13 @@ make_histogram_with(Storage&& s, Iterator begin, Iterator end) { return make_histogram_with(std::forward(s), std::move(axes)); } -/// dynamic type factory from iterator range with standard storage type -template > -auto make_histogram(Iterator begin, Iterator end) - -> decltype(make_histogram_with(default_storage(), begin, end)) -{ - return make_histogram_with(default_storage(), begin, end); -} +// /// dynamic type factory from iterator range with standard storage type +// template > +// auto make_histogram(Iterator begin, Iterator end) +// -> decltype(make_histogram_with(default_storage(), begin, end)) +// { +// return make_histogram_with(default_storage(), begin, end); +// } } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index d22466cd..a0fb4c29 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -26,7 +26,7 @@ namespace transform { template struct identity; template struct log; template struct sqrt; -template struct unit; +template struct quantity; template struct pow; } // namespace transform @@ -60,7 +60,7 @@ class variant; template > struct adaptive_storage; -template > +template > struct array_storage; using default_storage = adaptive_storage<>; diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index 94b603c4..342f2382 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -45,8 +44,35 @@ bool safe_assign(T& t, const U& u) { return true; } +template +struct make_unsigned_impl; + +template +struct make_unsigned_impl { + using type = typename std::make_unsigned::type; +}; + +template +struct make_unsigned_impl { + using type = T; +}; + +template +using make_unsigned = typename make_unsigned_impl::type>::type; + template bool safe_radd(T& t, const U& u) { + BOOST_ASSERT(t >= 0); + BOOST_ASSERT(u >= 0); + using V = make_unsigned; + // static_cast converts back from signed to unsigned integer + if (static_cast(std::numeric_limits::max() - t) < static_cast(u)) return false; + t += static_cast(u); // static_cast to suppress conversion warning + return true; +} + +template +bool safe_radd(T& t, const boost::multiprecision::number& u) { BOOST_ASSERT(t >= 0); BOOST_ASSERT(u >= 0); // static_cast converts back from signed to unsigned integer @@ -64,11 +90,9 @@ struct adaptive_storage { "adaptive_storage requires allocator with trivial pointer type"); using allocator_type = Allocator; - using element_type = weight_counter; - using scale_type = double; - using const_reference = element_type; + using element_type = double; + using const_reference = double; - using wcount = weight_counter; using mp_int = boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 0, 0, boost::multiprecision::signed_magnitude, @@ -80,7 +104,7 @@ struct adaptive_storage { >; using types = mp11::mp_list; + mp_int, double>; template static constexpr char type_index() { @@ -92,6 +116,7 @@ struct adaptive_storage { char type; std::size_t size; void* ptr; + buffer_type(std::size_t s = 0, const allocator_type& a = allocator_type()) : alloc(a), type(0), size(s), ptr(nullptr) {} @@ -131,16 +156,72 @@ struct adaptive_storage { type = type_index(); ptr = p; } + + template + auto apply(F&& f, Ts&&... ts) + -> decltype(f(ptr, *this, std::forward(ts)...)) + { + // this is intentionally not a switch, the if-chain is faster in benchmarks + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + // b.type == 0 is intentionally the last in the chain, + // because it is rarely triggered + return f(ptr, *this, std::forward(ts)...); + } + + template + auto apply(F&& f, Ts&&... ts) const + -> decltype(f(ptr, *this, std::forward(ts)...)) + { + // this is intentionally not a switch, the if-chain is faster in benchmarks + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + if (type == type_index()) + return f(reinterpret_cast(ptr), *this, + std::forward(ts)...); + // b.type == 0 is intentionally the last in the chain, + // because it is rarely triggered + return f(ptr, *this, std::forward(ts)...); + } }; - ~adaptive_storage() { apply(destroyer(), buffer); } + ~adaptive_storage() { buffer.apply(destroyer()); } adaptive_storage(const adaptive_storage& o) { - apply(replacer(), o.buffer, buffer); + o.buffer.apply(replacer(), buffer); } adaptive_storage& operator=(const adaptive_storage& o) { - if (this != &o) { apply(replacer(), o.buffer, buffer); } + if (this != &o) { o.buffer.apply(replacer(), buffer); } return *this; } @@ -157,8 +238,8 @@ struct adaptive_storage { template > explicit adaptive_storage(const S& s) : buffer(s.size(), s.get_allocator()) { - buffer.set(buffer.template create()); - auto it = static_cast(buffer.ptr); + buffer.set(buffer.template create()); + auto it = static_cast(buffer.ptr); const auto end = it + size(); std::size_t i = 0; while (it != end) *it++ = s[i++]; @@ -167,7 +248,7 @@ struct adaptive_storage { template > adaptive_storage& operator=(const S& s) { // no check for self-assign needed, since S is different type - apply(destroyer(), buffer); + buffer.apply(destroyer()); buffer.alloc = s.get_allocator(); buffer.size = s.size(); buffer.set(buffer.template create()); @@ -182,7 +263,7 @@ struct adaptive_storage { allocator_type get_allocator() const { return buffer.alloc; } void reset(std::size_t s) { - apply(destroyer(), buffer); + buffer.apply(destroyer()); buffer.size = s; buffer.set(buffer.template create()); } @@ -191,22 +272,28 @@ struct adaptive_storage { void increase(std::size_t i) { BOOST_ASSERT(i < size()); - apply(increaser(), buffer, i); + buffer.apply(increaser(), i); } template void add(std::size_t i, const T& x) { BOOST_ASSERT(i < size()); - apply(adder(), buffer, i, x); + buffer.apply(adder(), i, x); + } + + template + void add(std::size_t i, const weight_type& x) { + BOOST_ASSERT(i < size()); + buffer.apply(adder(), i, x.value); } const_reference operator[](std::size_t i) const { - return apply(getter(), buffer, i); + return buffer.apply(getter(), i); } bool operator==(const adaptive_storage& o) const { if (size() != o.size()) return false; - return apply(comparer(), buffer, o.buffer); + return buffer.apply(comparer(), o.buffer); } // precondition: storages have same size @@ -221,9 +308,9 @@ struct adaptive_storage { frequently in real applications. */ const auto copy = o; - apply(buffer_adder(), copy.buffer, buffer); + copy.buffer.apply(buffer_adder(), buffer); } else { - apply(buffer_adder(), o.buffer, buffer); + o.buffer.apply(buffer_adder(), buffer); } return *this; } @@ -237,7 +324,7 @@ struct adaptive_storage { } adaptive_storage& operator*=(const double x) { - apply(multiplier(), buffer, x); + buffer.apply(multiplier(), x); return *this; } @@ -261,39 +348,13 @@ struct adaptive_storage { void operator()(void*, Buffer&) {} }; - template - static typename std::result_of::type apply(F&& f, B&& b, Ts&&... ts) { - // this is intentionally not a switch, the if-chain is faster in benchmarks - if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), std::forward(b), - std::forward(ts)...); - if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), std::forward(b), - std::forward(ts)...); - if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), std::forward(b), - std::forward(ts)...); - if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), std::forward(b), - std::forward(ts)...); - if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), std::forward(b), - std::forward(ts)...); - if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), std::forward(b), - std::forward(ts)...); - // b.type == 0 is intentionally the last in the chain, because it is rarely - // triggered - return f(b.ptr, std::forward(b), std::forward(ts)...); - } - struct replacer { template void operator()(T* optr, const OBuffer& ob, Buffer& b) { if (b.size == ob.size && b.type == ob.type) { std::copy(optr, optr + ob.size, reinterpret_cast(b.ptr)); } else { - apply(destroyer(), b); + b.apply(destroyer()); b.alloc = ob.alloc; b.size = ob.size; b.set(b.template create(optr)); @@ -302,7 +363,7 @@ struct adaptive_storage { template void operator()(void*, const OBuffer& ob, Buffer& b) { - apply(destroyer(), b); + b.apply(destroyer()); b.type = 0; b.size = ob.size; } @@ -333,7 +394,7 @@ struct adaptive_storage { } template - void operator()(wcount* tp, Buffer&, std::size_t i) { + void operator()(double* tp, Buffer&, std::size_t i) { ++tp[i]; } }; @@ -357,15 +418,15 @@ struct adaptive_storage { template void if_U_is_integral(std::false_type, T* tp, Buffer& b, std::size_t i, const U& x) { - auto ptr = b.template create(tp); + auto ptr = b.template create(tp); destroyer()(tp, b); b.set(ptr); - operator()(static_cast(b.ptr), b, i, x); + operator()(static_cast(b.ptr), b, i, x); } template void operator()(T* tp, Buffer& b, std::size_t i, const U& x) { - if_U_is_integral(std::integral_constant::value || std::is_same::value)>(), tp, b, i, x); } @@ -378,12 +439,12 @@ struct adaptive_storage { } template - void operator()(wcount* tp, Buffer&, std::size_t i, const U& x) { + void operator()(double* tp, Buffer&, std::size_t i, const U& x) { tp[i] += x; } template - void operator()(wcount* tp, Buffer&, std::size_t i, const mp_int& x) { + void operator()(double* tp, Buffer&, std::size_t i, const mp_int& x) { tp[i] += static_cast(x); } }; @@ -391,7 +452,7 @@ struct adaptive_storage { struct buffer_adder { template void operator()(T* tp, const OBuffer&, Buffer& b) { - for (std::size_t i = 0; i < b.size; ++i) { apply(adder(), b, i, tp[i]); } + for (std::size_t i = 0; i < b.size; ++i) { b.apply(adder(), i, tp[i]); } } template @@ -400,22 +461,31 @@ struct adaptive_storage { struct getter { template - wcount operator()(T* tp, Buffer&, std::size_t i) { - return static_cast(tp[i]); + double operator()(T* tp, Buffer&, std::size_t i) { + return static_cast(tp[i]); } template - wcount operator()(void*, Buffer&, std::size_t) { - return static_cast(0); + double operator()(void*, Buffer&, std::size_t) { + return 0.0; } }; // precondition: buffers already have same size struct comparer { struct inner { + struct cmp { + template + bool operator()(const T& t, const U& u) { return t == u; } + bool operator()(const mp_int& t, const double& u) { + return static_cast(t) == u; } + bool operator()(const double& t, const mp_int& u) { + return t == static_cast(u); } + }; + template bool operator()(const U* optr, const OBuffer& ob, const T* tp) { - return std::equal(optr, optr + ob.size, tp); + return std::equal(optr, optr + ob.size, tp, cmp()); } template @@ -437,24 +507,24 @@ struct adaptive_storage { template bool operator()(const T* tp, const Buffer& b, const OBuffer& ob) { BOOST_ASSERT(b.size == ob.size); - return apply(inner(), ob, tp); + return ob.apply(inner(), tp); } }; struct multiplier { template void operator()(T* tp, Buffer& b, const double x) { - auto ptr = b.template create(tp); + auto ptr = b.template create(tp); destroyer()(tp, b); b.set(ptr); - operator()(reinterpret_cast(b.ptr), b, x); + operator()(reinterpret_cast(b.ptr), b, x); } template void operator()(void*, Buffer&, const double) {} template - void operator()(wcount* tp, Buffer& b, const double x) { + void operator()(double* tp, Buffer& b, const double x) { for (auto end = tp + b.size; tp != end; ++tp) *tp *= x; } }; diff --git a/include/boost/histogram/storage/array_storage.hpp b/include/boost/histogram/storage/array_storage.hpp index d621ffb5..77a6ecb5 100644 --- a/include/boost/histogram/storage/array_storage.hpp +++ b/include/boost/histogram/storage/array_storage.hpp @@ -18,11 +18,10 @@ namespace boost { namespace histogram { -template +template struct array_storage { using allocator_type = Allocator; using element_type = T; - using scale_type = ScaleType; using const_reference = const T&; using buffer_type = std::vector; @@ -90,7 +89,7 @@ struct array_storage { return *this; } - array_storage& operator*=(const scale_type& x) noexcept { + array_storage& operator*=(const double x) noexcept { for (std::size_t i = 0; i < size(); ++i) buffer[i] *= x; return *this; } diff --git a/include/boost/histogram/storage/weight_counter.hpp b/include/boost/histogram/storage/weight_counter.hpp index 55c4127e..b0b86f36 100644 --- a/include/boost/histogram/storage/weight_counter.hpp +++ b/include/boost/histogram/storage/weight_counter.hpp @@ -56,7 +56,7 @@ public: } template - weight_counter& operator+=(const detail::weight_type& rhs) { + weight_counter& operator+=(const weight_type& rhs) { const auto x = static_cast(rhs.value); w += x; w2 += x * x; diff --git a/include/boost/histogram/weight.hpp b/include/boost/histogram/weight.hpp index 50baf61e..c070c6fe 100644 --- a/include/boost/histogram/weight.hpp +++ b/include/boost/histogram/weight.hpp @@ -7,31 +7,23 @@ #ifndef BOOST_HISTOGRAM_WEIGHT_HPP #define BOOST_HISTOGRAM_WEIGHT_HPP +#include + namespace boost { namespace histogram { -namespace detail { + +/// Type wrapper to make T as weight template struct weight_type { T value; }; -template -struct sample_type { - T value; -}; -} // namespace detail - /// Helper function to mark argument as a weight template -detail::weight_type weight(T&& t) { - return {t}; +weight_type weight(T&& t) { + return {std::forward(t)}; } -/// Helper function to mark argument as a sample -template -detail::sample_type sample(T&& t) { - return {t}; -} } } diff --git a/test/adaptive_storage_test.cpp b/test/adaptive_storage_test.cpp index 7ff9276e..f934916d 100644 --- a/test/adaptive_storage_test.cpp +++ b/test/adaptive_storage_test.cpp @@ -15,7 +15,7 @@ namespace bh = boost::histogram; using adaptive_storage_type = bh::adaptive_storage>; -template using array_storage = bh::array_storage>; +template using array_storage = bh::array_storage>; using bh::weight; template @@ -53,8 +53,7 @@ template void equal_impl() { auto a = prepare(1); auto b = prepare(1, T(0)); - BOOST_TEST_EQ(a[0].value(), 0.0); - BOOST_TEST_EQ(a[0].variance(), 0.0); + BOOST_TEST_EQ(a[0], 0.0); BOOST_TEST(a == b); b.increase(0); BOOST_TEST(!(a == b)); @@ -73,8 +72,7 @@ void equal_impl() { auto b = prepare(1, 0); auto c = prepare(2, 0); auto d = prepare(1); - BOOST_TEST_EQ(a[0].value(), 0.0); - BOOST_TEST_EQ(a[0].variance(), 0.0); + BOOST_TEST_EQ(a[0], 0.0); BOOST_TEST(a == b); BOOST_TEST(b == a); BOOST_TEST(a == d); @@ -100,24 +98,24 @@ void increase_and_grow_impl() { auto x = prepare(2); x.increase(0); - n2.add(0, x[0].value()); + n2.add(0, x[0]); double v = tmax; ++v; - BOOST_TEST_EQ(n[0].value(), v); - BOOST_TEST_EQ(n2[0].value(), v); - BOOST_TEST_EQ(n[1].value(), 0.0); - BOOST_TEST_EQ(n2[1].value(), 0.0); + BOOST_TEST_EQ(n[0], v); + BOOST_TEST_EQ(n2[0], v); + BOOST_TEST_EQ(n[1], 0.0); + BOOST_TEST_EQ(n2[1], 0.0); } template <> void increase_and_grow_impl() { auto s = prepare(2); - BOOST_TEST_EQ(s[0].value(), 0); - BOOST_TEST_EQ(s[1].value(), 0); + BOOST_TEST_EQ(s[0], 0); + BOOST_TEST_EQ(s[1], 0); s.increase(0); - BOOST_TEST_EQ(s[0].value(), 1); - BOOST_TEST_EQ(s[1].value(), 0); + BOOST_TEST_EQ(s[0], 1); + BOOST_TEST_EQ(s[1], 0); } template @@ -129,20 +127,20 @@ void convert_array_storage_impl() { auto a = aref; a = s; - BOOST_TEST_EQ(a[0].value(), 1.0); + BOOST_TEST_EQ(a[0], 1.0); BOOST_TEST(a == s); a.increase(0); BOOST_TEST(!(a == s)); adaptive_storage_type b(s); - BOOST_TEST_EQ(b[0].value(), 1.0); + BOOST_TEST_EQ(b[0], 1.0); BOOST_TEST(b == s); b.increase(0); BOOST_TEST(!(b == s)); auto c = aref; c.add(0, s[0]); - BOOST_TEST_EQ(c[0].value(), 1.0); + BOOST_TEST_EQ(c[0], 1.0); BOOST_TEST(c == s); BOOST_TEST(s == c); @@ -156,20 +154,20 @@ void convert_array_storage_impl() { auto e = aref; e = s; - BOOST_TEST_EQ(e[0].value(), 1.0); + BOOST_TEST_EQ(e[0], 1.0); BOOST_TEST(e == s); e.increase(0); BOOST_TEST(!(e == s)); adaptive_storage_type f(s); - BOOST_TEST_EQ(f[0].value(), 1.0); + BOOST_TEST_EQ(f[0], 1.0); BOOST_TEST(f == s); f.increase(0); BOOST_TEST(!(f == s)); auto g = aref; g.add(0, s[0]); - BOOST_TEST_EQ(g[0].value(), 1.0); + BOOST_TEST_EQ(g[0], 1.0); BOOST_TEST(g == s); BOOST_TEST(s == g); @@ -185,21 +183,21 @@ void convert_array_storage_impl() { template <> void convert_array_storage_impl() { const auto aref = prepare(1); - BOOST_TEST_EQ(aref[0].value(), 0.0); + BOOST_TEST_EQ(aref[0], 0.0); array_storage s; s.reset(1); s.increase(0); auto a = aref; a = s; - BOOST_TEST_EQ(a[0].value(), 1.0); + BOOST_TEST_EQ(a[0], 1.0); BOOST_TEST(a == s); a.increase(0); BOOST_TEST(!(a == s)); auto c = aref; c.add(0, s[0]); - BOOST_TEST_EQ(c[0].value(), 1.0); + BOOST_TEST_EQ(c[0], 1.0); BOOST_TEST(c == s); BOOST_TEST(s == c); @@ -216,15 +214,14 @@ void add_impl() { auto b = prepare(2); if (std::is_same::value) { a += b; - BOOST_TEST_EQ(a[0].value(), 0); - BOOST_TEST_EQ(a[1].value(), 0); + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 0); } else { b.increase(0); b.increase(0); a += b; - BOOST_TEST_EQ(a[0].value(), 2); - BOOST_TEST_EQ(a[0].variance(), 2); - BOOST_TEST_EQ(a[1].value(), 0); + BOOST_TEST_EQ(a[0], 2); + BOOST_TEST_EQ(a[1], 0); } } @@ -236,7 +233,7 @@ void add_impl_all_rhs() { add_impl(); add_impl(); add_impl(); - add_impl(); + add_impl(); } int main() { @@ -270,7 +267,7 @@ int main() { // copy { - copy_impl(); + copy_impl(); copy_impl(); copy_impl(); copy_impl(); @@ -287,7 +284,7 @@ int main() { equal_impl(); equal_impl(); equal_impl(); - equal_impl(); + equal_impl(); } // increase_and_grow @@ -300,11 +297,11 @@ int main() { // only increase for mp_int auto a = prepare(2, 1); - BOOST_TEST_EQ(a[0].value(), 1); - BOOST_TEST_EQ(a[1].value(), 0); + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 0); a.increase(0); - BOOST_TEST_EQ(a[0].value(), 2); - BOOST_TEST_EQ(a[1].value(), 0); + BOOST_TEST_EQ(a[0], 2); + BOOST_TEST_EQ(a[1], 0); } // add @@ -315,38 +312,33 @@ int main() { add_impl_all_rhs(); add_impl_all_rhs(); add_impl_all_rhs(); - add_impl_all_rhs(); + add_impl_all_rhs(); } // add_and_grow { auto a = prepare(1); a += a; - BOOST_TEST_EQ(a[0].value(), 0); + BOOST_TEST_EQ(a[0], 0); a.increase(0); double x = 1; auto b = prepare(1); b.increase(0); - BOOST_TEST_EQ(b[0].value(), x); + BOOST_TEST_EQ(b[0], x); for (unsigned i = 0; i < 80; ++i) { x += x; - a.add(0, a[0].value()); + a.add(0, a[0]); b += b; - BOOST_TEST_EQ(a[0].value(), x); - BOOST_TEST_EQ(a[0].variance(), x); - BOOST_TEST_EQ(b[0].value(), x); - BOOST_TEST_EQ(b[0].variance(), x); + BOOST_TEST_EQ(a[0], x); + BOOST_TEST_EQ(b[0], x); auto c = prepare(1); - c.add(0, a[0].value()); - BOOST_TEST_EQ(c[0].value(), x); - BOOST_TEST_EQ(c[0].variance(), x); + c.add(0, a[0]); + BOOST_TEST_EQ(c[0], x); c.add(0, weight(0)); - BOOST_TEST_EQ(c[0].value(), x); - BOOST_TEST_EQ(c[0].variance(), x); + BOOST_TEST_EQ(c[0], x); auto d = prepare(1); d.add(0, weight(x)); - BOOST_TEST_EQ(d[0].value(), x); - BOOST_TEST_EQ(d[0].variance(), x * x); + BOOST_TEST_EQ(d[0], x); } } @@ -354,24 +346,18 @@ int main() { { auto a = prepare(2); a *= 2; - BOOST_TEST_EQ(a[0].value(), 0); - BOOST_TEST_EQ(a[1].value(), 0); + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 0); a.increase(0); a *= 3; - BOOST_TEST_EQ(a[0].value(), 3); - BOOST_TEST_EQ(a[0].variance(), 9); - BOOST_TEST_EQ(a[1].value(), 0); - BOOST_TEST_EQ(a[1].variance(), 0); - a.add(1, adaptive_storage_type::element_type(2, 5)); - BOOST_TEST_EQ(a[0].value(), 3); - BOOST_TEST_EQ(a[0].variance(), 9); - BOOST_TEST_EQ(a[1].value(), 2); - BOOST_TEST_EQ(a[1].variance(), 5); + BOOST_TEST_EQ(a[0], 3); + BOOST_TEST_EQ(a[1], 0); + a.add(1, 2); + BOOST_TEST_EQ(a[0], 3); + BOOST_TEST_EQ(a[1], 2); a *= 3; - BOOST_TEST_EQ(a[0].value(), 9); - BOOST_TEST_EQ(a[0].variance(), 81); - BOOST_TEST_EQ(a[1].value(), 6); - BOOST_TEST_EQ(a[1].variance(), 45); + BOOST_TEST_EQ(a[0], 9); + BOOST_TEST_EQ(a[1], 6); } // convert_array_storage @@ -382,7 +368,7 @@ int main() { convert_array_storage_impl(); convert_array_storage_impl(); convert_array_storage_impl(); - convert_array_storage_impl(); + convert_array_storage_impl(); } return boost::report_errors(); From 945b547048136f94d7364aaafc3236668d9aef13 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 20 Oct 2018 13:02:42 -0400 Subject: [PATCH 03/44] fixed axis_test --- CMakeLists.txt | 19 +- doc/guide.qbk | 11 +- examples/getting_started_listing_02.cpp | 20 +- examples/guide_axis_with_uoflow_off.cpp | 5 +- examples/guide_custom_minimal_axis.cpp | 23 ++ ...xis.cpp => guide_custom_modified_axis.cpp} | 2 +- include/boost/histogram/axis/base.hpp | 8 +- .../boost/histogram/axis/interval_view.hpp | 3 +- include/boost/histogram/axis/iterator.hpp | 41 +-- .../histogram/axis/ostream_operators.hpp | 122 ++++---- include/boost/histogram/axis/traits.hpp | 2 +- include/boost/histogram/axis/types.hpp | 68 ++++- include/boost/histogram/axis/variant.hpp | 246 ++++++++++----- include/boost/histogram/detail/axes.hpp | 91 ++---- include/boost/histogram/detail/meta.hpp | 164 ++++++---- include/boost/histogram/histogram.hpp | 59 ++-- include/boost/histogram/histogram_fwd.hpp | 4 +- test/axis_test.cpp | 287 ++++++++++-------- ..._static_at_vector_wrong_dimension_fail.cpp | 4 +- test/meta_test.cpp | 172 +++++++++-- test/utility.hpp | 23 +- 21 files changed, 824 insertions(+), 550 deletions(-) create mode 100644 examples/guide_custom_minimal_axis.cpp rename examples/{guide_custom_axis.cpp => guide_custom_modified_axis.cpp} (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a061894e..d05d7c5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,20 +19,6 @@ option(TEST_SERIALIZATION "Test serialization code" OFF) mark_as_advanced(BUILD_BENCHMARKS) mark_as_advanced(TEST_SERIALIZATION) -# set build type if none is specified -set(default_build_type "Release") -if (EXISTS "${PROJECT_SOURCE_DIR}/.git") - set(default_build_type "Debug") -endif() -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${default_build_type}' as none was specified.") - set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE - STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") -endif() - # serialization is optional if (TEST_SERIALIZATION) find_package(Boost ${MIN_BOOST_VERSION} REQUIRED COMPONENTS serialization) @@ -59,6 +45,8 @@ function(compiled_test SRC) if(CMAKE_BUILD_TYPE MATCHES coverage) target_compile_options(${BASENAME} PRIVATE -O0 -g --coverage) target_link_libraries(${BASENAME} PRIVATE --coverage) + else() + target_compile_options(${BASENAME} PRIVATE -O0 -g) endif() # max warnings @@ -118,7 +106,8 @@ compiled_test(examples/getting_started_listing_02.cpp) compiled_test(examples/guide_access_bin_counts.cpp) compiled_test(examples/guide_axis_with_labels.cpp) compiled_test(examples/guide_axis_with_uoflow_off.cpp) -compiled_test(examples/guide_custom_axis.cpp) +compiled_test(examples/guide_custom_modified_axis.cpp) +compiled_test(examples/guide_custom_minimal_axis.cpp) compiled_test(examples/guide_custom_storage.cpp) compiled_test(examples/guide_fill_histogram.cpp) compiled_test(examples/guide_histogram_operators.cpp) diff --git a/doc/guide.qbk b/doc/guide.qbk index 951ba93b..0a9a924e 100644 --- a/doc/guide.qbk +++ b/doc/guide.qbk @@ -174,8 +174,15 @@ In C++, users can implement their own axis class without touching any library co The simplest way to make a custom axis is to derive from a builtin class. Here is a contrived example of a custom axis that inherits from the [classref boost::histogram::axis::integer integer axis] and accepts c-strings representing numbers. -[import ../examples/guide_custom_axis.cpp] -[guide_custom_axis] +[import ../examples/guide_custom_modified_axis.cpp] +[guide_custom_modified_axis] + +Alternatively, you can also make an axis completely from scratch. An minimal axis is a functor that maps an input to a bin index. The index has a range `[0, AxisType::size())`. + +[import ../examples/guide_custom_minimal_axis.cpp] +[guide_custom_minimal_axis] + +Such a minimal axis works, even though it lacks convenience features provided by the builtin axis types. For example, one cannot iterate over this axis. Not even a bin description can be queried, because `operator[]` is not implemented. It is up to the user to implement these optional aspects. [endsect] diff --git a/examples/getting_started_listing_02.cpp b/examples/getting_started_listing_02.cpp index 856706fe..d5d020a6 100644 --- a/examples/getting_started_listing_02.cpp +++ b/examples/getting_started_listing_02.cpp @@ -8,13 +8,13 @@ namespace bh = boost::histogram; int main() { /* - create a dynamic histogram with the factory `make_dynamic_histogram` + 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 + axis::variant, the polymorphic type that can hold concrete axis types */ std::vector< - bh::axis::any< + bh::axis::variant< bh::axis::regular<>, bh::axis::category > @@ -22,7 +22,7 @@ int main() { axes.emplace_back(bh::axis::category({"red", "blue"})); axes.emplace_back(bh::axis::regular<>(5, 0, 1, "x")); axes.emplace_back(bh::axis::regular<>(5, 0, 1, "y")); - auto h = bh::make_dynamic_histogram(axes.begin(), axes.end()); + auto h = bh::make_histogram(axes.begin(), axes.end()); // fill histogram with data, usually this happens in a loop h("red", 0.1, 0.2); @@ -31,18 +31,16 @@ int main() { h("red", 0.7, 0.8); /* - 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 + Print dynamic histogram by iterating over bins. + If the [bin type] of the axis is not convertible to a + double interval, you need to cast axis::variant before looping; + this is here the case for the category axis. */ using cas = bh::axis::category; for (auto cbin : static_cast(h.axis(0))) { for (auto ybin : h.axis(2)) { // rows for (auto xbin : h.axis(1)) { // columns - const auto v = h.at(cbin, xbin, ybin).value(); + const auto v = h.at(cbin, xbin, ybin); if (v) std::printf("%4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n", cbin.value().c_str(), diff --git a/examples/guide_axis_with_uoflow_off.cpp b/examples/guide_axis_with_uoflow_off.cpp index 69e5fc65..e593d07a 100644 --- a/examples/guide_axis_with_uoflow_off.cpp +++ b/examples/guide_axis_with_uoflow_off.cpp @@ -6,8 +6,9 @@ namespace bh = boost::histogram; int main() { // create a 1d-histogram for dice throws with integer values from 1 to 6 - auto h = bh::make_static_histogram( - bh::axis::integer<>(1, 7, "eyes", bh::axis::uoflow_type::off)); + auto h = bh::make_histogram( + bh::axis::integer<>(1, 7, "eyes", bh::axis::option_type::none) + ); // do something with h } diff --git a/examples/guide_custom_minimal_axis.cpp b/examples/guide_custom_minimal_axis.cpp new file mode 100644 index 00000000..36c0cc74 --- /dev/null +++ b/examples/guide_custom_minimal_axis.cpp @@ -0,0 +1,23 @@ +//[ guide_custom_minimal_axis + +#include +#include + +namespace bh = boost::histogram; + +// stateless axis which returns 1 if the input is even and 0 otherwise +struct minimal_axis { + int operator()(int x) const { return x % 2 == 0; } + unsigned size() const { return 2; } +}; + +int main() { + auto h = bh::make_histogram(minimal_axis()); + + h(0); h(1); h(2); + + assert(h.at(0) == 2); // two even numbers + assert(h.at(1) == 1); // one uneven number +} + +//] diff --git a/examples/guide_custom_axis.cpp b/examples/guide_custom_modified_axis.cpp similarity index 96% rename from examples/guide_custom_axis.cpp rename to examples/guide_custom_modified_axis.cpp index 67332fab..60018e7c 100644 --- a/examples/guide_custom_axis.cpp +++ b/examples/guide_custom_modified_axis.cpp @@ -1,4 +1,4 @@ -//[ guide_custom_axis +//[ guide_custom_modified_axis #include #include diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index 28bb3033..c481f4b4 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -111,7 +112,7 @@ template class iterator_mixin { public: using const_iterator = iterator_over; - using const_reverse_iterator = reverse_iterator_over; + using const_reverse_iterator = boost::reverse_iterator; const_iterator begin() const noexcept { return const_iterator(*static_cast(this), 0); @@ -121,11 +122,10 @@ public: static_cast(this)->size()); } const_reverse_iterator rbegin() const noexcept { - return const_reverse_iterator(*static_cast(this), - static_cast(this)->size()); + return boost::make_reverse_iterator(end()); } const_reverse_iterator rend() const noexcept { - return const_reverse_iterator(*static_cast(this), 0); + return boost::make_reverse_iterator(begin()); } }; diff --git a/include/boost/histogram/axis/interval_view.hpp b/include/boost/histogram/axis/interval_view.hpp index 121794fc..3abe9e2b 100644 --- a/include/boost/histogram/axis/interval_view.hpp +++ b/include/boost/histogram/axis/interval_view.hpp @@ -7,7 +7,6 @@ #ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP #define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP -#include #include namespace boost { @@ -32,7 +31,7 @@ public: auto upper() const noexcept -> decltype(std::declval().lower(0)) { return axis_.lower(idx_ + 1); } - typename Axis::value_type width() const noexcept { + auto width() const noexcept -> decltype(upper() - lower()) { return upper() - lower(); } diff --git a/include/boost/histogram/axis/iterator.hpp b/include/boost/histogram/axis/iterator.hpp index 04c676d8..4c34a354 100644 --- a/include/boost/histogram/axis/iterator.hpp +++ b/include/boost/histogram/axis/iterator.hpp @@ -15,9 +15,13 @@ namespace axis { template class iterator_over - : public iterator_facade, typename Axis::bin_type, - random_access_traversal_tag, - typename Axis::bin_type> { + : public iterator_facade< + iterator_over, + decltype(std::declval()[0]), + random_access_traversal_tag, + decltype(std::declval()[0]), + int + > { public: explicit iterator_over(const Axis& axis, int idx) : axis_(axis), idx_(idx) {} @@ -35,36 +39,9 @@ protected: bool equal(const iterator_over& other) const noexcept { return &axis_ == &other.axis_ && idx_ == other.idx_; } - typename Axis::bin_type dereference() const { return axis_[idx_]; } - friend class ::boost::iterator_core_access; + decltype(std::declval()[0]) + dereference() const { return axis_[idx_]; } - const Axis& axis_; - int idx_; -}; - -template -class reverse_iterator_over - : public iterator_facade< - reverse_iterator_over, typename Axis::bin_type, - random_access_traversal_tag, typename Axis::bin_type> { -public: - explicit reverse_iterator_over(const Axis& axis, int idx) - : axis_(axis), idx_(idx) {} - - reverse_iterator_over(const reverse_iterator_over&) = default; - reverse_iterator_over& operator=(const reverse_iterator_over&) = default; - -protected: - void increment() noexcept { --idx_; } - void decrement() noexcept { ++idx_; } - void advance(int n) noexcept { idx_ -= n; } - int distance_to(const reverse_iterator_over& other) const noexcept { - return other.idx_ - idx_; - } - bool equal(const reverse_iterator_over& other) const noexcept { - return &axis_ == &other.axis_ && idx_ == other.idx_; - } - typename Axis::bin_type dereference() const { return axis_[idx_ - 1]; } friend class ::boost::iterator_core_access; const Axis& axis_; diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index a4128ab2..a8f36754 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -12,20 +12,22 @@ #include #include #include -#include +#include +#include #include namespace boost { namespace histogram { -namespace axis { namespace detail { -inline ::boost::string_view to_string(const transform::identity&) { return {}; } -inline ::boost::string_view to_string(const transform::log&) { return {"_log", 4}; } -inline ::boost::string_view to_string(const transform::sqrt&) { return {"_sqrt", 5}; } +template const char* to_string(const axis::transform::identity&) { return ""; } +template const char* to_string(const axis::transform::log&) { return "_log"; } +template const char* to_string(const axis::transform::sqrt&) { return "_sqrt"; } +template const char* to_string(const axis::transform::quantity&) { return "_quantity"; } +template const char* to_string(const axis::transform::pow&) { return "_pow"; } template -void escape_string(OStream& os, const ::boost::string_view s) { +void escape_string(OStream& os, const std::string& s) { os << '\''; for (auto sit = s.begin(); sit != s.end(); ++sit) { if (*sit == '\'' && (sit == s.begin() || *(sit - 1) != '\\')) { @@ -36,8 +38,45 @@ void escape_string(OStream& os, const ::boost::string_view s) { } os << '\''; } + +template +void stream_metadata(OStream& os, const T& t) { + if (detail::is_streamable::value) { + std::ostringstream oss; + oss << t; + if (!oss.str().empty()) + os << ", metadata="; + escape_string(os, oss.str()); + } +} + +template +void stream_options(OStream& os, const axis::option_type o) { + os << ", options="; + switch (o) { + case axis::option_type::none: os << "none"; break; + case axis::option_type::overflow: os << "overflow"; break; + case axis::option_type::underflow_and_overflow: os << "underflow_and_overflow"; break; + } +} + +template +void stream_transform(OStream&, const T&) {} + +template +void stream_transform(OStream& os, const axis::transform::pow& t) { + os << ", power=" << t.power; +} + +template +void stream_transform(OStream& os, const axis::transform::quantity& t) { + os << ", unit=" << t.unit; +} + } // namespace detail +namespace axis { + template std::basic_ostream& operator<<(std::basic_ostream& os, const interval_view& i) { @@ -57,25 +96,9 @@ std::basic_ostream& operator<<(std::basic_ostream& const regular& a) { os << "regular" << detail::to_string(a.transform()) << "(" << a.size() << ", " << a.lower(0) << ", " << a.lower(a.size()); - // if (!a.label().empty()) { - // os << ", label="; - // detail::escape_string(os, a.label()); - // } - // if (!a.uoflow()) { os << ", uoflow=False"; } - os << ")"; - return os; -} - -template -std::basic_ostream& operator<<( - std::basic_ostream& os, const regular, M>& a) { - os << "regular_pow(" << a.size() << ", " << a.lower(0) << ", " << a.lower(a.size()) - << ", " << a.transform().power; - // if (!a.label().empty()) { - // os << ", label="; - // detail::escape_string(os, a.label()); - // } - // if (!a.uoflow()) { os << ", uoflow=False"; } + detail::stream_metadata(os, a.metadata()); + detail::stream_options(os, a.options()); + detail::stream_transform(os, a.transform()); os << ")"; return os; } @@ -83,17 +106,10 @@ std::basic_ostream& operator<<( template std::basic_ostream& operator<<(std::basic_ostream& os, const circular& a) { - os << "circular(" << a.size(); - const auto phase = a.lower(0); - const auto perimeter = a.lower(a.size()) - a.lower(0); - if (phase != 0.0) { os << ", phase=" << phase; } - if (perimeter != circular::two_pi()) { - os << ", perimeter=" << perimeter; - } - // if (!a.label().empty()) { - // os << ", label="; - // detail::escape_string(os, a.label()); - // } + os << "circular(" << a.size() + << ", " << a.lower(0) << ", " << a.lower(a.size()); + detail::stream_metadata(os, a.metadata()); + detail::stream_options(os, a.options()); os << ")"; return os; } @@ -102,12 +118,9 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, const variable& a) { os << "variable(" << a.lower(0); - for (int i = 1; i <= a.size(); ++i) { os << ", " << a.lower(i); } - // if (!a.label().empty()) { - // os << ", label="; - // detail::escape_string(os, a.label()); - // } - // if (!a.uoflow()) { os << ", uoflow=False"; } + for (unsigned i = 1; i <= a.size(); ++i) { os << ", " << a.lower(i); } + detail::stream_metadata(os, a.metadata()); + detail::stream_options(os, a.options()); os << ")"; return os; } @@ -116,11 +129,8 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, const integer& a) { os << "integer(" << a.lower(0) << ", " << a.lower(a.size()); - // if (!a.label().empty()) { - // os << ", label="; - // detail::escape_string(os, a.label()); - // } - // if (!a.uoflow()) { os << ", uoflow=False"; } + detail::stream_metadata(os, a.metadata()); + detail::stream_options(os, a.options()); os << ")"; return os; } @@ -129,27 +139,23 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, const category& a) { os << "category("; - for (int i = 0; i < a.size(); ++i) { os << a[i] << (i == (a.size() - 1) ? "" : ", "); } - // if (!a.label().empty()) { - // os << ", label="; - // detail::escape_string(os, a.label()); - // } + for (unsigned i = 0; i < a.size(); ++i) { os << a[i] << (i == (a.size() - 1) ? "" : ", "); } + detail::stream_metadata(os, a.metadata()); + detail::stream_options(os, a.options()); os << ")"; return os; } template -inline std::basic_ostream& operator<<( +std::basic_ostream& operator<<( std::basic_ostream& os, const category& a) { os << "category("; - for (int i = 0; i < a.size(); ++i) { + for (unsigned i = 0; i < a.size(); ++i) { detail::escape_string(os, a.value(i)); os << (i == (a.size() - 1) ? "" : ", "); } - // if (!a.label().empty()) { - // os << ", label="; - // detail::escape_string(os, a.label()); - // } + detail::stream_metadata(os, a.metadata()); + detail::stream_options(os, a.options()); os << ")"; return os; } diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index f8410fb7..9f281431 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -31,7 +31,7 @@ namespace axis { template struct traits { - using call_args = boost::callable_traits::args_t; + using args = boost::callable_traits::args_t; static option_type options(const T& t) { return detail::axis_traits_options_impl(detail::has_method_options(), t); diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index 5b39fb2e..c0ddb3d6 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -133,7 +133,7 @@ public: : base_type(n, m, o) , data_(trans, n, begin, end) { - if (!std::isnormal(data_.min) || !std::isnormal(data_.delta)) + if (!std::isfinite(data_.min) || !std::isfinite(data_.delta)) throw std::invalid_argument("forward transform of lower or upper invalid"); } @@ -170,6 +170,8 @@ public: return data_.inverse(x); } + const transform_type& transform() const { return data_; } + bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } bool operator==(const regular& o) const noexcept { @@ -216,7 +218,7 @@ public: option_type::overflow : o) , phase_(phase), delta_(perimeter / n) { - if (!std::isnormal(phase) || !(perimeter > 0)) + if (!std::isfinite(phase) || !(perimeter > 0)) throw std::invalid_argument("invalid phase or perimeter"); } @@ -225,7 +227,7 @@ public: /// Returns the bin index for the passed argument. int operator()(value_type x) const noexcept { const auto z = std::floor((x - phase_) / delta_); - if (std::isnormal(z)) { + if (std::isfinite(z)) { const auto i = static_cast(z) % base_type::size(); return i + (i < 0) * base_type::size(); } @@ -269,6 +271,16 @@ class variable : public base, { typename std::allocator_traits::pointer x = nullptr; using allocator_type::allocator_type; + data(const allocator_type& a) : allocator_type(a) {} + data() = default; + + friend void swap(data& a, data& b) noexcept + { + std::swap(a.x, b.x); + auto tmp = static_cast(a); + a = static_cast(b); + b = static_cast(tmp); + } }; public: @@ -329,6 +341,13 @@ public: const allocator_type& a = allocator_type()) : variable(std::begin(iterable), std::end(iterable), m, o, a) {} + template + variable(const std::initializer_list& t, + const metadata_type& m = metadata_type(), + const option_type o = option_type::underflow_and_overflow, + const allocator_type& a = allocator_type()) + : variable(t.begin(), t.end(), m, o, a) {} + variable() = default; variable(const variable& o) : base_type(o), data_(o.data_) { @@ -359,9 +378,9 @@ public: variable& operator=(variable&& o) { if (this != &o) { - std::swap(static_cast(*this), o); - std::swap(static_cast(data_), o.data_); - std::swap(data_.x, o.data_.x); + std::swap(static_cast(*this), + static_cast(o)); + std::swap(data_, o.data_); } return *this; } @@ -378,7 +397,7 @@ public: /// Returns the starting edge of the bin. value_type lower(int i) const noexcept { if (i < 0) { return -std::numeric_limits::infinity(); } - if (i > base_type::size()) { return std::numeric_limits::infinity(); } + if (i > static_cast(base_type::size())) { return std::numeric_limits::infinity(); } return data_.x[i]; } @@ -434,13 +453,13 @@ public: /// Returns the bin index for the passed argument. int operator()(value_type x) const noexcept { const int z = x - min_; - return z >= 0 ? (z > base_type::size() ? base_type::size() : z) : -1; + return z >= 0 ? (z > static_cast(base_type::size()) ? base_type::size() : z) : -1; } /// Returns lower edge of the integral bin. value_type lower(int i) const noexcept { if (i < 0) { return std::numeric_limits::min(); } - if (i > base_type::size()) { return std::numeric_limits::max(); } + if (i > static_cast(base_type::size())) { return std::numeric_limits::max(); } return min_ + i; } @@ -477,6 +496,16 @@ class category : public base, struct data : allocator_type { typename std::allocator_traits::pointer x = nullptr; using allocator_type::allocator_type; + data(const allocator_type& a) : allocator_type(a) {} + data() = default; + + friend void swap(data& a, data& b) noexcept + { + std::swap(a.x, b.x); + auto tmp = static_cast(a); + a = static_cast(b); + b = static_cast(tmp); + } }; public: @@ -506,11 +535,18 @@ public: * \param metadata description of the axis. */ template > - category(T iterable, + category(T t, const metadata_type& m = metadata_type(), const option_type o = option_type::overflow, const allocator_type& a = allocator_type()) - : category(std::begin(iterable), std::end(iterable), m, o, a) {} + : category(std::begin(t), std::end(t), m, o, a) {} + + template + category(const std::initializer_list& t, + const metadata_type& m = metadata_type(), + const option_type o = option_type::overflow, + const allocator_type& a = allocator_type()) + : category(t.begin(), t.end(), m, o, a) {} category() = default; @@ -526,6 +562,7 @@ public: if (base_type::size() != o.size()) { detail::destroy_buffer(data_, data_.x, base_type::size()); base_type::operator=(o); + data_ = o.data_; data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); } else { base_type::operator=(o); @@ -537,15 +574,14 @@ public: category(category&& o) : base_type(std::move(o)) - , data_(std::move(o)) { + , data_(std::move(o.data_)) { o.data_.x = nullptr; } category& operator=(category&& o) { if (this != &o) { - std::swap(static_cast(*this), o); - std::swap(static_cast(data_), o.data_); - std::swap(data_.x, o.data_.x); + std::swap(static_cast(*this), static_cast(o)); + std::swap(data_, o.data_); } return *this; } @@ -563,7 +599,7 @@ public: /// Returns the value for the bin index (performs a range check). const value_type& value(int idx) const { - if (idx < 0 || idx >= base_type::size()) + if (idx < 0 || idx >= static_cast(base_type::size())) throw std::out_of_range("category index out of range"); return data_.x[idx]; } diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 1963ff1c..b85653a1 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -20,55 +20,56 @@ #include #include #include +#include namespace boost { namespace histogram { namespace detail { -// struct call_visitor : public boost::static_visitor { -// const double x; -// explicit call_visitor(const double arg) : x(arg) {} -// template -// int operator()(const T& t) const { -// using args = typename axis::traits::call_args; -// return impl(mp11::mp_bool< -// (mp11::mp_size::value == 1 && -// std::is_convertible>::value) -// >(), t); -// } -// template -// int impl(std::true_type, const T& a) const { -// using U = mp11::mp_first::call_args>; -// return a(static_cast(x)); -// } -// template -// int impl(std::false_type, const T&) const { -// using args = typename axis::traits::call_args; -// throw std::runtime_error(boost::histogram::detail::cat( -// "cannot convert double to ", -// boost::core::demangled_name( BOOST_CORE_TYPEID(args) ), -// " for ", -// boost::core::demangled_name( BOOST_CORE_TYPEID(T) ) -// )); -// } -// }; +struct call_visitor { + const double x; + call_visitor(const double arg) : x(arg) {} + template + int operator()(const T& t) const { + using args = typename axis::traits::args; + return impl(mp11::mp_bool< + (mp11::mp_size::value == 1 && + std::is_convertible>::value) + >(), t); + } + template + int impl(std::true_type, const T& a) const { + using U = mp11::mp_first::args>; + return a(static_cast(x)); + } + template + int impl(std::false_type, const T&) const { + using args = typename axis::traits::args; + throw std::invalid_argument(detail::cat( + "cannot convert double to ", + boost::core::demangled_name( BOOST_CORE_TYPEID(args) ), + " for ", + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ) + )); + } +}; -struct size_visitor : public boost::static_visitor { +struct size_visitor { template unsigned operator()(const T& a) const { return a.size(); } }; -struct options_visitor : public boost::static_visitor { +struct options_visitor { template axis::option_type operator()(const T& t) const { return axis::traits::options(t); } }; -struct lower_visitor : public boost::static_visitor { +struct lower_visitor { int idx; lower_visitor(int i) : idx(i) {} template @@ -81,32 +82,19 @@ struct lower_visitor : public boost::static_visitor { } template double impl(std::false_type, const T&) const { - throw std::runtime_error(boost::histogram::detail::cat( - "cannot use ", boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " with generic boost::histogram::axis::variant interface, use" - " static_cast to access the underlying axis type")); - } -}; - -struct bicmp_visitor : public boost::static_visitor { - template - bool operator()(const T&, const U&) const { - return false; - } - - template - bool operator()(const T& a, const T& b) const { - return a == b; + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " has no lower method")); } }; template -struct assign_visitor : public boost::static_visitor { +struct assign_visitor { T& t; - assign_visitor(T& tt) : t(tt) {} + assign_visitor(T& x) : t(x) {} template void operator()(const U& u) const { - impl(mp11::mp_contains(), u); + impl(mp11::mp_contains, U>(), u); } template @@ -116,11 +104,55 @@ struct assign_visitor : public boost::static_visitor { template void impl(std::false_type, const U&) const { - throw std::invalid_argument(boost::histogram::detail::cat( - "argument ", boost::typeindex::type_id().pretty_name(), - " is not a bounded type of ", boost::typeindex::type_id().pretty_name())); + throw std::invalid_argument(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), + " is not a bounded type of ", + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ) + )); } }; + +template +struct equal_visitor { + const T& t; + equal_visitor(const T& x) : t(x) {} + template + bool operator()(const U& u) const { + return t == u; + } +}; + +template +struct ostream_visitor { + OStream& os; + ostream_visitor(OStream& o) : os(o) {} + template + void operator()(const T& t) const { + impl(is_streamable(), t); + } + template + void impl(std::true_type, const T& t) const { + os << t; + } + template + void impl(std::false_type, const T&) const { + throw std::invalid_argument(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " is not streamable")); + } +}; + +template +struct functor_wrapper : public boost::static_visitor { + F fcn; + functor_wrapper(F f) : fcn(std::forward(f)) {} + + template + R operator()(T&& t) const { + return fcn(std::forward(t)); + } +}; + } // namespace detail namespace axis { @@ -128,8 +160,8 @@ namespace axis { /// Polymorphic axis type template class variant - : public iterator_mixin> - , private boost::variant + : private boost::variant + , public iterator_mixin> { using base_type = boost::variant; using bin_type = interval_view; @@ -137,11 +169,9 @@ class variant template using requires_bounded_type = mp11::mp_if>, void>; + detail::rm_cvref>, void>; public: - using types = mp11::mp_list; - variant() = default; template > @@ -155,33 +185,33 @@ public: template variant(const variant& u) { - boost::apply_visitor(detail::assign_visitor(*this), u); + visit(detail::assign_visitor(*this), u); } template variant& operator=(const variant& u) { - boost::apply_visitor(detail::assign_visitor(*this), u); + visit(detail::assign_visitor(*this), u); return *this; } unsigned size() const { - return boost::apply_visitor(detail::size_visitor(), *this); + return visit(detail::size_visitor(), *this); } option_type options() const { - return boost::apply_visitor(detail::options_visitor(), *this); + return visit(detail::options_visitor(), *this); } - // // this only works for axes with compatible call signature - // // and will throw a runtime_error otherwise - // int operator()(double x) const { - // return boost::apply_visitor(detail::call_visitor(x), *this); - // } + // Only works for axes with compatible call signature + // and will throw a invalid_argument exception otherwise + int operator()(double x) const { + return visit(detail::call_visitor(x), *this); + } - // this only works for axes with a lower method + // Only works for axes with a lower method // and will throw a runtime_error otherwise double lower(int idx) const { - return boost::apply_visitor(detail::lower_visitor(idx), *this); + return visit(detail::lower_visitor(idx), *this); } // this only works for axes with compatible bin type @@ -194,13 +224,13 @@ public: template bool operator==(const variant& u) const { - return boost::apply_visitor(detail::bicmp_visitor(), *this, u); + return visit(detail::equal_visitor(u), *this); } - template > + template bool operator==(const T& t) const { - // variant::operator==(T) is implemented only to fail, we cannot use it - auto tp = boost::get(this); + // boost::variant::operator==(T) implemented only to fail, cannot use it + auto tp = boost::relaxed_get(this); return tp && *tp == t; } @@ -209,22 +239,72 @@ public: return !operator==(t); } - template - explicit operator const T&() const { - return boost::strict_get(*this); - } - - template - explicit operator T&() { - return boost::strict_get(*this); - } - template void serialize(Archive&, unsigned); + + template + friend auto visit(Functor&& f, Variant&& v) + -> detail::visitor_return_type; + + template + friend T& get(variant& v); + + template + friend const T& get(const variant& v); + + template + friend T&& get(variant&& v); + + template + friend T* get(variant* v); + + template + friend const T* get(const variant* v); }; -// TODO add something like std::visit +template +auto visit(Functor&& f, Variant&& v) + -> detail::visitor_return_type +{ + using R = detail::visitor_return_type; + return boost::apply_visitor(detail::functor_wrapper(std::forward(f)), + static_cast< + detail::copy_qualifiers::base_type> + >(v)); +} +template +std::basic_ostream& operator<<(std::basic_ostream& os, const variant& v) +{ + visit(detail::ostream_visitor(os), v); + return os; +} + +template +T& get(variant& v) { + return boost::relaxed_get(v); +} + +template +const T& get(const variant& v) { + return boost::relaxed_get(v); +} + +template +T&& get(variant&& v) { + return boost::relaxed_get(v); +} + +template +T* get(variant* v) { + return boost::relaxed_get(v); +} + +template +const T* get(const variant* v) { + return boost::relaxed_get(v); +} } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index f03f611a..9440999a 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -15,8 +15,6 @@ #include #include #include -#include -#include #include #include #include @@ -43,7 +41,7 @@ struct axes_equal_static_dynamic_impl { template void operator()(N) const { using T = mp11::mp_at; - auto tp = boost::relaxed_get(&v[N::value]); + auto tp = axis::get(&v[N::value]); equal &= (tp && *tp == std::get(t)); } }; @@ -66,7 +64,7 @@ struct axes_assign_static_dynamic_impl { template void operator()(N) const { using T = mp11::mp_at; - std::get(t) = static_cast(v[N::value]); + std::get(t) = axis::get(v[N::value]); } }; @@ -156,42 +154,19 @@ void range_check(const std::tuple&) { static_assert(N < sizeof...(Ts), "index out of range"); } -namespace { -template -struct axis_at_impl {}; +template +using axis_at = mp_at_c::value * N>; -template -struct axis_at_impl> { - using type = mp11::mp_at_c, N>; -}; - -template -struct axis_at_impl> { - using type = T; -}; +template > +auto axis_get(T&& axes) -> decltype(std::get(std::forward(axes))) { + return std::get(std::forward(axes)); } -template -using axis_at = typename axis_at_impl::type; - -template -auto axis_get(std::tuple& axes) -> axis_at>& { - return std::get(axes); -} - -template -auto axis_get(const std::tuple& axes) -> const axis_at>& { - return std::get(axes); -} - -template -T& axis_get(std::vector& axes) { - return axes[N]; -} - -template -const T& axis_get(const std::vector& axes) { - return axes[N]; +template > +auto axis_get(T&& axes) -> decltype(std::forward(axes)[N]) { + return std::forward(axes)[N]; } template @@ -199,22 +174,10 @@ void for_each_axis(const std::tuple& axes, F&& f) { mp11::tuple_for_each(axes, std::forward(f)); } -namespace { -template -struct unary_adaptor : public boost::static_visitor { - Unary&& unary; - unary_adaptor(Unary&& u) : unary(std::forward(u)) {} - template - void operator()(const T& a) const { - unary(a); - } -}; -} - -template -void for_each_axis(const std::vector& axes, F&& f) { +template +void for_each_axis(const std::vector& axes, F&& f) { for (const auto& x : axes) { - boost::apply_visitor(unary_adaptor(std::forward(f)), x); + axis::visit(std::forward(f), x); } } @@ -381,7 +344,7 @@ void indices_to_index_iter(mp11::mp_size_t, optional_index& idx, const std::tuple& axes, Iterator iter) { constexpr auto D = mp11::mp_size_t() - N; const auto& a = std::get(axes); - const auto a_size = a.size(); + const auto a_size = static_cast(a.size()); const auto a_shape = extend(a); const auto j = static_cast(*iter); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid @@ -465,29 +428,33 @@ void args_to_index_get(mp11::mp_size_t, optional_index& idx, namespace { template -struct args_to_index_visitor : public boost::static_visitor { +struct args_to_index_visitor { optional_index& idx; const T& val; args_to_index_visitor(optional_index& i, const T& v) : idx(i), val(v) {} template void operator()(const Axis& a) const { - impl(std::is_convertible(), a); + using value_type = return_type; + impl(std::is_convertible(), a); } template void impl(std::true_type, const Axis& a) const { + using value_type = return_type; const auto a_size = a.size(); const auto a_shape = extend(a); - const auto j = a(static_cast(val)); + const auto j = a(static_cast(val)); linearize(idx, a_size, a_shape, j); } template void impl(std::false_type, const Axis&) const { + using value_type = return_type; throw std::invalid_argument(detail::cat( - "axis ", boost::typeindex::type_id().pretty_name(), ": argument ", - boost::typeindex::type_id().pretty_name(), " not convertible to value_type ", - boost::typeindex::type_id().pretty_name())); + boost::core::demangled_name( BOOST_CORE_TYPEID(Axis) ), + ": cannot convert argument of type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), " to ", + boost::core::demangled_name( BOOST_CORE_TYPEID(value_type) ))); } }; } @@ -498,7 +465,7 @@ void args_to_index(optional_index&, const std::vector&) {} template void args_to_index(optional_index& idx, const std::vector& axes, const U& u, const Us&... us) { - boost::apply_visitor(args_to_index_visitor(idx, u), axes[D]); + axis::visit(args_to_index_visitor(idx, u), axes[D]); args_to_index<(D + 1)>(idx, axes, us...); } @@ -507,7 +474,7 @@ void args_to_index_iter(optional_index& idx, const std::vector& axes, Iterator iter) { for (const auto& a : axes) { // iter could be a plain pointer, so we cannot use nested value_type here - boost::apply_visitor(args_to_index_visitor(idx, *iter++), a); + axis::visit(args_to_index_visitor(idx, *iter++), a); } } @@ -520,7 +487,7 @@ void args_to_index_get(mp11::mp_size_t, optional_index& idx, const std::vector& axes, const T& t) { constexpr std::size_t D = mp_size::value - N; using U = decltype(std::get(t)); - boost::apply_visitor(args_to_index_visitor(idx, std::get(t)), axes[D]); + axis::visit(args_to_index_visitor(idx, std::get(t)), axes[D]); args_to_index_get(mp11::mp_size_t<(N - 1)>(), idx, axes, t); } diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 7aedd14e..652dc437 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -22,6 +22,47 @@ namespace boost { namespace histogram { namespace detail { +template +using rm_cvref = typename std::remove_cv::type>::type; + +template +using mp_size = mp11::mp_size>; + +template +using mp_at_c = mp11::mp_at_c, N>; + +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_set_union = mp11::mp_apply_q, L>; + +template +using mp_last = mp11::mp_at_c::value - 1)>; + +template +using container_element_type = mp11::mp_first>; + +template +using unqualified_iterator_value_type = rm_cvref::value_type>; + +template +using return_type = typename boost::callable_traits::return_type::type; + +template +using args_type = boost::callable_traits::args_t; + +template +using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size>::value + N : N)>; + +template +using visitor_return_type = decltype(std::declval()(std::declval>>())); + #define BOOST_HISTOGRAM_MAKE_SFINAE(name, cond) \ template \ struct name##_impl { \ @@ -39,22 +80,57 @@ namespace detail { BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support, (std::declval().value(), std::declval().variance())); -BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, (std::declval().lower(0))); +BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, + (std::declval().lower(0))); BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, (static_cast(std::declval().options()))); -BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval().metadata())); +BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, + (std::declval().metadata())); -BOOST_HISTOGRAM_MAKE_SFINAE(is_dynamic_container, (std::begin(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_dynamic_container, + (std::declval()[0], std::declval().size())); -BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, (std::get<0>(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, + (std::get<0>(std::declval()))); -BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, (static_cast(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, + (static_cast(std::declval()))); BOOST_HISTOGRAM_MAKE_SFINAE(is_equal_comparable, (std::declval() == std::declval())); +BOOST_HISTOGRAM_MAKE_SFINAE(is_axis, + (std::declval().size(), &T::operator())); + +BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable, + (std::begin(std::declval()), + std::end(std::declval()))); + +BOOST_HISTOGRAM_MAKE_SFINAE(is_streamable, + (std::declval() << std::declval())); + +namespace { +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_axis_vector = mp11::mp_all< + is_dynamic_container, + mp11::mp_any< + is_axis_variant>>, + is_axis>> + > + >; + struct static_container_tag {}; struct dynamic_container_tag {}; struct no_container_tag {}; @@ -65,27 +141,6 @@ using classify_container = typename std::conditional< typename std::conditional::value, dynamic_container_tag, no_container_tag>::type>::type; -template ().size(), std::declval().increase(0), - std::declval()[0])> -struct requires_storage {}; - -template (), ++std::declval())> -struct requires_iterator {}; - -template ()))> -struct requires_iterable {}; - -template ()[0])> -struct requires_vector {}; - -template (std::declval()))> -struct requires_tuple {}; - -template ().size(), &T::operator())> -struct requires_axis {}; - namespace { struct bool_mask_impl { std::vector& b; @@ -104,43 +159,42 @@ std::vector bool_mask(unsigned n, bool v) { return b; } -template -using rm_cv_ref = typename std::remove_cv::type>::type; +// poor-mans concept checks +template ().size(), std::declval().increase(0), + std::declval()[0])> +struct requires_storage {}; -template -using mp_size = mp11::mp_size>; +template (), ++std::declval())> +struct requires_iterator {}; -template -using mp_at_c = mp11::mp_at_c, N>; +template , void>> +struct requires_iterable {}; -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 , void>> +struct requires_dynamic_container {}; -template -using mp_set_union = mp11::mp_apply_q, L>; +template , void>> +struct requires_static_container {}; -template -using mp_last = mp11::mp_at_c::value - 1)>; +template , void>> +struct requires_tuple {}; -template -using container_element_type = mp11::mp_first; +template , void>> +struct requires_axis {}; -template -using iterator_value_type = rm_cv_ref())>; +template ::value || + is_axis_variant::value), void>> +struct requires_axis_or_axis_variant {}; -template -using return_type = typename boost::callable_traits::return_type::type; - -template -using args_type = boost::callable_traits::args_t; - -template -using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size>::value + N : N)>; +template , + typename = mp11::mp_if_c<(is_dynamic_container::value && + (is_axis::value || + is_axis_variant::value)), void>> +struct requires_axis_vector {}; } // namespace detail } // namespace histogram diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index fb909a6d..894a4870 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -105,34 +105,36 @@ public: /// Get N-th axis (const version) template - auto axis(mp11::mp_size_t) const -> const detail::axis_at& { + auto axis(mp11::mp_size_t) const -> const detail::axis_at& { detail::range_check(axes_); return detail::axis_get(axes_); } /// Get N-th axis template - auto axis(mp11::mp_size_t) -> detail::axis_at& { + auto axis(mp11::mp_size_t) -> detail::axis_at& { detail::range_check(axes_); return detail::axis_get(axes_); } /// Get first axis (convenience for 1-d histograms, const version) - const detail::axis_at<0, axes_type>& axis() const { return axis(mp11::mp_size_t<0>()); } + auto axis() const -> const detail::axis_at& { return axis(mp11::mp_size_t<0>()); } /// Get first axis (convenience for 1-d histograms) - detail::axis_at<0, axes_type>& axis() { return axis(mp11::mp_size_t<0>()); } + auto axis() -> detail::axis_at& { return axis(mp11::mp_size_t<0>()); } /// Get N-th axis with runtime index (const version) - template > - const detail::axis_at<0, U>& axis(std::size_t i) const { + template > + auto axis(std::size_t i) const -> const detail::container_element_type& { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; } /// Get N-th axis with runtime index - template > - detail::axis_at<0, U>& axis(std::size_t i) { + template > + auto axis(std::size_t i) -> detail::container_element_type& { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; } @@ -217,7 +219,7 @@ public: /// Returns a lower-dimensional histogram // precondition: sequence must be strictly ascending axis indices template , + typename = detail::requires_dynamic_container, typename = detail::requires_iterator> histogram reduce_to(Iterator begin, Iterator end) const { BOOST_ASSERT_MSG(std::is_sorted(begin, end, std::less_equal()), @@ -259,12 +261,12 @@ private: /// static type factory with custom storage type template > histogram< - std::tuple, detail::rm_cv_ref...>, - detail::rm_cv_ref + std::tuple, detail::rm_cvref...>, + detail::rm_cvref > make_histogram_with(S&& s, T&& axis0, Ts&&... axis) { auto axes = std::make_tuple(std::forward(axis0), std::forward(axis)...); - return histogram>( + return histogram>( std::move(axes), std::forward(s) ); } @@ -282,18 +284,18 @@ auto make_histogram(T&& axis0, Ts&&... axis) } /// dynamic type factory from vector-like with custom storage type -template , - typename = detail::requires_axis>> -histogram, detail::rm_cv_ref> +template > +histogram, detail::rm_cvref> make_histogram_with(S&& s, T&& t) { - return histogram, detail::rm_cv_ref>( + return histogram, detail::rm_cvref>( std::forward(t), std::forward(s) ); } /// dynamic type factory from vector-like with standard storage type -template , - typename = detail::requires_axis>> +template > auto make_histogram(T&& t) -> decltype(make_histogram_with(default_storage(), std::forward(t))) { @@ -303,20 +305,21 @@ auto make_histogram(T&& t) /// dynamic type factory from iterator range with custom storage type template > -histogram>, - detail::rm_cv_ref> +histogram>, + detail::rm_cvref> make_histogram_with(Storage&& s, Iterator begin, Iterator end) { - auto axes = std::vector>(begin, end); + using T = detail::unqualified_iterator_value_type; + auto axes = std::vector(begin, end); return make_histogram_with(std::forward(s), std::move(axes)); } -// /// dynamic type factory from iterator range with standard storage type -// template > -// auto make_histogram(Iterator begin, Iterator end) -// -> decltype(make_histogram_with(default_storage(), begin, end)) -// { -// return make_histogram_with(default_storage(), begin, end); -// } +/// dynamic type factory from iterator range with standard storage type +template > +auto make_histogram(Iterator begin, Iterator end) + -> decltype(make_histogram_with(default_storage(), begin, end)) +{ + return make_histogram_with(default_storage(), begin, end); +} } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index a0fb4c29..4604b199 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -39,7 +39,7 @@ template , + typename Allocator = std::allocator, typename MetaData = boost::container::string> class variable; @@ -48,7 +48,7 @@ template , + typename Allocator = std::allocator, typename MetaData = boost::container::string> class category; diff --git a/test/axis_test.cpp b/test/axis_test.cpp index e8eda59d..902f9dc6 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,13 +39,13 @@ void test_axis_iterator(const Axis& a, int begin, int end) { int main() { // bad_ctors { - BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::logic_error); - BOOST_TEST_THROWS(axis::regular<>(1, 1, -1), std::logic_error); - BOOST_TEST_THROWS(axis::circular<>(0), std::logic_error); - BOOST_TEST_THROWS(axis::variable<>({}), std::logic_error); - BOOST_TEST_THROWS(axis::variable<>({1.0}), std::logic_error); - BOOST_TEST_THROWS(axis::integer<>(1, -1), std::logic_error); - BOOST_TEST_THROWS(axis::category<>({}), std::logic_error); + BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument); + // BOOST_TEST_THROWS(axis::regular<>(1, 1, -1), std::invalid_argument); + BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); + BOOST_TEST_THROWS(axis::variable<>(std::vector()), std::invalid_argument); + BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); + BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); + BOOST_TEST_THROWS(axis::category<>(std::vector()), std::invalid_argument); } // axis::regular @@ -66,41 +66,41 @@ int main() { BOOST_TEST_NOT(c == d); d = std::move(c); BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a.index(-10.), -1); - BOOST_TEST_EQ(a.index(-2.1), -1); - BOOST_TEST_EQ(a.index(-2.0), 0); - BOOST_TEST_EQ(a.index(-1.1), 0); - BOOST_TEST_EQ(a.index(0.0), 2); - BOOST_TEST_EQ(a.index(0.9), 2); - BOOST_TEST_EQ(a.index(1.0), 3); - BOOST_TEST_EQ(a.index(10.), 4); - BOOST_TEST_EQ(a.index(-std::numeric_limits::infinity()), -1); - BOOST_TEST_EQ(a.index(std::numeric_limits::infinity()), 4); - BOOST_TEST_EQ(a.index(std::numeric_limits::quiet_NaN()), 4); + BOOST_TEST_EQ(a(-10.), -1); + BOOST_TEST_EQ(a(-2.1), -1); + BOOST_TEST_EQ(a(-2.0), 0); + BOOST_TEST_EQ(a(-1.1), 0); + BOOST_TEST_EQ(a(0.0), 2); + BOOST_TEST_EQ(a(0.9), 2); + BOOST_TEST_EQ(a(1.0), 3); + BOOST_TEST_EQ(a(10.), 4); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); } // axis::regular with log transform { - axis::regular b{2, 1e0, 1e2}; + axis::regular> b{2, 1e0, 1e2}; BOOST_TEST_EQ(b[-1].lower(), 0.0); BOOST_TEST_IS_CLOSE(b[0].lower(), 1.0, 1e-9); BOOST_TEST_IS_CLOSE(b[1].lower(), 10.0, 1e-9); BOOST_TEST_IS_CLOSE(b[2].lower(), 100.0, 1e-9); BOOST_TEST_EQ(b[2].upper(), std::numeric_limits::infinity()); - BOOST_TEST_EQ(b.index(-1), 2); // produces NaN in conversion - BOOST_TEST_EQ(b.index(0), -1); - BOOST_TEST_EQ(b.index(1), 0); - BOOST_TEST_EQ(b.index(9), 0); - BOOST_TEST_EQ(b.index(10), 1); - BOOST_TEST_EQ(b.index(90), 1); - BOOST_TEST_EQ(b.index(100), 2); - BOOST_TEST_EQ(b.index(std::numeric_limits::infinity()), 2); + BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion + BOOST_TEST_EQ(b(0), -1); + BOOST_TEST_EQ(b(1), 0); + BOOST_TEST_EQ(b(9), 0); + BOOST_TEST_EQ(b(10), 1); + BOOST_TEST_EQ(b(90), 1); + BOOST_TEST_EQ(b(100), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); } // axis::regular with sqrt transform { - axis::regular b{2, 0, 4}; + axis::regular> b{2, 0, 4}; // this is weird: -inf * -inf = inf, thus the lower bound BOOST_TEST_EQ(b[-1].lower(), std::numeric_limits::infinity()); BOOST_TEST_IS_CLOSE(b[0].lower(), 0.0, 1e-9); @@ -108,20 +108,20 @@ int main() { BOOST_TEST_IS_CLOSE(b[2].lower(), 4.0, 1e-9); BOOST_TEST_EQ(b[2].upper(), std::numeric_limits::infinity()); - BOOST_TEST_EQ(b.index(-1), 2); // produces NaN in conversion - BOOST_TEST_EQ(b.index(0), 0); - BOOST_TEST_EQ(b.index(0.99), 0); - BOOST_TEST_EQ(b.index(1), 1); - BOOST_TEST_EQ(b.index(3.99), 1); - BOOST_TEST_EQ(b.index(4), 2); - BOOST_TEST_EQ(b.index(100), 2); - BOOST_TEST_EQ(b.index(std::numeric_limits::infinity()), 2); + BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion + BOOST_TEST_EQ(b(0), 0); + BOOST_TEST_EQ(b(0.99), 0); + BOOST_TEST_EQ(b(1), 1); + BOOST_TEST_EQ(b(3.99), 1); + BOOST_TEST_EQ(b(4), 2); + BOOST_TEST_EQ(b(100), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); } // axis::circular { - axis::circular<> a{4}; - BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - a.perimeter()); + axis::circular<> a{4, 0, 1}; + BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1); axis::circular<> b; BOOST_TEST_NOT(a == b); b = a; @@ -135,15 +135,15 @@ int main() { BOOST_TEST_NOT(c == d); d = std::move(c); BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a.index(-1.0 * a.perimeter()), 0); - BOOST_TEST_EQ(a.index(0.0), 0); - BOOST_TEST_EQ(a.index(0.25 * a.perimeter()), 1); - BOOST_TEST_EQ(a.index(0.5 * a.perimeter()), 2); - BOOST_TEST_EQ(a.index(0.75 * a.perimeter()), 3); - BOOST_TEST_EQ(a.index(1.00 * a.perimeter()), 0); - BOOST_TEST_EQ(a.index(std::numeric_limits::infinity()), 0); - BOOST_TEST_EQ(a.index(-std::numeric_limits::infinity()), 0); - BOOST_TEST_EQ(a.index(std::numeric_limits::quiet_NaN()), 0); + BOOST_TEST_EQ(a(-1.0 * 3), 0); + BOOST_TEST_EQ(a(0.0), 0); + BOOST_TEST_EQ(a(0.25), 1); + BOOST_TEST_EQ(a(0.5), 2); + BOOST_TEST_EQ(a(0.75), 3); + BOOST_TEST_EQ(a(1.0), 0); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); } // axis::variable @@ -166,20 +166,20 @@ int main() { BOOST_TEST_EQ(d, a); axis::variable<> e{-2, 0, 2}; BOOST_TEST_NOT(a == e); - BOOST_TEST_EQ(a.index(-10.), -1); - BOOST_TEST_EQ(a.index(-1.), 0); - BOOST_TEST_EQ(a.index(0.), 1); - BOOST_TEST_EQ(a.index(1.), 2); - BOOST_TEST_EQ(a.index(10.), 2); - BOOST_TEST_EQ(a.index(-std::numeric_limits::infinity()), -1); - BOOST_TEST_EQ(a.index(std::numeric_limits::infinity()), 2); - BOOST_TEST_EQ(a.index(std::numeric_limits::quiet_NaN()), 2); + BOOST_TEST_EQ(a(-10.), -1); + BOOST_TEST_EQ(a(-1.), 0); + BOOST_TEST_EQ(a(0.), 1); + BOOST_TEST_EQ(a(1.), 2); + BOOST_TEST_EQ(a(10.), 2); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 2); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 2); } // axis::integer { axis::integer<> a{-1, 2}; - BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::max()); + BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits::min()); BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::max()); axis::integer<> b; BOOST_TEST_NOT(a == b); @@ -194,21 +194,21 @@ int main() { BOOST_TEST_NOT(c == d); d = std::move(c); BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a.index(-10), -1); - BOOST_TEST_EQ(a.index(-2), -1); - BOOST_TEST_EQ(a.index(-1), 0); - BOOST_TEST_EQ(a.index(0), 1); - BOOST_TEST_EQ(a.index(1), 2); - BOOST_TEST_EQ(a.index(2), 3); - BOOST_TEST_EQ(a.index(10), 3); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-2), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(2), 3); + BOOST_TEST_EQ(a(10), 3); } // axis::category { std::string A("A"), B("B"), C("C"), other; - axis::category a{{A, B, C}}; + axis::category a({A, B, C}); axis::category b; - BOOST_TEST_NOT(a == b); + BOOST_TEST_NOT(a == b); b = a; BOOST_TEST_EQ(a, b); b = axis::category{{B, A, C}}; @@ -224,10 +224,10 @@ int main() { d = std::move(c); BOOST_TEST_EQ(d, a); BOOST_TEST_EQ(a.size(), 3); - BOOST_TEST_EQ(a.index(A), 0); - BOOST_TEST_EQ(a.index(B), 1); - BOOST_TEST_EQ(a.index(C), 2); - BOOST_TEST_EQ(a.index(other), 3); + BOOST_TEST_EQ(a(A), 0); + BOOST_TEST_EQ(a(B), 1); + BOOST_TEST_EQ(a(C), 2); + BOOST_TEST_EQ(a(other), 3); BOOST_TEST_EQ(a.value(0), A); BOOST_TEST_EQ(a.value(1), B); BOOST_TEST_EQ(a.value(2), C); @@ -237,44 +237,44 @@ int main() { // iterators { enum { A, B, C }; - test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::uoflow_type::off), 0, 5); - test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::uoflow_type::on), 0, 5); + test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5); + test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3); - test_axis_iterator(axis::any_std(axis::regular<>(5, 0, 1)), 0, 5); - BOOST_TEST_THROWS(axis::any_std(axis::category<>({A, B, C})).lower(0), - std::runtime_error); + test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); + // BOOST_TEST_THROWS(axis::variant>(axis::category<>({A, B, C}))[0].lower(), + // std::runtime_error); } // axis::any copyable { - axis::any_std a1(axis::regular<>(2, -1, 1)); - axis::any_std a2(a1); + axis::variant> a1(axis::regular<>(2, -1, 1)); + axis::variant> a2(a1); BOOST_TEST_EQ(a1, a2); - axis::any_std a3; + axis::variant> a3; BOOST_TEST_NE(a3, a1); a3 = a1; BOOST_TEST_EQ(a3, a1); - axis::any> a4(axis::regular<>(3, -2, 2)); - axis::any_std a5(a4); + axis::variant> a4(axis::regular<>(3, -2, 2)); + axis::variant, axis::integer<>> a5(a4); BOOST_TEST_EQ(a4, a5); - axis::any> a6; + axis::variant> a6; a6 = a1; BOOST_TEST_EQ(a6, a1); - axis::any, axis::integer<>> a7(axis::integer<>(0, 2)); - BOOST_TEST_THROWS(axis::any> a8(a7), std::invalid_argument); + axis::variant, axis::integer<>> a7(axis::integer<>(0, 2)); + BOOST_TEST_THROWS(axis::variant> a8(a7), std::invalid_argument); BOOST_TEST_THROWS(a4 = a7, std::invalid_argument); } // axis::any movable { - axis::any_std a(axis::regular<>(2, -1, 1)); - axis::any_std r(a); - axis::any_std b(std::move(a)); + axis::variant> a(axis::regular<>(2, -1, 1)); + axis::variant> r(a); + axis::variant> b(std::move(a)); BOOST_TEST_EQ(b, r); - axis::any_std c; + axis::variant> c; BOOST_TEST_NOT(a == c); c = std::move(b); BOOST_TEST(c == r); @@ -285,32 +285,41 @@ int main() { enum { A, B, C }; std::string a = "A"; std::string b = "B"; - std::vector axes; + std::vector, + axis::regular>, + axis::regular>, + axis::circular<>, + axis::variable<>, + axis::category<>, + axis::category, + axis::integer<> + >> axes; axes.push_back(axis::regular<>{2, -1, 1, "regular1"}); - axes.push_back(axis::regular(2, 1, 10, "regular2", - axis::uoflow_type::off)); - axes.push_back(axis::regular(2, 1, 10, "regular3", - axis::uoflow_type::on, 0.5)); - axes.push_back(axis::regular(2, 1, 10, "regular4", - axis::uoflow_type::off, -0.5)); + axes.push_back(axis::regular>(2, 1, 10, "regular2", + axis::option_type::none)); + axes.push_back(axis::regular>(2, 1, 10, "regular3", + axis::option_type::overflow, 0.5)); + axes.push_back(axis::regular>(2, 1, 10, "regular4", + axis::option_type::none, -0.5)); axes.push_back(axis::circular<>(4, 0.1, 1.0, "polar")); - axes.push_back(axis::variable<>({-1, 0, 1}, "variable", axis::uoflow_type::off)); + axes.push_back(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none)); axes.push_back(axis::category<>({A, B, C}, "category")); axes.push_back(axis::category({a, b}, "category2")); - axes.push_back(axis::integer<>(-1, 1, "integer", axis::uoflow_type::off)); + axes.push_back(axis::integer<>(-1, 1, "integer", axis::option_type::none)); std::ostringstream os; for (const auto& a : axes) { os << a << "\n"; } os << axes.back()[0]; const std::string ref = - "regular(2, -1, 1, label='regular1')\n" - "regular_log(2, 1, 10, label='regular2', uoflow=False)\n" - "regular_pow(2, 1, 10, 0.5, label='regular3')\n" - "regular_pow(2, 1, 10, -0.5, label='regular4', uoflow=False)\n" - "circular(4, phase=0.1, perimeter=1, label='polar')\n" - "variable(-1, 0, 1, label='variable', uoflow=False)\n" - "category(0, 1, 2, label='category')\n" - "category('A', 'B', label='category2')\n" - "integer(-1, 1, label='integer', uoflow=False)\n" + "regular(2, -1, 1, metadata='regular1', options=underflow_and_overflow)\n" + "regular_log(2, 1, 10, metadata='regular2', options=none)\n" + "regular_pow(2, 1, 10, metadata='regular3', options=overflow, power=0.5)\n" + "regular_pow(2, 1, 10, metadata='regular4', options=none, power=-0.5)\n" + "circular(4, 0.1, 1.1, metadata='polar', options=overflow)\n" + "variable(-1, 0, 1, metadata='variable', options=none)\n" + "category(0, 1, 2, metadata='category', options=overflow)\n" + "category('A', 'B', metadata='category2', options=overflow)\n" + "integer(-1, 1, metadata='integer', options=none)\n" "[-1, 0)"; BOOST_TEST_EQ(os.str(), ref); } @@ -318,47 +327,54 @@ int main() { // axis::any equal_comparable { enum { A, B, C }; - std::vector axes; + using variant = axis::variant< + axis::regular<>, + axis::regular>, + axis::circular<>, + axis::variable<>, + axis::category<>, + axis::integer<> + >; + std::vector axes; axes.push_back(axis::regular<>{2, -1, 1}); axes.push_back( - axis::regular(2, 1, 4, "", axis::uoflow_type::on, 0.5)); + axis::regular>(2, 1, 4, "", axis::option_type::underflow_and_overflow, 0.5)); axes.push_back(axis::circular<>{4}); axes.push_back(axis::variable<>{-1, 0, 1}); - axes.push_back(axis::category<>{A, B, C}); + axes.push_back(axis::category<>({A, B, C})); axes.push_back(axis::integer<>{-1, 1}); for (const auto& a : axes) { - BOOST_TEST(!(a == axis::any_std())); - BOOST_TEST_EQ(a, axis::any_std(a)); + BOOST_TEST(!(a == variant())); + BOOST_TEST_EQ(a, variant(a)); } - BOOST_TEST_NOT(axes == std::vector()); - BOOST_TEST(axes == std::vector(axes)); + BOOST_TEST_NOT(axes == std::vector()); + BOOST_TEST(axes == std::vector(axes)); } // axis::any value_to_index_failure { 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); - auto cx = static_cast&>(x); - BOOST_TEST_EQ(cx.index(b), 1); + axis::variant> x = axis::category({a, b}, "category"); + auto cx = axis::get>(x); + BOOST_TEST_EQ(cx(b), 1); } // sequence equality { enum { A, B, C }; std::vector< - axis::any, axis::variable<>, axis::category<>, axis::integer<>>> + axis::variant, axis::variable<>, axis::category<>, axis::integer<>>> std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, axis::category<>{A, B, C}}; - std::vector, axis::variable<>, axis::category<>>> + std::vector, axis::variable<>, axis::category<>>> std_vector2 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, axis::category<>{{A, B, C}}}; - std::vector, axis::variable<>>> std_vector3 = { + std::vector, axis::variable<>>> std_vector3 = { axis::variable<>{-1, 0, 1}, axis::regular<>{2, -1, 1}}; - std::vector, axis::regular<>>> std_vector4 = { + std::vector, axis::regular<>>> std_vector4 = { axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}}; BOOST_TEST(detail::axes_equal(std_vector1, std_vector2)); @@ -384,11 +400,11 @@ int main() { { enum { A, B, C, D }; std::vector< - axis::any, axis::variable<>, axis::category<>, axis::integer<>>> + axis::variant, axis::variable<>, axis::category<>, axis::integer<>>> std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, axis::category<>{A, B, C}}; - std::vector, axis::variable<>, axis::category<>>> + std::vector, axis::variable<>, axis::category<>>> std_vector2 = {axis::regular<>{2, -2, 2}, axis::variable<>{-2, 0, 2}, axis::category<>{A, B}}; @@ -447,25 +463,26 @@ int main() { // vector of axes with custom allocators { - using T1 = axis::regular>; - using T2 = axis::circular>; - using T3 = axis::variable>; - using T4 = axis::integer>; - using T5 = axis::category>; - using axis_type = axis::any; // no heap allocation + struct null {}; + using M = std::vector>; + using T1 = axis::regular, M>; + using T2 = axis::circular; + using T3 = axis::variable, null>; + using T4 = axis::integer; + using T5 = axis::category, null>; + using axis_type = axis::variant; // no heap allocation using axes_type = std::vector>; tracing_allocator_db db; { auto a = tracing_allocator(db); - const auto label = std::string(512, 'c'); axes_type axes(a); axes.reserve(5); - axes.emplace_back(T1(1, 0, 1, label, axis::uoflow_type::on, {}, a)); - axes.emplace_back(T2(2, 0, T2::two_pi(), label, a)); - axes.emplace_back(T3({0., 1., 2.}, label, axis::uoflow_type::on, a)); - axes.emplace_back(T4(0, 4, label, axis::uoflow_type::on, a)); - axes.emplace_back(T5({1, 2, 3, 4, 5}, label, axis::uoflow_type::on, a)); + axes.emplace_back(T1(1, 0, 1, M(3, 'c', a))); + axes.emplace_back(T2(2)); + axes.emplace_back(T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a)); + axes.emplace_back(T4(0, 4)); + axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); } // 5 axis::any objects BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second); @@ -473,7 +490,7 @@ int main() { // 5 labels BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); - BOOST_TEST_GE(db[typeid(char)].first, 5 * 512u); + BOOST_TEST_GE(db[typeid(char)].first, 3u); // nothing to allocate for T1 // nothing to allocate for T2 diff --git a/test/histogram_static_at_vector_wrong_dimension_fail.cpp b/test/histogram_static_at_vector_wrong_dimension_fail.cpp index c425e536..8ee5ae05 100644 --- a/test/histogram_static_at_vector_wrong_dimension_fail.cpp +++ b/test/histogram_static_at_vector_wrong_dimension_fail.cpp @@ -5,9 +5,9 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include - using namespace boost::histogram; + int main() { - auto h = make_static_histogram(axis::integer<>(0, 2)); + auto h = make_histogram(axis::integer<>(0, 2)); h.at(std::vector({0, 0})); } diff --git a/test/meta_test.cpp b/test/meta_test.cpp index 41e8abd0..480a43e3 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -8,17 +8,26 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include "utility.hpp" -using namespace boost::histogram::detail; -using namespace boost::histogram::literals; +namespace bh = boost::histogram; +using namespace bh::detail; +using namespace bh::literals; namespace mp11 = boost::mp11; +struct VisitorTestFunctor { + template + T operator()(T&&); +}; + int main() { // literals { @@ -34,36 +43,95 @@ int main() { // has_variance_support { - struct no_methods {}; + struct A {}; - struct value_method { + struct B { void value() {} }; - struct variance_method { + struct C { void variance() {} }; - struct value_and_variance_methods { + struct D { void value() {} void variance() {} }; - BOOST_TEST_TRAIT_FALSE((has_variance_support)); - BOOST_TEST_TRAIT_FALSE((has_variance_support)); - BOOST_TEST_TRAIT_FALSE((has_variance_support)); - BOOST_TEST_TRAIT_TRUE((has_variance_support)); + BOOST_TEST_TRAIT_FALSE((has_variance_support)); + BOOST_TEST_TRAIT_FALSE((has_variance_support)); + BOOST_TEST_TRAIT_FALSE((has_variance_support)); + BOOST_TEST_TRAIT_TRUE((has_variance_support)); } // has_method_lower { - struct no_methods {}; - struct lower_method { - void lower(int) {} - }; + struct A {}; + struct B { void lower(int) {} }; - BOOST_TEST_TRAIT_FALSE((has_method_lower)); - BOOST_TEST_TRAIT_TRUE((has_method_lower)); + BOOST_TEST_TRAIT_FALSE((has_method_lower)); + BOOST_TEST_TRAIT_TRUE((has_method_lower)); + } + + // has_method_options + { + struct NotOptions {}; + struct A {}; + struct B { NotOptions options(); }; + struct C { bh::axis::option_type options(); }; + + BOOST_TEST_TRAIT_FALSE((has_method_options)); + BOOST_TEST_TRAIT_FALSE((has_method_options)); + BOOST_TEST_TRAIT_TRUE((has_method_options)); + } + + // has_method_metadata + { + struct A {}; + struct B { void metadata(); }; + + BOOST_TEST_TRAIT_FALSE((has_method_metadata)); + BOOST_TEST_TRAIT_TRUE((has_method_metadata)); + } + + // is_equal_comparable + { + struct A {}; + struct B { bool operator==(const B&); }; + BOOST_TEST_TRAIT_TRUE((is_equal_comparable)); + BOOST_TEST_TRAIT_FALSE((is_equal_comparable)); + BOOST_TEST_TRAIT_TRUE((is_equal_comparable)); + } + + // is_axis + { + struct A {}; + struct B { int operator()(double); unsigned size() const; }; + struct C { int operator()(double); }; + struct D { unsigned size(); }; + BOOST_TEST_TRAIT_FALSE((is_axis)); + BOOST_TEST_TRAIT_TRUE((is_axis)); + BOOST_TEST_TRAIT_FALSE((is_axis)); + BOOST_TEST_TRAIT_FALSE((is_axis)); + } + + // is_iterable + { + using A = std::vector; + using B = int[3]; + using C = std::initializer_list; + BOOST_TEST_TRAIT_FALSE((is_iterable)); + BOOST_TEST_TRAIT_TRUE((is_iterable)); + BOOST_TEST_TRAIT_TRUE((is_iterable)); + BOOST_TEST_TRAIT_TRUE((is_iterable)); + } + + // is_axis_variant + { + struct A {}; + BOOST_TEST_TRAIT_FALSE((is_axis_variant)); + BOOST_TEST_TRAIT_TRUE((is_axis_variant>)); + BOOST_TEST_TRAIT_TRUE((is_axis_variant>>)); } // classify_container @@ -86,12 +154,8 @@ int main() { using result3a = classify_container&>; BOOST_TEST_TRAIT_TRUE((std::is_same)); - // (c-)strings are not regarded as dynamic containers - using result4a = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); - - using result4b = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using result4 = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); using result5 = classify_container; // has no std::end BOOST_TEST_TRAIT_TRUE((std::is_same)); @@ -106,7 +170,7 @@ int main() { BOOST_TEST_EQ(v2, std::vector({false, true, false, true})); } - // rm_cv_ref + // rm_cvref { using T1 = int; using T2 = int&&; @@ -116,14 +180,14 @@ int main() { using T6 = volatile int&&; using T7 = volatile const int; using T8 = volatile const int&; - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); } // mp_size @@ -156,5 +220,51 @@ int main() { BOOST_TEST_TRAIT_TRUE((std::is_same)); } + // mp_last + { + using L = mp11::mp_list; + BOOST_TEST_TRAIT_TRUE((std::is_same, long>)); + } + + // container_element_type + { + using T1 = std::vector; + using U1 = container_element_type; + using T2 = const std::vector&; + using U2 = container_element_type; + BOOST_TEST_TRAIT_TRUE((std::is_same)); + BOOST_TEST_TRAIT_TRUE((std::is_same)); + } + + // unqualified_iterator_value_type + { + using T1 = const char*; + using T2 = std::iterator; + BOOST_TEST_TRAIT_TRUE((std::is_same, char>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + } + + // visitor_return_type + { + using V1 = bh::axis::variant; + using V2 = bh::axis::variant&; + using V3 = const bh::axis::variant&; + BOOST_TEST_TRAIT_TRUE((std::is_same, char>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int&>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, const long&>)); + } + + // is_axis_vector + { + using A = std::vector>; + using B = std::vector>>; + using C = std::vector; + using D = bh::axis::regular<>; + BOOST_TEST_TRAIT_TRUE((is_axis_vector)); + BOOST_TEST_TRAIT_TRUE((is_axis_vector)); + BOOST_TEST_TRAIT_FALSE((is_axis_vector)); + BOOST_TEST_TRAIT_FALSE((is_axis_vector)); + } + return boost::report_errors(); } diff --git a/test/utility.hpp b/test/utility.hpp index 7acb5a55..a32981d1 100644 --- a/test/utility.hpp +++ b/test/utility.hpp @@ -60,31 +60,38 @@ typename Histogram::element_type sum(const Histogram& h) { return std::accumulate(h.begin(), h.end(), typename Histogram::element_type(0)); } +template +std::vector...>> +make_axis_vector(Ts&& ... ts) { + using T = axis::variant...>; + return std::vector({T(std::forward(ts))...}); +} + using static_tag = std::false_type; using dynamic_tag = std::true_type; template auto make(static_tag, Axes&&... axes) - -> decltype(make_static_histogram(std::forward(axes)...)) { - return make_static_histogram(std::forward(axes)...); + -> decltype(make_histogram(std::forward(axes)...)) { + return make_histogram(std::forward(axes)...); } template auto make_s(static_tag, S&& s, Axes&&... axes) - -> decltype(make_static_histogram_with(s, std::forward(axes)...)) { - return make_static_histogram_with(s, std::forward(axes)...); + -> decltype(make_histogram_with(s, std::forward(axes)...)) { + return make_histogram_with(s, std::forward(axes)...); } template auto make(dynamic_tag, Axes&&... axes) - -> decltype(make_dynamic_histogram...>>(std::forward(axes)...)) { - return make_dynamic_histogram...>>(std::forward(axes)...); + -> decltype(make_histogram(make_axis_vector(std::forward(axes)...))) { + return make_histogram(make_axis_vector(std::forward(axes)...)); } template auto make_s(dynamic_tag, S&& s, Axes&&... axes) - -> decltype(make_dynamic_histogram_with...>>(s, std::forward(axes)...)) { - return make_dynamic_histogram_with...>>(s, std::forward(axes)...); + -> decltype(make_histogram_with(s, make_axis_vector(std::forward(axes)...))) { + return make_histogram_with(s, make_axis_vector(std::forward(axes)...)); } using tracing_allocator_db = std::unordered_map< From c35c100654e52c7fccb789fb26af8c0c392dd9de Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sun, 21 Oct 2018 22:23:34 +0200 Subject: [PATCH 04/44] wip, axis_test passes --- CMakeLists.txt | 2 +- examples/getting_started_listing_02.cpp | 2 +- examples/guide_custom_modified_axis.cpp | 2 +- include/boost/histogram/axis/base.hpp | 8 +- .../histogram/axis/ostream_operators.hpp | 6 +- include/boost/histogram/axis/traits.hpp | 45 ++-- include/boost/histogram/axis/types.hpp | 126 +++++----- include/boost/histogram/axis/variant.hpp | 203 ++++++++-------- include/boost/histogram/detail/axes.hpp | 63 +++-- .../boost/histogram/detail/index_cache.hpp | 15 +- include/boost/histogram/detail/meta.hpp | 36 +-- include/boost/histogram/histogram.hpp | 6 +- include/boost/histogram/histogram_fwd.hpp | 4 +- include/boost/histogram/iterator.hpp | 2 +- include/boost/histogram/ostream_operators.hpp | 2 +- .../histogram/storage/weight_counter.hpp | 2 +- test/axis_test.cpp | 185 ++++++++++----- ...ynamic_fill_one_dimensional_tuple_fail.cpp | 3 +- ...namic_fill_one_dimensional_vector_fail.cpp | 3 +- test/histogram_static_add_fail.cpp | 4 +- test/histogram_test.cpp | 223 +++++++++--------- test/meta_test.cpp | 58 +++-- 22 files changed, 553 insertions(+), 447 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d05d7c5b..4ca96c16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required (VERSION 3.5) project(histogram CXX) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(MIN_BOOST_VERSION 1.66) diff --git a/examples/getting_started_listing_02.cpp b/examples/getting_started_listing_02.cpp index d5d020a6..ce95ee23 100644 --- a/examples/getting_started_listing_02.cpp +++ b/examples/getting_started_listing_02.cpp @@ -37,7 +37,7 @@ int main() { this is here the case for the category axis. */ using cas = bh::axis::category; - for (auto cbin : static_cast(h.axis(0))) { + for (auto cbin : bh::axis::get(h.axis(0))) { for (auto ybin : h.axis(2)) { // rows for (auto xbin : h.axis(1)) { // columns const auto v = h.at(cbin, xbin, ybin); diff --git a/examples/guide_custom_modified_axis.cpp b/examples/guide_custom_modified_axis.cpp index 60018e7c..b09c6ef1 100644 --- a/examples/guide_custom_modified_axis.cpp +++ b/examples/guide_custom_modified_axis.cpp @@ -26,7 +26,7 @@ int main() { for (auto xi : h.axis()) { std::cout << "bin " << xi.idx() << " [" << xi.lower() << ", " - << xi.upper() << ") " << h.at(xi).value() << std::endl; + << xi.upper() << ") " << h.at(xi) << std::endl; } /* prints: diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index c481f4b4..5190c822 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -47,8 +47,8 @@ class base } return *this; } - data(const metadata_type& m, int s, option_type o) - : metadata_type(m), size(s), opt(o) {} + data(metadata_type&& m, int s, option_type o) + : metadata_type(std::move(m)), size(s), opt(o) {} bool operator==(const data& rhs) const noexcept { return size == rhs.size && opt == rhs.opt && @@ -85,8 +85,8 @@ public: } protected: - base(unsigned size, const metadata_type& m, option_type opt) - : data_(m, size, opt) + base(unsigned size, metadata_type&& m, option_type opt) + : data_(std::move(m), size, opt) { if (size == 0) { throw std::invalid_argument("bins > 0 required"); } const auto max_index = static_cast(std::numeric_limits::max() diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index a8f36754..1ad43d28 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -23,8 +23,8 @@ namespace detail { template const char* to_string(const axis::transform::identity&) { return ""; } template const char* to_string(const axis::transform::log&) { return "_log"; } template const char* to_string(const axis::transform::sqrt&) { return "_sqrt"; } -template const char* to_string(const axis::transform::quantity&) { return "_quantity"; } template const char* to_string(const axis::transform::pow&) { return "_pow"; } +template const char* to_string(const axis::transform::quantity&) { return "_quantity"; } template void escape_string(OStream& os, const std::string& s) { @@ -68,8 +68,8 @@ void stream_transform(OStream& os, const axis::transform::pow& t) { os << ", power=" << t.power; } -template -void stream_transform(OStream& os, const axis::transform::quantity& t) { +template +void stream_transform(OStream& os, const axis::transform::quantity& t) { os << ", unit=" << t.unit; } diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index 9f281431..abd30b1b 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -9,39 +9,42 @@ #include #include -#include -#include namespace boost { namespace histogram { - namespace detail { -template -axis::option_type axis_traits_options_impl(std::true_type, const T& t) { - return t.options(); -} - -template -axis::option_type axis_traits_options_impl(std::false_type, const T&) { - return axis::option_type::none; -} + template + struct metadata_type_impl; + template + struct metadata_type_impl { + using type = detail::rm_cvref().metadata())>; + }; + template + struct metadata_type_impl { + using type = axis::missing_metadata_type; + }; } // namespace detail - namespace axis { +namespace traits { + template + using args = detail::args_type; -template -struct traits { - using args = boost::callable_traits::args_t; + template + using metadata_type = typename detail::metadata_type_impl, T>::type; - static option_type options(const T& t) { - return detail::axis_traits_options_impl(detail::has_method_options(), t); + template + option_type options(const T& t) { + return detail::overload( + [](std::true_type, const auto& x) { return x.options(); }, + [](std::false_type, const auto&) { return axis::option_type::none; } + )(detail::has_method_options(), t); } - static unsigned extend(const T& t) { + template + unsigned extend(const T& t) { return t.size() + static_cast(options(t)); } -}; - +} // namespace traits } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index c0ddb3d6..e224cec0 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -50,19 +50,6 @@ struct sqrt : public identity { static T inverse(T x) { return x * x; } }; -template -struct quantity { - T unit; - using U = decltype(std::declval() / std::declval()); - - U forward(T x) const { return x / unit; } - T inverse(U x) const { return x * unit; } - - bool operator==(const quantity& o) const noexcept { return unit == o.unit; } - template - void serialize(Archive&, unsigned); -}; - template struct pow { T power = 1.0; @@ -77,6 +64,22 @@ struct pow { template void serialize(Archive&, unsigned); }; + +template +struct quantity { + Unit unit; + + quantity(const Unit& u) : unit(u) {} + + using Dimensionless = decltype(std::declval() / std::declval()); + + Dimensionless forward(Quantity x) const { return x / unit; } + Quantity inverse(Dimensionless x) const { return x * unit; } + + bool operator==(const quantity& o) const noexcept { return unit == o.unit; } + template + void serialize(Archive&, unsigned); +}; } // namespace transform /** Axis for equidistant intervals on the real line. @@ -120,18 +123,18 @@ public: /** Construct axis with n bins over real range [begin, end). * * \param n number of bins. - * \param begin low edge of first bin. - * \param end high edge of last bin. + * \param start low edge of first bin. + * \param stop high edge of last bin. * \param metadata description of the axis. * \param options extra bin options. * \param trans transform instance to use. */ - regular(unsigned n, value_type begin, value_type end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const transform_type& trans = transform_type()) - : base_type(n, m, o) - , data_(trans, n, begin, end) + regular(unsigned n, value_type start, value_type stop, + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + transform_type trans = transform_type()) + : base_type(n, std::move(m), o) + , data_(std::move(trans), n, start, stop) { if (!std::isfinite(data_.min) || !std::isfinite(data_.delta)) throw std::invalid_argument("forward transform of lower or upper invalid"); @@ -159,11 +162,11 @@ public: /// Returns lower edge of bin. value_type lower(int idx) const noexcept { const auto z = internal_type(idx) / base_type::size(); - value_type x; + internal_type x; if (z < 0) - x = -std::numeric_limits::infinity(); + x = -std::numeric_limits::infinity(); else if (z > 1) - x = std::numeric_limits::infinity(); + x = std::numeric_limits::infinity(); else { x = (1 - z) * data_.min + z * (data_.min + data_.delta * base_type::size()); } @@ -212,9 +215,9 @@ public: */ explicit circular(unsigned n, value_type phase = 0.0, value_type perimeter = two_pi(), - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow) - : base_type(n, m, o == option_type::underflow_and_overflow ? + metadata_type m = metadata_type(), + option_type o = option_type::overflow) + : base_type(n, std::move(m), o == option_type::underflow_and_overflow ? option_type::overflow : o) , phase_(phase), delta_(perimeter / n) { @@ -295,11 +298,11 @@ public: template > variable(Iterator begin, Iterator end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const allocator_type& a = allocator_type()) - : base_type(begin == end ? 0 : std::distance(begin, end) - 1, m, o) - , data_(a) + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o) + , data_(std::move(a)) { using AT = std::allocator_traits; data_.x = AT::allocate(data_, nx()); @@ -335,18 +338,18 @@ public: * \param allocator allocator instance to use. */ template > - variable(T iterable, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const allocator_type& a = allocator_type()) - : variable(std::begin(iterable), std::end(iterable), m, o, a) {} + variable(const T& t, + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : variable(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} template - variable(const std::initializer_list& t, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const allocator_type& a = allocator_type()) - : variable(t.begin(), t.end(), m, o, a) {} + variable(std::initializer_list t, + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : variable(t.begin(), t.end(), std::move(m), o, std::move(a)) {} variable() = default; @@ -438,9 +441,9 @@ public: * \param options extra bin options. */ integer(value_type begin, value_type end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow) - : base_type(end - begin, m, o), min_(begin) { + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow) + : base_type(end - begin, std::move(m), o), min_(begin) { if (begin >= end) { throw std::invalid_argument("begin < end required"); } } @@ -520,11 +523,11 @@ public: template > category(Iterator begin, Iterator end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow, - const allocator_type& a = allocator_type()) - : base_type(std::distance(begin, end), m, o) - , data_(a) + metadata_type m = metadata_type(), + option_type o = option_type::overflow, + allocator_type a = allocator_type()) + : base_type(std::distance(begin, end), std::move(m), o) + , data_(std::move(a)) { data_.x = detail::create_buffer_from_iter(data_, base_type::size(), begin); } @@ -534,19 +537,20 @@ public: * \param seq sequence of unique values. * \param metadata description of the axis. */ - template > - category(T t, - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow, - const allocator_type& a = allocator_type()) - : category(std::begin(t), std::end(t), m, o, a) {} + template > + category(const T& t, + metadata_type m = metadata_type(), + option_type o = option_type::overflow, + allocator_type a = allocator_type()) + : category(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} template - category(const std::initializer_list& t, - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow, - const allocator_type& a = allocator_type()) - : category(t.begin(), t.end(), m, o, a) {} + category(std::initializer_list t, + metadata_type m = metadata_type(), + option_type o = option_type::overflow, + allocator_type a = allocator_type()) + : category(t.begin(), t.end(), std::move(m), o, std::move(a)) {} category() = default; diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index b85653a1..550839f0 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -32,7 +32,7 @@ struct call_visitor { call_visitor(const double arg) : x(arg) {} template int operator()(const T& t) const { - using args = typename axis::traits::args; + using args = axis::traits::args; return impl(mp11::mp_bool< (mp11::mp_size::value == 1 && std::is_convertible>::value) @@ -40,12 +40,11 @@ struct call_visitor { } template int impl(std::true_type, const T& a) const { - using U = mp11::mp_first::args>; - return a(static_cast(x)); + return a(x); } template int impl(std::false_type, const T&) const { - using args = typename axis::traits::args; + using args = axis::traits::args; throw std::invalid_argument(detail::cat( "cannot convert double to ", boost::core::demangled_name( BOOST_CORE_TYPEID(args) ), @@ -55,97 +54,10 @@ struct call_visitor { } }; -struct size_visitor { - template - unsigned operator()(const T& a) const { - return a.size(); - } -}; - -struct options_visitor { - template - axis::option_type operator()(const T& t) const { - return axis::traits::options(t); - } -}; - -struct lower_visitor { - int idx; - lower_visitor(int i) : idx(i) {} - template - double operator()(const T& a) const { - return impl(detail::has_method_lower(), a); - } - template - double impl(std::true_type, const T& a) const { - return a.lower(idx); - } - template - double impl(std::false_type, const T&) const { - throw std::runtime_error(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " has no lower method")); - } -}; - -template -struct assign_visitor { - T& t; - assign_visitor(T& x) : t(x) {} - template - void operator()(const U& u) const { - impl(mp11::mp_contains, U>(), u); - } - - template - void impl(std::true_type, const U& u) const { - t = u; - } - - template - void impl(std::false_type, const U&) const { - throw std::invalid_argument(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), - " is not a bounded type of ", - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ) - )); - } -}; - -template -struct equal_visitor { - const T& t; - equal_visitor(const T& x) : t(x) {} - template - bool operator()(const U& u) const { - return t == u; - } -}; - -template -struct ostream_visitor { - OStream& os; - ostream_visitor(OStream& o) : os(o) {} - template - void operator()(const T& t) const { - impl(is_streamable(), t); - } - template - void impl(std::true_type, const T& t) const { - os << t; - } - template - void impl(std::false_type, const T&) const { - throw std::invalid_argument(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " is not streamable")); - } -}; - template struct functor_wrapper : public boost::static_visitor { - F fcn; - functor_wrapper(F f) : fcn(std::forward(f)) {} + F& fcn; + functor_wrapper(F& f) : fcn(f) {} template R operator()(T&& t) const { @@ -165,14 +77,19 @@ class variant { using base_type = boost::variant; using bin_type = interval_view; + using first_bounded_type = mp11::mp_first; + using metadata_type = traits::metadata_type; template using requires_bounded_type = - mp11::mp_if>, void>; + mp11::mp_if>, void>; public: variant() = default; + variant(const variant&) = default; + variant& operator=(const variant&) = default; + variant(variant&&) = default; + variant& operator=(variant&&) = default; template > variant(T&& t) : base_type(std::forward(t)) {} @@ -185,21 +102,57 @@ public: template variant(const variant& u) { - visit(detail::assign_visitor(*this), u); + this->operator=(u); } template variant& operator=(const variant& u) { - visit(detail::assign_visitor(*this), u); + visit([this](const auto& u) { + using U = detail::rm_cvref; + detail::overload( + [this](std::true_type, const auto& u) { this->operator=(u); }, + [](std::false_type, const auto&) { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), + " is not a bounded type of ", + boost::core::demangled_name( BOOST_CORE_TYPEID(variant) ) + )); + } + )(mp11::mp_contains(), u); + }, u); return *this; } unsigned size() const { - return visit(detail::size_visitor(), *this); + return visit([](const auto& x) { return x.size(); }, *this); } option_type options() const { - return visit(detail::options_visitor(), *this); + return visit([](const auto& x) { return axis::traits::options(x); }, *this); + } + + const metadata_type& metadata() const { + return visit([](const auto& x) { + using T = detail::rm_cvref; + using U = traits::metadata_type; + return detail::overload( + [](std::true_type, const auto& x) { return x.metadata(); }, + [](std::false_type, const auto&) -> const metadata_type& { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " cannot return metadata of type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), + " through variant interface which uses type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type) ) + )); + } + )(std::is_same(), x); + }, *this); + } + + metadata_type& metadata() { + // return visit([](auto& x) { return axis::traits::metadata(x); }, *this); + return metadata_type(); } // Only works for axes with compatible call signature @@ -211,7 +164,17 @@ public: // Only works for axes with a lower method // and will throw a runtime_error otherwise double lower(int idx) const { - return visit(detail::lower_visitor(idx), *this); + return visit([idx](const auto& x) { + using T = detail::rm_cvref; + return detail::overload( + [idx](std::true_type, const auto& x) -> double { return x.lower(idx); }, + [](std::false_type, const auto&) -> double { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " has no lower method")); + } + )(detail::has_method_lower(), x); + }, *this); } // this only works for axes with compatible bin type @@ -224,7 +187,7 @@ public: template bool operator==(const variant& u) const { - return visit(detail::equal_visitor(u), *this); + return visit([&u](const auto& x) { return u == x; }, *this); } template @@ -267,7 +230,7 @@ auto visit(Functor&& f, Variant&& v) -> detail::visitor_return_type { using R = detail::visitor_return_type; - return boost::apply_visitor(detail::functor_wrapper(std::forward(f)), + return boost::apply_visitor(detail::functor_wrapper(f), static_cast< detail::copy_qualifiers::base_type> @@ -277,34 +240,58 @@ auto visit(Functor&& f, Variant&& v) template std::basic_ostream& operator<<(std::basic_ostream& os, const variant& v) { - visit(detail::ostream_visitor(os), v); + visit([&os](const auto& x) { + using T = detail::rm_cvref; + detail::overload( + [&os](std::true_type, const auto& x) { os << x; }, + [](std::false_type, const auto&) { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " is not streamable")); + } + )(detail::is_streamable(), x); + }, v); return os; } template T& get(variant& v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type&>(v)); } template const T& get(const variant& v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type&>(v)); } template T&& get(variant&& v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type&&>(v)); } template T* get(variant* v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type*>(v)); } template const T* get(const variant* v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type*>(v)); } + +// pass-through if T is an axis instead of a variant +template >, + typename = mp11::mp_if< + std::is_same< + detail::rm_cvref, + detail::rm_cvref + >, void> + > +U get(U&& u) { + return std::forward(u); +} + } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index 9440999a..f00567cd 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -26,11 +26,6 @@ namespace detail { namespace { -template -unsigned extend(const T& t) { - return axis::traits::extend(t); -} - template struct axes_equal_static_dynamic_impl { bool& equal; @@ -164,7 +159,7 @@ auto axis_get(T&& axes) -> decltype(std::get(std::forward(axes))) { } template > + typename = requires_axis_vector> auto axis_get(T&& axes) -> decltype(std::forward(axes)[N]) { return std::forward(axes)[N]; } @@ -186,7 +181,7 @@ struct field_counter { std::size_t value = 1; template void operator()(const T& t) { - value *= extend(t); + value *= axis::traits::extend(t); } }; } @@ -226,7 +221,7 @@ struct shape_collector { shape_collector(std::vector::iterator i) : iter(i) {} template void operator()(const T& t) { - *iter++ = extend(t); + *iter++ = axis::traits::extend(t); } }; @@ -329,7 +324,7 @@ void indices_to_index(optional_index& idx, const Axes& axes, const int j, const Us... us) { const auto& a = axis_get(axes); const auto a_size = static_cast(a.size()); - const auto a_shape = static_cast(extend(a)); + const auto a_shape = static_cast(axis::traits::extend(a)); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); indices_to_index<(D + 1)>(idx, axes, us...); @@ -345,7 +340,7 @@ void indices_to_index_iter(mp11::mp_size_t, optional_index& idx, constexpr auto D = mp11::mp_size_t() - N; const auto& a = std::get(axes); const auto a_size = static_cast(a.size()); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = static_cast(*iter); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -357,7 +352,7 @@ void indices_to_index_iter(optional_index& idx, const std::vector& axes, Iterator iter) { for (const auto& a : axes) { const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = static_cast(*iter++); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -372,8 +367,8 @@ void indices_to_index_get(mp11::mp_size_t, optional_index& idx, const Axes& a const T& t) { constexpr std::size_t D = mp_size() - N; const auto& a = axis_get(axes); - const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_size = static_cast(a.size()); + const auto a_shape = axis::traits::extend(a); const auto j = static_cast(std::get(t)); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -388,7 +383,7 @@ void args_to_index(optional_index& idx, const std::tuple& axes, const U& const Us&... us) { const auto& a = std::get(axes); const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = a(u); linearize(idx, a_size, a_shape, j); args_to_index<(D + 1)>(idx, axes, us...); @@ -404,7 +399,7 @@ void args_to_index_iter(mp11::mp_size_t, optional_index& idx, constexpr std::size_t D = sizeof...(Ts)-N; const auto& a = axis_get(axes); const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = a(*iter); linearize(idx, a_size, a_shape, j); args_to_index_iter(mp11::mp_size_t<(N - 1)>(), idx, axes, ++iter); @@ -420,7 +415,7 @@ void args_to_index_get(mp11::mp_size_t, optional_index& idx, constexpr std::size_t D = mp_size::value - N; const auto& a = std::get(axes); const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = a(std::get(t)); linearize(idx, a_size, a_shape, j); args_to_index_get(mp11::mp_size_t<(N - 1)>(), idx, axes, t); @@ -432,29 +427,29 @@ struct args_to_index_visitor { optional_index& idx; const T& val; args_to_index_visitor(optional_index& i, const T& v) : idx(i), val(v) {} - template - void operator()(const Axis& a) const { - using value_type = return_type; - impl(std::is_convertible(), a); + template + void operator()(const U& a) const { + using arg_type = mp11::mp_first>; + impl(std::is_convertible(), a); } - template - void impl(std::true_type, const Axis& a) const { - using value_type = return_type; + template + void impl(std::true_type, const U& a) const { + using arg_type = mp11::mp_first>; const auto a_size = a.size(); - const auto a_shape = extend(a); - const auto j = a(static_cast(val)); + const auto a_shape = axis::traits::extend(a); + const auto j = a(static_cast(val)); linearize(idx, a_size, a_shape, j); } - template - void impl(std::false_type, const Axis&) const { - using value_type = return_type; + template + void impl(std::false_type, const U&) const { + using arg_type = mp11::mp_first>; throw std::invalid_argument(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(Axis) ), + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), ": cannot convert argument of type ", boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), " to ", - boost::core::demangled_name( BOOST_CORE_TYPEID(value_type) ))); + boost::core::demangled_name( BOOST_CORE_TYPEID(arg_type) ))); } }; } @@ -519,7 +514,7 @@ optional_index call_impl(static_container_tag, const std::tuple& } template -optional_index call_impl(dynamic_container_tag, const std::tuple& axes, +optional_index call_impl(iterable_container_tag, const std::tuple& axes, const U& u) { dimension_check(axes, u.size()); optional_index i; @@ -548,7 +543,7 @@ optional_index call_impl(static_container_tag, const std::vector& axes, } template -optional_index call_impl(dynamic_container_tag, const std::vector& axes, +optional_index call_impl(iterable_container_tag, const std::vector& axes, const U& u) { if (axes.size() == 1) // do not unpack for 1d histograms, it is ambiguous return call_impl(no_container_tag(), axes, u); @@ -583,7 +578,7 @@ std::size_t at_impl(detail::static_container_tag, const A& axes, const U& u) { } template -std::size_t at_impl(detail::dynamic_container_tag, const std::tuple& axes, +std::size_t at_impl(detail::iterable_container_tag, const std::tuple& axes, const U& u) { dimension_check(axes, std::distance(std::begin(u), std::end(u))); auto index = detail::optional_index(); @@ -594,7 +589,7 @@ std::size_t at_impl(detail::dynamic_container_tag, const std::tuple& axes } template -std::size_t at_impl(detail::dynamic_container_tag, const std::vector& axes, +std::size_t at_impl(detail::iterable_container_tag, const std::vector& axes, const U& u) { dimension_check(axes, std::distance(std::begin(u), std::end(u))); auto index = detail::optional_index(); diff --git a/include/boost/histogram/detail/index_cache.hpp b/include/boost/histogram/detail/index_cache.hpp index af3cd889..25884ace 100644 --- a/include/boost/histogram/detail/index_cache.hpp +++ b/include/boost/histogram/detail/index_cache.hpp @@ -9,6 +9,7 @@ #include #include +#include namespace boost { namespace histogram { @@ -35,19 +36,19 @@ struct index_cache : public std::unique_ptr { struct dim_visitor { mutable std::size_t stride; mutable block_t* b; - template - void operator()(const Axis& a) const noexcept { - b->dim = dim_t{0, a.size(), stride}; + template + void operator()(const T& a) const noexcept { + b->dim = dim_t{0, static_cast(a.size()), stride}; ++b; - stride *= a.shape(); + stride *= axis::traits::extend(a); } }; template void set(const H& h) { - if (!(*this) || h.dim() != ptr_t::get()->state.dim) { - ptr_t::reset(new block_t[h.dim() + 1]); - ptr_t::get()->state.dim = h.dim(); + if (!(*this) || h.rank() != ptr_t::get()->state.dim) { + ptr_t::reset(new block_t[h.rank() + 1]); + ptr_t::get()->state.dim = h.rank(); ptr_t::get()->state.idx = 0; } h.for_each_axis(dim_visitor{1, ptr_t::get() + 1}); diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 652dc437..aea73067 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -55,7 +55,11 @@ template using return_type = typename boost::callable_traits::return_type::type; template -using args_type = boost::callable_traits::args_t; +using args_type = mp11::mp_if< + std::is_member_function_pointer, + mp11::mp_pop_front>, + boost::callable_traits::args_t +>; template using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size>::value + N : N)>; @@ -63,6 +67,16 @@ using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size using visitor_return_type = decltype(std::declval()(std::declval>>())); +template +struct overload_type : T1, T2 { + overload_type(T1 t1, T2 t2) : T1(t1), T2(t2) {} + using T1::operator(); + using T2::operator(); +}; + +template +overload_type overload(T1 t1, T2 t2) { return overload_type(t1, t2); } + #define BOOST_HISTOGRAM_MAKE_SFINAE(name, cond) \ template \ struct name##_impl { \ @@ -89,7 +103,7 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval().metadata())); -BOOST_HISTOGRAM_MAKE_SFINAE(is_dynamic_container, +BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container, (std::declval()[0], std::declval().size())); BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, @@ -124,7 +138,7 @@ using is_axis_variant = typename is_axis_variant_impl::type; template using is_axis_vector = mp11::mp_all< - is_dynamic_container, + is_random_access_container, mp11::mp_any< is_axis_variant>>, is_axis>> @@ -132,14 +146,14 @@ using is_axis_vector = mp11::mp_all< >; struct static_container_tag {}; -struct dynamic_container_tag {}; +struct iterable_container_tag {}; struct no_container_tag {}; template using classify_container = typename std::conditional< - is_static_container::value, static_container_tag, - typename std::conditional::value, - dynamic_container_tag, no_container_tag>::type>::type; + is_iterable::value, iterable_container_tag, + typename std::conditional::value, + static_container_tag, no_container_tag>::type>::type; namespace { struct bool_mask_impl { @@ -171,15 +185,9 @@ struct requires_iterator {}; template , void>> struct requires_iterable {}; -template , void>> -struct requires_dynamic_container {}; - template , void>> struct requires_static_container {}; -template , void>> -struct requires_tuple {}; - template , void>> struct requires_axis {}; @@ -191,7 +199,7 @@ struct requires_axis_or_axis_variant {}; template , - typename = mp11::mp_if_c<(is_dynamic_container::value && + typename = mp11::mp_if_c<(is_random_access_container::value && (is_axis::value || is_axis_variant::value)), void>> struct requires_axis_vector {}; diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index 894a4870..c97b22ac 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -125,7 +125,7 @@ public: /// Get N-th axis with runtime index (const version) template > + typename = detail::requires_axis_vector> auto axis(std::size_t i) const -> const detail::container_element_type& { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; @@ -133,7 +133,7 @@ public: /// Get N-th axis with runtime index template > + typename = detail::requires_axis_vector> auto axis(std::size_t i) -> detail::container_element_type& { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; @@ -219,7 +219,7 @@ public: /// Returns a lower-dimensional histogram // precondition: sequence must be strictly ascending axis indices template , + typename = detail::requires_axis_vector, typename = detail::requires_iterator> histogram reduce_to(Iterator begin, Iterator end) const { BOOST_ASSERT_MSG(std::is_sorted(begin, end, std::less_equal()), diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 4604b199..7734aee1 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -16,6 +16,8 @@ namespace histogram { namespace axis { +struct missing_metadata_type {}; + enum class option_type { none = 0, overflow = 1, @@ -26,8 +28,8 @@ namespace transform { template struct identity; template struct log; template struct sqrt; -template struct quantity; template struct pow; +template struct quantity; } // namespace transform template , diff --git a/include/boost/histogram/iterator.hpp b/include/boost/histogram/iterator.hpp index 73696f8b..18905676 100644 --- a/include/boost/histogram/iterator.hpp +++ b/include/boost/histogram/iterator.hpp @@ -32,7 +32,7 @@ public: cache_.reset(); } - std::size_t dim() const noexcept { return histogram_.dim(); } + std::size_t rank() const noexcept { return histogram_.rank(); } int idx(std::size_t dim = 0) const noexcept { if (!cache_) { cache_.set(histogram_); } diff --git a/include/boost/histogram/ostream_operators.hpp b/include/boost/histogram/ostream_operators.hpp index 734728df..a42de867 100644 --- a/include/boost/histogram/ostream_operators.hpp +++ b/include/boost/histogram/ostream_operators.hpp @@ -33,7 +33,7 @@ std::basic_ostream& operator<<(std::basic_ostream& using OS = std::basic_ostream; os << "histogram("; h.for_each_axis(detail::axis_ostream_visitor(os)); - os << (h.dim() ? "\n)" : ")"); + os << (h.rank() ? "\n)" : ")"); return os; } diff --git a/include/boost/histogram/storage/weight_counter.hpp b/include/boost/histogram/storage/weight_counter.hpp index b0b86f36..e3bd083f 100644 --- a/include/boost/histogram/storage/weight_counter.hpp +++ b/include/boost/histogram/storage/weight_counter.hpp @@ -19,7 +19,7 @@ class access; namespace histogram { /// Double counter which holds a sum of weights and a sum of squared weights -template +template class weight_counter { public: /// Beware: For performance reasons counters are not initialized diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 902f9dc6..52b40c85 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include "utility.hpp" using namespace boost::histogram; @@ -36,6 +38,33 @@ void test_axis_iterator(const Axis& a, int begin, int end) { } } +// quantity with unit for testing +template +struct quantity { + double value; +}; + +struct length { + double value; +}; + +auto meter = length{1.0}; + +template +double operator/(const quantity& a, const Unit& u) { + return a.value / u.value; +} + +template +quantity operator*(double x, const Unit& u) { + return quantity{x * u.value}; +} + +template +quantity operator-(const quantity& a, const quantity& b) { + return quantity{a.value - b.value}; +} + int main() { // bad_ctors { @@ -79,6 +108,19 @@ int main() { BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); } + // regular axis with inverted range + { + axis::regular<> a(2, 1, -1); + BOOST_TEST_EQ(a[0].lower(), 1); + BOOST_TEST_EQ(a[1].lower(), 0); + BOOST_TEST_EQ(a[2].lower(), -1); + BOOST_TEST_EQ(a(2), -1); + BOOST_TEST_EQ(a(1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(-1), 2); + BOOST_TEST_EQ(a(-2), 2); + } + // axis::regular with log transform { axis::regular> b{2, 1e0, 1e2}; @@ -118,6 +160,27 @@ int main() { BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); } + // axis::regular with quantity transform + { + axis::regular, length>> b{ + 2, 0*meter, 2*meter, {}, axis::option_type::underflow_and_overflow, meter + }; + BOOST_TEST_EQ(b[-1].lower()/meter, -std::numeric_limits::infinity()); + BOOST_TEST_IS_CLOSE(b[0].lower()/meter, 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[1].lower()/meter, 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[2].lower()/meter, 2.0, 1e-9); + BOOST_TEST_EQ(b[2].upper()/meter, std::numeric_limits::infinity()); + + BOOST_TEST_EQ(b(-1*meter), -1); // produces NaN in conversion + BOOST_TEST_EQ(b(0*meter), 0); + BOOST_TEST_EQ(b(0.99*meter), 0); + BOOST_TEST_EQ(b(1*meter), 1); + BOOST_TEST_EQ(b(1.99*meter), 1); + BOOST_TEST_EQ(b(2*meter), 2); + BOOST_TEST_EQ(b(100*meter), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity()*meter), 2); + } + // axis::circular { axis::circular<> a{4, 0, 1}; @@ -248,7 +311,7 @@ int main() { // std::runtime_error); } - // axis::any copyable + // axis::variant copyable { axis::variant> a1(axis::regular<>(2, -1, 1)); axis::variant> a2(a1); @@ -264,11 +327,11 @@ int main() { a6 = a1; BOOST_TEST_EQ(a6, a1); axis::variant, axis::integer<>> a7(axis::integer<>(0, 2)); - BOOST_TEST_THROWS(axis::variant> a8(a7), std::invalid_argument); - BOOST_TEST_THROWS(a4 = a7, std::invalid_argument); + BOOST_TEST_THROWS(axis::variant> a8(a7), std::runtime_error); + BOOST_TEST_THROWS(a4 = a7, std::runtime_error); } - // axis::any movable + // axis::variant movable { axis::variant> a(axis::regular<>(2, -1, 1)); axis::variant> r(a); @@ -280,51 +343,68 @@ int main() { BOOST_TEST(c == r); } - // axis::any streamable + // axis::variant streamable { - enum { A, B, C }; - std::string a = "A"; - std::string b = "B"; - std::vector, - axis::regular>, - axis::regular>, - axis::circular<>, - axis::variable<>, - axis::category<>, - axis::category, - axis::integer<> - >> axes; - axes.push_back(axis::regular<>{2, -1, 1, "regular1"}); - axes.push_back(axis::regular>(2, 1, 10, "regular2", - axis::option_type::none)); - axes.push_back(axis::regular>(2, 1, 10, "regular3", - axis::option_type::overflow, 0.5)); - axes.push_back(axis::regular>(2, 1, 10, "regular4", - axis::option_type::none, -0.5)); - axes.push_back(axis::circular<>(4, 0.1, 1.0, "polar")); - axes.push_back(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none)); - axes.push_back(axis::category<>({A, B, C}, "category")); - axes.push_back(axis::category({a, b}, "category2")); - axes.push_back(axis::integer<>(-1, 1, "integer", axis::option_type::none)); - std::ostringstream os; - for (const auto& a : axes) { os << a << "\n"; } - os << axes.back()[0]; - const std::string ref = - "regular(2, -1, 1, metadata='regular1', options=underflow_and_overflow)\n" - "regular_log(2, 1, 10, metadata='regular2', options=none)\n" - "regular_pow(2, 1, 10, metadata='regular3', options=overflow, power=0.5)\n" - "regular_pow(2, 1, 10, metadata='regular4', options=none, power=-0.5)\n" - "circular(4, 0.1, 1.1, metadata='polar', options=overflow)\n" - "variable(-1, 0, 1, metadata='variable', options=none)\n" - "category(0, 1, 2, metadata='category', options=overflow)\n" - "category('A', 'B', metadata='category2', options=overflow)\n" - "integer(-1, 1, metadata='integer', options=none)\n" - "[-1, 0)"; - BOOST_TEST_EQ(os.str(), ref); + auto test = [](auto&& a, const char* ref) { + using T = detail::rm_cvref; + axis::variant axis(std::move(a)); + std::ostringstream os; + os << axis; + BOOST_TEST_EQ(os.str(), ref); + }; + + test(axis::regular<>{2, -1, 1, "regular1"}, + "regular(2, -1, 1, metadata='regular1', options=underflow_and_overflow)"); + test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), + "regular_log(2, 1, 10, metadata='regular2', options=none)"); + test(axis::regular>(2, 1, 10, "regular3", axis::option_type::overflow, 0.5), + "regular_pow(2, 1, 10, metadata='regular3', options=overflow, power=0.5)"); + test(axis::regular>(2, 1, 10, "regular4", axis::option_type::none, -0.5), + "regular_pow(2, 1, 10, metadata='regular4', options=none, power=-0.5)"); + test(axis::circular<>(4, 0.1, 1.0, "polar"), + "circular(4, 0.1, 1.1, metadata='polar', options=overflow)"); + test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none), + "variable(-1, 0, 1, metadata='variable', options=none)"); + test(axis::category<>({0, 1, 2}, "category"), + "category(0, 1, 2, metadata='category', options=overflow)"); + test(axis::category({"A", "B"}, "category2"), + "category('A', 'B', metadata='category2', options=overflow)"); + test(axis::integer<>(-1, 1, "integer", axis::option_type::none), + "integer(-1, 1, metadata='integer', options=none)"); } - // axis::any equal_comparable + // axis::variant support for minimal_axis + { + struct minimal_axis { + int operator()(double) const { return 0; } + unsigned size() const { return 1; } + }; + + axis::variant axis; + BOOST_TEST_EQ(axis(0), 0); + BOOST_TEST_EQ(axis(10), 0); + BOOST_TEST_EQ(axis.size(), 1); + BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); + BOOST_TEST_THROWS(axis.lower(0), std::runtime_error); + BOOST_TEST_TRAIT_TRUE((std::is_same)); + } + + // bin_type streamable + { + auto test = [](const auto& x, const char* ref) { + std::ostringstream os; + os << x; + BOOST_TEST_EQ(os.str(), ref); + }; + + auto a = axis::regular<>(2, 0, 1); + test(a[0], "[0, 0.5)"); + + auto b = axis::category<>({1, 2}); + test(b[0], "1"); + } + + // axis::variant equal_comparable { enum { A, B, C }; using variant = axis::variant< @@ -351,12 +431,11 @@ int main() { BOOST_TEST(axes == std::vector(axes)); } - // axis::any value_to_index_failure + // axis::variant value_to_index_failure { - std::string a = "A", b = "B"; - axis::variant> x = axis::category({a, b}, "category"); + axis::variant> x = axis::category({"A", "B"}, "category"); auto cx = axis::get>(x); - BOOST_TEST_EQ(cx(b), 1); + // BOOST_TEST_EQ(cx(b), 1); } // sequence equality @@ -484,13 +563,13 @@ int main() { axes.emplace_back(T4(0, 4)); axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); } - // 5 axis::any objects + // 5 axis::variant objects BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second); BOOST_TEST_EQ(db[typeid(axis_type)].first, 5); - // 5 labels + // label BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); - BOOST_TEST_GE(db[typeid(char)].first, 3u); + BOOST_TEST_EQ(db[typeid(char)].first, 3u); // nothing to allocate for T1 // nothing to allocate for T2 diff --git a/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp b/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp index 177529f3..59d02778 100644 --- a/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp +++ b/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp @@ -9,6 +9,7 @@ using namespace boost::histogram; int main() { - auto a = make_dynamic_histogram(axis::integer<>(0, 2)); + std::vector>> v(1, axis::integer<>(0, 2)); + auto a = make_histogram(v); a(std::make_tuple(1)); // fails, because tuple is intentionally not unpacked } diff --git a/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp b/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp index bb49e7ef..f57d5fe4 100644 --- a/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp +++ b/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp @@ -9,6 +9,7 @@ using namespace boost::histogram; int main() { - auto a = make_dynamic_histogram(axis::integer<>(0, 2)); + std::vector>> v(1, axis::integer<>(0, 2)); + auto a = make_histogram(v); a(std::vector(1)); // fails, because tuple is intentionally not unpacked } diff --git a/test/histogram_static_add_fail.cpp b/test/histogram_static_add_fail.cpp index 7e73c286..91c9b3ab 100644 --- a/test/histogram_static_add_fail.cpp +++ b/test/histogram_static_add_fail.cpp @@ -8,7 +8,7 @@ using namespace boost::histogram; int main() { - auto a = make_static_histogram(axis::integer<>(0, 2)); - auto b = make_static_histogram(axis::integer<>(0, 3)); + auto a = make_histogram(axis::integer<>(0, 2)); + auto b = make_histogram(axis::integer<>(0, 3)); a += b; } diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 41936d0d..587b71ae 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -38,23 +39,23 @@ void run_tests() { // init_1 { auto h = make(Tag(), axis::regular<>{3, -1, 1}); - BOOST_TEST_EQ(h.dim(), 1); + BOOST_TEST_EQ(h.rank(), 1); BOOST_TEST_EQ(h.size(), 5); - BOOST_TEST_EQ(h.axis(0_c).shape(), 5); - BOOST_TEST_EQ(h.axis().shape(), 5); + BOOST_TEST_EQ(h.axis(0_c).size(), 3); + BOOST_TEST_EQ(h.axis().size(), 3); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}); BOOST_TEST_EQ(h2, h); } // init_2 { - auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}); - BOOST_TEST_EQ(h.dim(), 2); - BOOST_TEST_EQ(h.size(), 25); - BOOST_TEST_EQ(h.axis(0_c).shape(), 5); - BOOST_TEST_EQ(h.axis(1_c).shape(), 5); + auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 3}); + BOOST_TEST_EQ(h.rank(), 2); + BOOST_TEST_EQ(h.size(), 30); + BOOST_TEST_EQ(h.axis(0_c).size(), 3); + BOOST_TEST_EQ(h.axis(1_c).size(), 4); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, - axis::integer<>{-1, 2}); + axis::integer<>{-1, 3}); BOOST_TEST_EQ(h2, h); } @@ -62,7 +63,7 @@ void run_tests() { { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}); - BOOST_TEST_EQ(h.dim(), 3); + BOOST_TEST_EQ(h.rank(), 3); BOOST_TEST_EQ(h.size(), 75); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}); @@ -73,7 +74,7 @@ void run_tests() { { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}, axis::variable<>{-1, 0, 1}); - BOOST_TEST_EQ(h.dim(), 4); + BOOST_TEST_EQ(h.rank(), 4); BOOST_TEST_EQ(h.size(), 300); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, @@ -87,7 +88,7 @@ void run_tests() { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}, axis::variable<>{-1, 0, 1}, axis::category<>{{A, B, C}}); - BOOST_TEST_EQ(h.dim(), 5); + BOOST_TEST_EQ(h.rank(), 5); BOOST_TEST_EQ(h.size(), 1200); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}, @@ -131,51 +132,49 @@ void run_tests() { const auto href = h; decltype(h) h2(std::move(h)); // static axes cannot shrink to zero - BOOST_TEST_EQ(h.dim(), expected_moved_from_dim(Tag(), 2)); - BOOST_TEST_EQ(sum(h).value(), 0); + BOOST_TEST_EQ(h.rank(), expected_moved_from_dim(Tag(), 2)); + BOOST_TEST_EQ(sum(h), 0); BOOST_TEST_EQ(h.size(), 0); BOOST_TEST_EQ(h2, href); decltype(h) h3; h3 = std::move(h2); // static axes cannot shrink to zero - BOOST_TEST_EQ(h2.dim(), expected_moved_from_dim(Tag(), 2)); - BOOST_TEST_EQ(sum(h2).value(), 0); + BOOST_TEST_EQ(h2.rank(), expected_moved_from_dim(Tag(), 2)); + BOOST_TEST_EQ(sum(h2), 0); BOOST_TEST_EQ(h2.size(), 0); BOOST_TEST_EQ(h3, href); } // axis methods { - enum { A = 3, B = 5 }; auto a = make(Tag(), axis::regular<>(1, 1, 2, "foo")); BOOST_TEST_EQ(a.axis().size(), 1); - BOOST_TEST_EQ(a.axis().shape(), 3); - BOOST_TEST_EQ(a.axis().index(1), 0); BOOST_TEST_EQ(a.axis()[0].lower(), 1); BOOST_TEST_EQ(a.axis()[0].upper(), 2); - BOOST_TEST_EQ(a.axis().label(), "foo"); - a.axis().label("bar"); - BOOST_TEST_EQ(a.axis().label(), "bar"); + BOOST_TEST_EQ(a.axis().metadata(), "foo"); + a.axis().metadata() = "bar"; + BOOST_TEST_EQ(a.axis().metadata(), "bar"); - auto b = make(Tag(), axis::integer<>(1, 2)); - BOOST_TEST_EQ(b.axis().size(), 1); - BOOST_TEST_EQ(b.axis().shape(), 3); - BOOST_TEST_EQ(b.axis().index(1), 0); - BOOST_TEST_EQ(b.axis()[0].lower(), 1); - BOOST_TEST_EQ(b.axis()[0].upper(), 2); - b.axis().label("foo"); - BOOST_TEST_EQ(b.axis().label(), "foo"); + auto b = make(Tag(), axis::regular<>(1, 1, 2, "foo"), + axis::integer<>(1, 3)); + BOOST_TEST_EQ(b.axis(0_c).size(), 1); + BOOST_TEST_EQ(b.axis(0_c)[0].lower(), 1); + BOOST_TEST_EQ(b.axis(0_c)[0].upper(), 2); + BOOST_TEST_EQ(b.axis(1_c).size(), 2); + BOOST_TEST_EQ(b.axis(1_c)[0].lower(), 1); + BOOST_TEST_EQ(b.axis(1_c)[0].upper(), 3); + b.axis(1_c).metadata() = "bar"; + BOOST_TEST_EQ(b.axis(0_c).metadata(), "foo"); + BOOST_TEST_EQ(b.axis(1_c).metadata(), "bar"); - auto c = make(Tag(), axis::category<>({A, B})); + enum class C { A = 3, B = 5 }; + auto c = make(Tag(), axis::category({C::A, C::B})); BOOST_TEST_EQ(c.axis().size(), 2); - BOOST_TEST_EQ(c.axis().shape(), 3); - BOOST_TEST_EQ(c.axis().index(A), 0); - BOOST_TEST_EQ(c.axis().index(B), 1); - c.axis().label("foo"); - BOOST_TEST_EQ(c.axis().label(), "foo"); + c.axis().metadata() = "foo"; + BOOST_TEST_EQ(c.axis().metadata(), "foo"); // need to cast here for this to work with Tag == dynamic_tag, too - auto ca = static_cast&>(c.axis()); - BOOST_TEST_EQ(ca[0].value(), A); + auto ca = axis::get>(c.axis()); + BOOST_TEST(ca[0].value() == C::A); } // equal_compare @@ -211,9 +210,8 @@ void run_tests() { h(-1); h(10); - BOOST_TEST_EQ(h.dim(), 1); - BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 4); + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); BOOST_TEST_EQ(sum(h), 4); BOOST_TEST_EQ(h.at(-1), 1); @@ -224,15 +222,14 @@ void run_tests() { // d1_2 { - auto h = make(Tag(), axis::integer<>(0, 2, "", axis::uoflow_type::off)); + auto h = make(Tag(), axis::integer<>(0, 2, "", axis::option_type::none)); h(0); h(-0); h(-1); h(10); - BOOST_TEST_EQ(h.dim(), 1); - BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 2); + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); BOOST_TEST_EQ(sum(h), 2); BOOST_TEST_EQ(h.at(0), 2); @@ -247,18 +244,19 @@ void run_tests() { h("D"); h("E"); - BOOST_TEST_EQ(h.dim(), 1); - BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 3); + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); BOOST_TEST_EQ(sum(h), 4); BOOST_TEST_EQ(h.at(0), 1); BOOST_TEST_EQ(h.at(1), 1); + BOOST_TEST_EQ(h.at(2), 2); // overflow bin } // d1w { - auto h = make(Tag(), axis::integer<>(0, 2)); + auto h = make_s(Tag(), array_storage>(), + axis::integer<>(0, 2)); h(-1); h(0); h(weight(0.5), 0); @@ -282,17 +280,15 @@ void run_tests() { // d2 { auto h = make(Tag(), axis::regular<>(2, -1, 1), - axis::integer<>(-1, 2, "", axis::uoflow_type::off)); + axis::integer<>(-1, 2, {}, axis::option_type::none)); h(-1, -1); h(-1, 0); h(-1, -10); h(-10, 0); - BOOST_TEST_EQ(h.dim(), 2); + BOOST_TEST_EQ(h.rank(), 2); BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 4); BOOST_TEST_EQ(h.axis(1_c).size(), 3); - BOOST_TEST_EQ(h.axis(1_c).shape(), 3); BOOST_TEST_EQ(sum(h), 3); BOOST_TEST_EQ(h.at(-1, 0), 0); @@ -314,8 +310,10 @@ void run_tests() { // d2w { - auto h = make(Tag(), axis::regular<>(2, -1, 1), - axis::integer<>(-1, 2, "", axis::uoflow_type::off)); + auto h = make_s(Tag(), + array_storage>(), + axis::regular<>(2, -1, 1), + axis::integer<>(-1, 2, {}, axis::option_type::none)); h(-1, 0); // -> 0, 1 h(weight(10), -1, -1); // -> 0, 0 h(weight(5), -1, -10); // is ignored @@ -359,17 +357,20 @@ void run_tests() { // d3w { - auto h = - make(Tag(), axis::integer<>(0, 3), axis::integer<>(0, 4), axis::integer<>(0, 5)); - for (auto i = 0; i < h.axis(0_c).size(); ++i) { - for (auto j = 0; j < h.axis(1_c).size(); ++j) { - for (auto k = 0; k < h.axis(2_c).size(); ++k) { h(weight(i + j + k), i, j, k); } + auto h = make_s(Tag(), + array_storage>(), + axis::integer<>(0, 3), + axis::integer<>(0, 4), + axis::integer<>(0, 5)); + for (auto i = 0u; i < h.axis(0_c).size(); ++i) { + for (auto j = 0u; j < h.axis(1_c).size(); ++j) { + for (auto k = 0u; k < h.axis(2_c).size(); ++k) { h(weight(i + j + k), i, j, k); } } } - for (auto i = 0; i < h.axis(0_c).size(); ++i) { - for (auto j = 0; j < h.axis(1_c).size(); ++j) { - for (auto k = 0; k < h.axis(2_c).size(); ++k) { + for (auto i = 0u; i < h.axis(0_c).size(); ++i) { + for (auto j = 0u; j < h.axis(1_c).size(); ++j) { + for (auto k = 0u; k < h.axis(2_c).size(); ++k) { BOOST_TEST_EQ(h.at(i, j, k).value(), i + j + k); BOOST_TEST_EQ(h.at(i, j, k).variance(), (i + j + k) * (i + j + k)); } @@ -402,8 +403,10 @@ void run_tests() { // add_2 { - auto a = make(Tag(), axis::integer<>(0, 2)); - auto b = make(Tag(), axis::integer<>(0, 2)); + auto a = make_s(Tag(), array_storage>(), + axis::integer<>(0, 2)); + auto b = make_s(Tag(), array_storage>(), + axis::integer<>(0, 2)); a(0); BOOST_TEST_EQ(a.at(0).variance(), 1); @@ -476,20 +479,20 @@ void run_tests() { BOOST_TEST_EQ(d.at(1), 3); auto e = 3 * a; auto f = b * 2; - BOOST_TEST_EQ(e.at(0).value(), 3); - BOOST_TEST_EQ(e.at(1).value(), 0); - BOOST_TEST_EQ(f.at(0).value(), 0); - BOOST_TEST_EQ(f.at(1).value(), 2); + BOOST_TEST_EQ(e.at(0), 3); + BOOST_TEST_EQ(e.at(1), 0); + BOOST_TEST_EQ(f.at(0), 0); + BOOST_TEST_EQ(f.at(1), 2); auto r = a; r += b; r += e; - BOOST_TEST_EQ(r.at(0).value(), 4); - BOOST_TEST_EQ(r.at(1).value(), 1); + BOOST_TEST_EQ(r.at(0), 4); + BOOST_TEST_EQ(r.at(1), 1); BOOST_TEST_EQ(r, a + b + 3 * a); auto s = r / 4; r /= 4; - BOOST_TEST_EQ(r.at(0).value(), 1); - BOOST_TEST_EQ(r.at(1).value(), 0.25); + BOOST_TEST_EQ(r.at(0), 1); + BOOST_TEST_EQ(r.at(1), 0.25); BOOST_TEST_EQ(r, s); } @@ -500,14 +503,14 @@ void run_tests() { os << a; BOOST_TEST_EQ(os.str(), "histogram(" - "\n regular(3, -1, 1, label='r')," - "\n integer(0, 2, label='i')," + "\n regular(3, -1, 1, metadata='r', options=underflow_and_overflow)," + "\n integer(0, 2, metadata='i', options=underflow_and_overflow)," "\n)"); } // histogram_reset { - auto h = make(Tag(), axis::integer<>(0, 2, "", axis::uoflow_type::off)); + auto h = make(Tag(), axis::integer<>(0, 2, {}, axis::option_type::none)); h(0); h(1); BOOST_TEST_EQ(h.at(0), 1); @@ -540,16 +543,14 @@ void run_tests() { */ auto h1_0 = h1.reduce_to(0_c); - BOOST_TEST_EQ(h1_0.dim(), 1); + BOOST_TEST_EQ(h1_0.rank(), 1); BOOST_TEST_EQ(sum(h1_0), 5); BOOST_TEST_EQ(h1_0.at(0), 2); BOOST_TEST_EQ(h1_0.at(1), 3); - BOOST_TEST_EQ(h1_0.axis()[0].lower(), 0); - BOOST_TEST_EQ(h1_0.axis()[1].lower(), 1); BOOST_TEST(h1_0.axis() == h1.axis(0_c)); auto h1_1 = h1.reduce_to(1_c); - BOOST_TEST_EQ(h1_1.dim(), 1); + BOOST_TEST_EQ(h1_1.rank(), 1); BOOST_TEST_EQ(sum(h1_1), 5); BOOST_TEST_EQ(h1_1.at(0), 2); BOOST_TEST_EQ(h1_1.at(1), 2); @@ -565,21 +566,21 @@ void run_tests() { h2(1, 0, 2); auto h2_0 = h2.reduce_to(0_c); - BOOST_TEST_EQ(h2_0.dim(), 1); + BOOST_TEST_EQ(h2_0.rank(), 1); BOOST_TEST_EQ(sum(h2_0), 5); BOOST_TEST_EQ(h2_0.at(0), 4); BOOST_TEST_EQ(h2_0.at(1), 1); BOOST_TEST(h2_0.axis() == axis::integer<>(0, 2)); auto h2_1 = h2.reduce_to(1_c); - BOOST_TEST_EQ(h2_1.dim(), 1); + BOOST_TEST_EQ(h2_1.rank(), 1); BOOST_TEST_EQ(sum(h2_1), 5); BOOST_TEST_EQ(h2_1.at(0), 3); BOOST_TEST_EQ(h2_1.at(1), 2); BOOST_TEST(h2_1.axis() == axis::integer<>(0, 3)); auto h2_2 = h2.reduce_to(2_c); - BOOST_TEST_EQ(h2_2.dim(), 1); + BOOST_TEST_EQ(h2_2.rank(), 1); BOOST_TEST_EQ(sum(h2_2), 5); BOOST_TEST_EQ(h2_2.at(0), 2); BOOST_TEST_EQ(h2_2.at(1), 1); @@ -587,7 +588,7 @@ void run_tests() { BOOST_TEST(h2_2.axis() == axis::integer<>(0, 4)); auto h2_01 = h2.reduce_to(0_c, 1_c); - BOOST_TEST_EQ(h2_01.dim(), 2); + BOOST_TEST_EQ(h2_01.rank(), 2); BOOST_TEST_EQ(sum(h2_01), 5); BOOST_TEST_EQ(h2_01.at(0, 0), 2); BOOST_TEST_EQ(h2_01.at(0, 1), 2); @@ -596,7 +597,7 @@ void run_tests() { BOOST_TEST(h2_01.axis(1_c) == axis::integer<>(0, 3)); auto h2_02 = h2.reduce_to(0_c, 2_c); - BOOST_TEST_EQ(h2_02.dim(), 2); + BOOST_TEST_EQ(h2_02.rank(), 2); BOOST_TEST_EQ(sum(h2_02), 5); BOOST_TEST_EQ(h2_02.at(0, 0), 2); BOOST_TEST_EQ(h2_02.at(0, 1), 1); @@ -606,7 +607,7 @@ void run_tests() { BOOST_TEST(h2_02.axis(1_c) == axis::integer<>(0, 4)); auto h2_12 = h2.reduce_to(1_c, 2_c); - BOOST_TEST_EQ(h2_12.dim(), 2); + BOOST_TEST_EQ(h2_12.rank(), 2); BOOST_TEST_EQ(sum(h2_12), 5); BOOST_TEST_EQ(h2_12.at(0, 0), 1); BOOST_TEST_EQ(h2_12.at(1, 0), 1); @@ -619,14 +620,12 @@ void run_tests() { // custom axis { struct custom_axis : public axis::integer<> { - using value_type = const char*; // type that is fed to the axis - using integer::integer; // inherit ctors of base - // the customization point - // - accept const char* and convert to int - // - then call index method of base class - int index(value_type s) const { return integer::index(std::atoi(s)); } + // customization point: convert argument and call base class + int operator()(const char* s) const { + return integer::operator()(std::atoi(s)); + } }; auto h = make(Tag(), custom_axis(0, 3)); @@ -635,7 +634,7 @@ void run_tests() { h("1"); h("9"); - BOOST_TEST_EQ(h.dim(), 1); + BOOST_TEST_EQ(h.rank(), 1); BOOST_TEST_EQ(h.axis(), custom_axis(0, 3)); BOOST_TEST_EQ(h.at(0), 1); BOOST_TEST_EQ(h.at(1), 1); @@ -644,13 +643,14 @@ void run_tests() { // histogram iterator 1D { - auto h = make(Tag(), axis::integer<>(0, 3)); + auto h = make_s(Tag(), array_storage>(), + axis::integer<>(0, 3)); const auto& a = h.axis(); h(weight(2), 0); h(1); h(1); auto it = h.begin(); - BOOST_TEST_EQ(it.dim(), 1); + BOOST_TEST_EQ(it.rank(), 1); BOOST_TEST_EQ(it.idx(), 0); BOOST_TEST_EQ(it.bin(), a[0]); @@ -678,8 +678,10 @@ void run_tests() { // histogram iterator 2D { - auto h = make(Tag(), axis::integer<>(0, 1), - axis::integer<>(2, 4, "", axis::uoflow_type::off)); + auto h = make_s(Tag(), + array_storage>(), + axis::integer<>(0, 1), + axis::integer<>(2, 4, "", axis::option_type::none)); const auto& a0 = h.axis(0_c); const auto& a1 = h.axis(1_c); h(weight(2), 0, 2); @@ -687,7 +689,7 @@ void run_tests() { h(1, 3); auto it = h.begin(); - BOOST_TEST_EQ(it.dim(), 2); + BOOST_TEST_EQ(it.rank(), 2); BOOST_TEST_EQ(it.idx(0), 0); BOOST_TEST_EQ(it.idx(1), 0); @@ -742,16 +744,18 @@ void run_tests() { { auto h = make(Tag(), axis::integer<>(0, 3)); for (int i = 0; i < 3; ++i) h(i); - auto a = std::vector>(); + auto a = std::vector(); std::partial_sum(h.begin(), h.end(), std::back_inserter(a)); - BOOST_TEST_EQ(a[0].value(), 1); - BOOST_TEST_EQ(a[1].value(), 2); - BOOST_TEST_EQ(a[2].value(), 3); + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 2); + BOOST_TEST_EQ(a[2], 3); } // using STL containers { - auto h = make(Tag(), axis::integer<>(0, 2), axis::regular<>(2, 2, 4)); + auto h = make_s(Tag(), + array_storage>(), + axis::integer<>(0, 2), axis::regular<>(2, 2, 4)); // vector in h(std::vector({0, 2})); // pair in @@ -805,9 +809,8 @@ void run_tests() { tracing_allocator_db db; { tracing_allocator a(db); - auto h = make_s(Tag(), array_storage>(a), - axis::integer>( - 0, 1024, std::string(512, 'c'), axis::uoflow_type::on, a)); + auto h = make_s(Tag(), array_storage>(a), + axis::integer<>(0, 1024)); h(0); } @@ -815,12 +818,8 @@ void run_tests() { BOOST_TEST_EQ(db[typeid(int)].first, db[typeid(int)].second); BOOST_TEST_GE(db[typeid(int)].first, 1024u); - // char allocation for axis label - BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); - BOOST_TEST_GE(db[typeid(char)].first, 512u); - - if (Tag()) { // axis::any allocation, only for dynamic histogram - using T = axis::any>>; + if (Tag()) { // axis::variant allocation, only for dynamic histogram + using T = axis::variant>; BOOST_TEST_EQ(db[typeid(T)].first, db[typeid(T)].second); BOOST_TEST_GE(db[typeid(T)].first, 1u); } diff --git a/test/meta_test.cpp b/test/meta_test.cpp index 480a43e3..6e3e5c1f 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -126,6 +126,14 @@ int main() { BOOST_TEST_TRAIT_TRUE((is_iterable)); } + // is_streamable + { + struct Foo {}; + BOOST_TEST_TRAIT_TRUE((is_streamable)); + BOOST_TEST_TRAIT_TRUE((is_streamable)); + BOOST_TEST_TRAIT_FALSE((is_streamable)); + } + // is_axis_variant { struct A {}; @@ -136,29 +144,36 @@ int main() { // classify_container { - using result1 = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using A = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result1a = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using B = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result2 = classify_container>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using C = classify_container>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result2a = classify_container&>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using D = classify_container&>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result3 = classify_container>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using E = classify_container>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result3a = classify_container&>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using F = classify_container&>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result4 = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using G = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result5 = classify_container; // has no std::end - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using H = classify_container; // has no std::end + BOOST_TEST_TRAIT_TRUE((std::is_same)); + + using I = classify_container>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); + + auto j = {0, 1}; + using J = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); } // bool mask @@ -244,6 +259,17 @@ int main() { BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); } + // args_type + { + struct Foo { + static int f1(char); + int f2(long) const; + }; + + BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + } + // visitor_return_type { using V1 = bh::axis::variant; From 9c832799989dcb30d0553824bb78f5a82176f410 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sun, 21 Oct 2018 22:55:53 +0200 Subject: [PATCH 05/44] wip, histogram_test compiles --- include/boost/histogram/axis/traits.hpp | 22 +++++++--------- include/boost/histogram/axis/variant.hpp | 32 ++++++++++++++++-------- include/boost/histogram/detail/axes.hpp | 2 +- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index abd30b1b..ed6fc97d 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -12,25 +12,21 @@ namespace boost { namespace histogram { -namespace detail { - template - struct metadata_type_impl; - template - struct metadata_type_impl { - using type = detail::rm_cvref().metadata())>; - }; - template - struct metadata_type_impl { - using type = axis::missing_metadata_type; - }; -} // namespace detail namespace axis { namespace traits { template using args = detail::args_type; template - using metadata_type = typename detail::metadata_type_impl, T>::type; + decltype(auto) metadata(T&& t) { + return detail::overload( + [](std::true_type, auto&& x) { return x.metadata(); }, + [](std::false_type, auto&&) { + static missing_metadata_type m; + return static_cast>(m); + } + )(detail::has_method_metadata>(), t); + } template option_type options(const T& t) { diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 550839f0..5cfcb9fb 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -78,7 +78,7 @@ class variant using base_type = boost::variant; using bin_type = interval_view; using first_bounded_type = mp11::mp_first; - using metadata_type = traits::metadata_type; + using metadata_type = detail::rm_cvref()))>; template using requires_bounded_type = @@ -132,27 +132,37 @@ public: } const metadata_type& metadata() const { - return visit([](const auto& x) { - using T = detail::rm_cvref; - using U = traits::metadata_type; + return visit([](const auto& x) -> const metadata_type& { + using U = decltype(traits::metadata(x)); return detail::overload( - [](std::true_type, const auto& x) { return x.metadata(); }, + [](std::true_type, const auto& x) -> const metadata_type& { return traits::metadata(x); }, [](std::false_type, const auto&) -> const metadata_type& { throw std::runtime_error(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " cannot return metadata of type ", + "cannot return metadata of type ", boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), " through variant interface which uses type ", - boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type) ) + boost::core::demangled_name( BOOST_CORE_TYPEID(const metadata_type&) ) )); } - )(std::is_same(), x); + )(std::is_same(), x); }, *this); } metadata_type& metadata() { - // return visit([](auto& x) { return axis::traits::metadata(x); }, *this); - return metadata_type(); + return visit([](auto& x) -> metadata_type& { + using U = decltype(traits::metadata(x)); + return detail::overload( + [](std::true_type, auto& x) -> metadata_type& { return traits::metadata(x); }, + [](std::false_type, auto&) -> metadata_type& { + throw std::runtime_error(detail::cat( + "cannot return metadata of type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), + " through variant interface which uses type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type&) ) + )); + } + )(std::is_same(), x); + }, *this); } // Only works for axes with compatible call signature diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index f00567cd..215bc868 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -351,7 +351,7 @@ template void indices_to_index_iter(optional_index& idx, const std::vector& axes, Iterator iter) { for (const auto& a : axes) { - const auto a_size = a.size(); + const auto a_size = static_cast(a.size()); const auto a_shape = axis::traits::extend(a); const auto j = static_cast(*iter++); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid From e35d4f56b6bba01ed9439ded8d82f8e833ad924e Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Tue, 23 Oct 2018 01:35:50 +0200 Subject: [PATCH 06/44] all tests without streamers passing, need to fix streamers --- examples/getting_started_listing_01.cpp | 55 ++++++++-------- examples/guide_access_bin_counts.cpp | 65 +++++++++---------- examples/guide_axis_with_labels.cpp | 5 +- examples/guide_custom_minimal_axis.cpp | 2 +- examples/guide_histogram_reduction.cpp | 37 ++++++----- examples/guide_histogram_streaming.cpp | 47 +++++++------- .../histogram/axis/ostream_operators.hpp | 44 +++++++------ include/boost/histogram/axis/traits.hpp | 20 +++--- include/boost/histogram/axis/variant.hpp | 17 ++--- include/boost/histogram/detail/meta.hpp | 4 ++ include/boost/histogram/histogram_fwd.hpp | 2 +- test/axis_test.cpp | 30 +++++---- test/detail_test.cpp | 31 +-------- test/histogram_dynamic_add_fail.cpp | 11 +++- ..._dynamic_at_tuple_wrong_dimension_fail.cpp | 5 +- ...dynamic_at_vector_wrong_dimension_fail.cpp | 4 +- ...togram_dynamic_at_wrong_dimension_fail.cpp | 5 +- ...togram_dynamic_reduce_wrong_order_fail.cpp | 6 +- test/histogram_dynamic_test.cpp | 24 +++---- ..._static_at_vector_wrong_dimension_fail.cpp | 2 + test/histogram_test.cpp | 41 ++++++------ test/meta_test.cpp | 2 +- test/utility.hpp | 14 +--- 23 files changed, 234 insertions(+), 239 deletions(-) diff --git a/examples/getting_started_listing_01.cpp b/examples/getting_started_listing_01.cpp index fcdc6e4d..97c356ac 100644 --- a/examples/getting_started_listing_01.cpp +++ b/examples/getting_started_listing_01.cpp @@ -1,7 +1,10 @@ //[ getting_started_listing_01 #include -#include +#include +#include +#include +#include int main() { namespace bh = boost::histogram; @@ -11,12 +14,13 @@ int main() { create a static 1d-histogram with an axis that has 6 equidistant bins on the real line from -1.0 to 2.0, and label it as "x" */ - auto h = bh::make_static_histogram(bh::axis::regular<>(6, -1.0, 2.0, "x")); + auto h = bh::make_histogram(bh::axis::regular<>(6, -1.0, 2.0, "x")); - // fill histogram with data, typically this happens in a loop - // STL algorithms are supported + // Fill histogram with data, typically this happens in a loop. + // STL algorithms are supported, but make sure to use std::ref in + // the call to std::for_each to avoid copying the argument. auto data = {-0.5, 1.1, 0.3, 1.7}; - h = std::for_each(data.begin(), data.end(), h); + std::for_each(data.begin(), data.end(), std::ref(h)); /* a regular axis is a sequence of semi-open bins; extra under- and @@ -39,40 +43,37 @@ int main() { iterate over bins with a fancy histogram iterator - order in which bins are iterated over is an implementation detail - iterator dereferences to histogram::element_type, which is defined by - its storage class; by default something with value() and - variance() methods; the first returns the - actual count, the second returns a variance estimate of the count - (see Rationale section for what this means) + its storage class; by default this is a double - idx(N) method returns the index of the N-th axis - bin(N_c) method returns current bin of N-th axis; the suffx _c turns the argument into a compile-time number, which is needed to return - different `bin_type`s for different axes + a different `bin_type`s for each axis - `bin_type` usually is a semi-open interval representing the bin, whose edges can be accessed with methods `lower()` and `upper()`, but the implementation depends on the axis, please look it up in the reference */ - std::cout.setf(std::ios_base::fixed); + std::ostringstream os; + os.setf(std::ios_base::fixed); for (auto it = h.begin(); it != h.end(); ++it) { const auto bin = it.bin(0_c); - std::cout << "bin " << it.idx(0) << " x in [" << std::setprecision(1) - << std::setw(4) << bin.lower() << ", " << std::setw(4) - << bin.upper() << "): " << std::setprecision(1) << it->value() - << " +/- " << std::setprecision(3) << std::sqrt(it->variance()) - << std::endl; + os << "bin " << std::setw(2) << it.idx(0) << " [" << std::setprecision(1) + << std::setw(4) << bin.lower() << ", " << std::setw(4) + << bin.upper() << "): " << *it << "\n"; } - /* program output: (note that under- and overflow bins appear at the end) + std::cout << os.str() << std::endl; - bin 0 x in [-1.0, -0.5): 1 +/- 1 - bin 1 x in [-0.5, 0.0): 0 +/- 0 - bin 2 x in [ 0.0, 0.5): 1 +/- 1 - bin 3 x in [ 0.5, 1.0): 0 +/- 0 - bin 4 x in [ 1.0, 1.5): 0 +/- 0 - bin 5 x in [ 1.5, 2.0): 0 +/- 0 - bin 6 x in [ 2.0, inf): 2 +/- 1.41421 - bin -1 x in [-inf, -1): 1 +/- 1 - - */ + assert(os.str() == + "bin 0 [-1.0, -0.5): 1.0\n" + "bin 1 [-0.5, -0.0): 1.0\n" + "bin 2 [-0.0, 0.5): 2.0\n" + "bin 3 [ 0.5, 1.0): 0.0\n" + "bin 4 [ 1.0, 1.5): 1.0\n" + "bin 5 [ 1.5, 2.0): 1.0\n" + "bin 6 [ 2.0, inf): 2.0\n" + "bin -1 [-inf, -1.0): 1.0\n" + ); + // note how under- and overflow bins appear at the end } //] diff --git a/examples/guide_access_bin_counts.cpp b/examples/guide_access_bin_counts.cpp index 7c448ee3..a3b46fce 100644 --- a/examples/guide_access_bin_counts.cpp +++ b/examples/guide_access_bin_counts.cpp @@ -1,15 +1,15 @@ //[ guide_access_bin_counts #include -#include #include +#include namespace bh = boost::histogram; int main() { // make histogram with 2 x 2 = 4 bins (not counting under-/overflow bins) - auto h = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1), - bh::axis::regular<>(2, 2, 4)); + auto h = bh::make_histogram(bh::axis::regular<>(2, -1, 1), + bh::axis::regular<>(2, 2, 4)); h(bh::weight(1), -0.5, 2.5); // bin index 0, 0 h(bh::weight(2), -0.5, 3.5); // bin index 0, 1 @@ -17,46 +17,45 @@ int main() { h(bh::weight(4), 0.5, 3.5); // bin index 1, 1 // access count value, number of indices must match number of axes - std::cout << h.at(0, 0).value() << " " << h.at(0, 1).value() << " " - << h.at(1, 0).value() << " " << h.at(1, 1).value() << std::endl; - - // prints: 1 2 3 4 - - // access count variance, number of indices must match number of axes - std::cout << h.at(0, 0).variance() << " " << h.at(0, 1).variance() << " " - << h.at(1, 0).variance() << " " << h.at(1, 1).variance() - << std::endl; - // prints: 1 4 9 16 - - // this is more efficient when you want to query value and variance - auto c11 = h.at(1, 1); - std::cout << c11.value() << " " << c11.variance() << std::endl; - // prints: 4 16 + assert(h.at(0, 0) == 1); + assert(h.at(0, 1) == 2); + assert(h.at(1, 0) == 3); + assert(h.at(1, 1) == 4); // histogram also supports access via container; using a container of // wrong size trips an assertion in debug mode auto idx = {0, 1}; - std::cout << h.at(idx).value() << std::endl; - // prints: 2 + assert(h.at(idx) == 2); // histogram also provides bin iterators - auto sum = std::accumulate(h.begin(), h.end(), - typename decltype(h)::element_type(0)); - std::cout << sum.value() << " " << sum.variance() << std::endl; - // prints: 10 30 + auto sum = std::accumulate(h.begin(), h.end(), 0.0); + assert(sum == 10); // bin iterators are fancy iterators with extra methods + // (note: iteration order is an implementation detail, don't rely on it) + std::ostringstream os; for (auto it = h.begin(), end = h.end(); it != end; ++it) { - const auto x = *it; - std::cout << it.idx(0) << " " << it.idx(1) << ": " - << x.value() << " " << x.variance() << std::endl; + os << std::setw(2) << it.idx(0) << " " << std::setw(2) << it.idx(1) << ": " << *it; } - // prints: (iteration order is an implementation detail, don't rely on it) - // 0 0: 1 1 - // 1 0: 3 9 - // ... - // 2 -1: 0 0 - // -1 -1: 0 0 + + assert(os.str() == + " 0 0: 1" + " 1 0: 3" + " 2 0: 0" + "-1 0: 0" + " 0 1: 2" + " 1 1: 4" + " 2 1: 0" + "-1 1: 0" + " 0 2: 0" + " 1 2: 0" + " 2 2: 0" + "-1 2: 0" + " 0 -1: 0" + " 1 -1: 0" + " 2 -1: 0" + "-1 -1: 0" + ); } //] diff --git a/examples/guide_axis_with_labels.cpp b/examples/guide_axis_with_labels.cpp index 2fe0dabc..eed20968 100644 --- a/examples/guide_axis_with_labels.cpp +++ b/examples/guide_axis_with_labels.cpp @@ -6,9 +6,10 @@ namespace bh = boost::histogram; int main() { // create a 2d-histogram with an "age" and an "income" axis - auto h = bh::make_static_histogram( + auto h = bh::make_histogram( bh::axis::regular<>(20, 0, 100, "age in years"), - bh::axis::regular<>(20, 0, 100, "yearly income in $1000")); + bh::axis::regular<>(20, 0, 100, "yearly income in $1000") + ); // do something with h } diff --git a/examples/guide_custom_minimal_axis.cpp b/examples/guide_custom_minimal_axis.cpp index 36c0cc74..7847b5a8 100644 --- a/examples/guide_custom_minimal_axis.cpp +++ b/examples/guide_custom_minimal_axis.cpp @@ -7,7 +7,7 @@ namespace bh = boost::histogram; // stateless axis which returns 1 if the input is even and 0 otherwise struct minimal_axis { - int operator()(int x) const { return x % 2 == 0; } + int operator()(int x) const { return x % 2; } unsigned size() const { return 2; } }; diff --git a/examples/guide_histogram_reduction.cpp b/examples/guide_histogram_reduction.cpp index 43670ebc..145eafa8 100644 --- a/examples/guide_histogram_reduction.cpp +++ b/examples/guide_histogram_reduction.cpp @@ -1,7 +1,8 @@ //[ guide_histogram_reduction #include -#include +#include +#include namespace bh = boost::histogram; @@ -18,8 +19,8 @@ int main() { using namespace bh::literals; // enables _c suffix // make a 2d histogram - auto h = bh::make_static_histogram(bh::axis::regular<>(3, -1, 1), - bh::axis::integer<>(0, 4)); + auto h = bh::make_histogram(bh::axis::regular<>(3, -1, 1), + bh::axis::integer<>(0, 4)); h(-0.9, 0); h(0.9, 3); @@ -32,26 +33,26 @@ int main() { reduce does not remove counts; returned histograms are summed over the removed axes, so h, hr0, and hr1 have same number of total counts */ - std::cout << sum(h).value() << " " << sum(hr0).value() << " " - << sum(hr1).value() << std::endl; - // prints: 3 3 3 + assert(sum(h) == 3 && sum(hr0) == 3 && sum(hr1) == 3); + std::ostringstream os1; for (auto yi : h.axis(1_c)) { - for (auto xi : h.axis(0_c)) { std::cout << h.at(xi, yi).value() << " "; } - std::cout << std::endl; + for (auto xi : h.axis(0_c)) { os1 << h.at(xi, yi) << " "; } + os1 << "\n"; } - // prints: 1 0 0 - // 0 0 0 - // 0 1 0 - // 0 0 1 + assert(os1.str() == + "1 0 0 \n" + "0 0 0 \n" + "0 1 0 \n" + "0 0 1 \n"); - for (auto xi : hr0.axis()) std::cout << hr0.at(xi).value() << " "; - std::cout << std::endl; - // prints: 1 1 1 + std::ostringstream os2; + for (auto xi : hr0.axis()) os2 << hr0.at(xi) << " "; + assert(os2.str() == "1 1 1 "); - for (auto yi : hr1.axis()) std::cout << hr1.at(yi).value() << " "; - std::cout << std::endl; - // prints: 1 0 1 1 + std::ostringstream os3; + for (auto yi : hr1.axis()) os3 << hr1.at(yi) << " "; + assert(os3.str() == "1 0 1 1 "); } //] diff --git a/examples/guide_histogram_streaming.cpp b/examples/guide_histogram_streaming.cpp index 1c465671..913d03c8 100644 --- a/examples/guide_histogram_streaming.cpp +++ b/examples/guide_histogram_streaming.cpp @@ -2,37 +2,38 @@ #include #include -#include +#include +#include namespace bh = boost::histogram; int main() { - namespace axis = boost::histogram::axis; + namespace axis = bh::axis; - enum { A, B, C }; + auto h = bh::make_histogram( + axis::regular<>(2, -1, 1), + axis::regular>(2, 1, 10, "axis 1"), + axis::circular(4, 0.1, 1.0), // axis without metadata + axis::variable<>({-1, 0, 1}, "axis 3", axis::option_type::none), + axis::category<>({2, 1, 3}, "axis 4"), + axis::integer<>(-1, 1, "axis 5") + ); - auto h = bh::make_static_histogram( - axis::regular<>(2, -1, 1, "regular1", axis::uoflow_type::off), - axis::regular(2, 1, 10, "regular2"), - axis::circular<>(4, 0.1, 1.0, "polar"), - axis::variable<>({-1, 0, 1}, "variable", axis::uoflow_type::off), - axis::category<>({A, B, C}, "category"), - axis::integer<>(-1, 1, "integer", axis::uoflow_type::off)); + std::ostringstream os; + os << h; - std::cout << h << std::endl; + std::cout << os.str() << std::endl; - /* prints: - - histogram( - regular(2, -1, 1, label='regular1', uoflow=False), - regular_log(2, 1, 10, label='regular2'), - circular(4, phase=0.1, perimeter=1, label='polar'), - variable(-1, 0, 1, label='variable', uoflow=False), - category(0, 1, 2, label='category'), - integer(-1, 1, label='integer', uoflow=False), - ) - - */ + assert(os.str() == + "histogram(\n" + " regular(2, -1, 1, options=underflow_and_overflow),\n" + " regular_log(2, 1, 10, metadata=\"axis 1\", options=underflow_and_overflow),\n" + " circular(4, 0.1, 1.1, options=overflow),\n" + " variable(-1, 0, 1, metadata=\"axis 3\", options=none),\n" + " category(2, 1, 3, metadata=\"axis 4\", options=overflow),\n" + " integer(-1, 1, metadata=\"axis 5\", options=underflow_and_overflow),\n" + ")" + ); } //] diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 1ad43d28..908cd057 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include namespace boost { namespace histogram { @@ -26,28 +29,21 @@ template const char* to_string(const axis::transform::sqrt&) { r template const char* to_string(const axis::transform::pow&) { return "_pow"; } template const char* to_string(const axis::transform::quantity&) { return "_quantity"; } -template -void escape_string(OStream& os, const std::string& s) { - os << '\''; - for (auto sit = s.begin(); sit != s.end(); ++sit) { - if (*sit == '\'' && (sit == s.begin() || *(sit - 1) != '\\')) { - os << "\\\'"; - } else { - os << *sit; - } - } - os << '\''; -} - template void stream_metadata(OStream& os, const T& t) { - if (detail::is_streamable::value) { - std::ostringstream oss; - oss << t; - if (!oss.str().empty()) - os << ", metadata="; - escape_string(os, oss.str()); - } + detail::overload( + [](std::true_type, OStream& os, const auto& t) { + std::ostringstream oss; + oss << t; + if (!oss.str().empty()) { + os << ", metadata=" << std::quoted(oss.str()); + } + }, + [](std::false_type, OStream& os, const T&) { + using U = detail::rm_cvref; + os << ", metadata=" << boost::core::demangled_name( BOOST_CORE_TYPEID(U) ); + } + )(detail::is_streamable(), os, t); } template @@ -77,6 +73,12 @@ void stream_transform(OStream& os, const axis::transform::quantity& t) { namespace axis { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const empty_metadata_type&) { + return os; // do nothing +} + template std::basic_ostream& operator<<(std::basic_ostream& os, const interval_view& i) { @@ -151,7 +153,7 @@ std::basic_ostream& operator<<( std::basic_ostream& os, const category& a) { os << "category("; for (unsigned i = 0; i < a.size(); ++i) { - detail::escape_string(os, a.value(i)); + os << std::quoted(a.value(i)); os << (i == (a.size() - 1) ? "" : ", "); } detail::stream_metadata(os, a.metadata()); diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index ed6fc97d..1c9937db 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -12,6 +12,16 @@ namespace boost { namespace histogram { +namespace detail { + template + decltype(auto) metadata_impl(std::true_type, T&& x) { return x.metadata(); } + + template + copy_qualifiers metadata_impl(std::false_type, T&&) { + static axis::empty_metadata_type m; + return m; + } +} namespace axis { namespace traits { template @@ -19,20 +29,14 @@ namespace traits { template decltype(auto) metadata(T&& t) { - return detail::overload( - [](std::true_type, auto&& x) { return x.metadata(); }, - [](std::false_type, auto&&) { - static missing_metadata_type m; - return static_cast>(m); - } - )(detail::has_method_metadata>(), t); + return detail::metadata_impl(detail::has_method_metadata>(), t); } template option_type options(const T& t) { return detail::overload( [](std::true_type, const auto& x) { return x.options(); }, - [](std::false_type, const auto&) { return axis::option_type::none; } + [](std::false_type, const T&) { return axis::option_type::none; } )(detail::has_method_options(), t); } diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 5cfcb9fb..4de3a40b 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -140,7 +140,7 @@ public: throw std::runtime_error(detail::cat( "cannot return metadata of type ", boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), - " through variant interface which uses type ", + " through axis::variant interface which uses type ", boost::core::demangled_name( BOOST_CORE_TYPEID(const metadata_type&) ) )); } @@ -156,8 +156,8 @@ public: [](std::false_type, auto&) -> metadata_type& { throw std::runtime_error(detail::cat( "cannot return metadata of type ", - boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), - " through variant interface which uses type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(mp11::mp_identity) ), + " through axis::variant interface which uses type ", boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type&) ) )); } @@ -281,23 +281,18 @@ T&& get(variant&& v) { template T* get(variant* v) { - return boost::get(static_cast::base_type*>(v)); + return boost::relaxed_get(static_cast::base_type*>(v)); } template const T* get(const variant* v) { - return boost::get(static_cast::base_type*>(v)); + return boost::relaxed_get(static_cast::base_type*>(v)); } // pass-through if T is an axis instead of a variant template >, - typename = mp11::mp_if< - std::is_same< - detail::rm_cvref, - detail::rm_cvref - >, void> - > + typename = detail::requires_same>> U get(U&& u) { return std::forward(u); } diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index aea73067..cc5841f6 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -204,6 +204,10 @@ template ::value)), void>> struct requires_axis_vector {}; +template , void>> +struct requires_same {}; + } // namespace detail } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 7734aee1..f5dc394f 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -16,7 +16,7 @@ namespace histogram { namespace axis { -struct missing_metadata_type {}; +struct empty_metadata_type {}; enum class option_type { none = 0, diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 52b40c85..3df4c12c 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -350,27 +350,29 @@ int main() { axis::variant axis(std::move(a)); std::ostringstream os; os << axis; - BOOST_TEST_EQ(os.str(), ref); + BOOST_TEST_EQ(os.str(), std::string(ref)); }; + struct user_defined {}; + test(axis::regular<>{2, -1, 1, "regular1"}, - "regular(2, -1, 1, metadata='regular1', options=underflow_and_overflow)"); + "regular(2, -1, 1, metadata=\"regular1\", options=underflow_and_overflow)"); test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), - "regular_log(2, 1, 10, metadata='regular2', options=none)"); + "regular_log(2, 1, 10, metadata=\"regular2\", options=none)"); test(axis::regular>(2, 1, 10, "regular3", axis::option_type::overflow, 0.5), - "regular_pow(2, 1, 10, metadata='regular3', options=overflow, power=0.5)"); + "regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=0.5)"); test(axis::regular>(2, 1, 10, "regular4", axis::option_type::none, -0.5), - "regular_pow(2, 1, 10, metadata='regular4', options=none, power=-0.5)"); - test(axis::circular<>(4, 0.1, 1.0, "polar"), - "circular(4, 0.1, 1.1, metadata='polar', options=overflow)"); + "regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-0.5)"); + test(axis::circular(4, 0.1, 1.0), + "circular(4, 0.1, 1.1, options=overflow)"); test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none), - "variable(-1, 0, 1, metadata='variable', options=none)"); + "variable(-1, 0, 1, metadata=\"variable\", options=none)"); test(axis::category<>({0, 1, 2}, "category"), - "category(0, 1, 2, metadata='category', options=overflow)"); + "category(0, 1, 2, metadata=\"category\", options=overflow)"); test(axis::category({"A", "B"}, "category2"), - "category('A', 'B', metadata='category2', options=overflow)"); - test(axis::integer<>(-1, 1, "integer", axis::option_type::none), - "integer(-1, 1, metadata='integer', options=none)"); + "category(\"A\", \"B\", metadata=\"category2\", options=overflow)"); + test(axis::integer(-1, 1, {}, axis::option_type::none), + "integer(-1, 1, metadata=main::user_defined, options=none)"); } // axis::variant support for minimal_axis @@ -386,7 +388,7 @@ int main() { BOOST_TEST_EQ(axis.size(), 1); BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); BOOST_TEST_THROWS(axis.lower(0), std::runtime_error); - BOOST_TEST_TRAIT_TRUE((std::is_same)); + BOOST_TEST_TRAIT_TRUE((std::is_same)); } // bin_type streamable @@ -394,7 +396,7 @@ int main() { auto test = [](const auto& x, const char* ref) { std::ostringstream os; os << x; - BOOST_TEST_EQ(os.str(), ref); + BOOST_TEST_EQ(os.str(), std::string(ref)); }; auto a = axis::regular<>(2, 0, 1); diff --git a/test/detail_test.cpp b/test/detail_test.cpp index dfb90e69..d22caa1a 100644 --- a/test/detail_test.cpp +++ b/test/detail_test.cpp @@ -5,39 +5,12 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include #include -#include -#include -#include "utility.hpp" -namespace bhd = boost::histogram::detail; -namespace bhad = boost::histogram::axis::detail; +using namespace boost::histogram::detail; int main() { - // escape0 - { - std::ostringstream os; - bhad::escape_string(os, std::string("abc")); - BOOST_TEST_EQ(os.str(), std::string("'abc'")); - } - - // escape1 - { - std::ostringstream os; - bhad::escape_string(os, std::string("abc\n")); - BOOST_TEST_EQ(os.str(), std::string("'abc\n'")); - } - - // escape2 - { - std::ostringstream os; - bhad::escape_string(os, std::string("'abc'")); - BOOST_TEST_EQ(os.str(), std::string("'\\\'abc\\\''")); - } - - // cat - { BOOST_TEST_EQ(bhd::cat("foo", 1, "bar"), std::string("foo1bar")); } + BOOST_TEST_EQ(cat("foo", 1, "bar"), "foo1bar"); return boost::report_errors(); } diff --git a/test/histogram_dynamic_add_fail.cpp b/test/histogram_dynamic_add_fail.cpp index 741e6647..d54405db 100644 --- a/test/histogram_dynamic_add_fail.cpp +++ b/test/histogram_dynamic_add_fail.cpp @@ -5,10 +5,17 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include using namespace boost::histogram; int main() { - auto a = make_dynamic_histogram(axis::integer<>(0, 2)); - auto b = make_dynamic_histogram(axis::integer<>(0, 3)); + auto va = std::vector>>(); + va.push_back(axis::integer<>(0, 2)); + auto a = make_histogram(va); + + auto vb = std::vector>>(); + vb.push_back(axis::integer<>(0, 3)); + auto b = make_histogram(vb); + a += b; } diff --git a/test/histogram_dynamic_at_tuple_wrong_dimension_fail.cpp b/test/histogram_dynamic_at_tuple_wrong_dimension_fail.cpp index 0ed96e26..c917276b 100644 --- a/test/histogram_dynamic_at_tuple_wrong_dimension_fail.cpp +++ b/test/histogram_dynamic_at_tuple_wrong_dimension_fail.cpp @@ -6,9 +6,12 @@ #include #include +#include using namespace boost::histogram; int main() { - auto h = make_dynamic_histogram(axis::integer<>(0, 2)); + auto v = std::vector>>(); + v.push_back(axis::integer<>(0, 2)); + auto h = make_histogram(v); h.at(std::make_pair(0, 0)); } diff --git a/test/histogram_dynamic_at_vector_wrong_dimension_fail.cpp b/test/histogram_dynamic_at_vector_wrong_dimension_fail.cpp index 9c551867..6e0779ad 100644 --- a/test/histogram_dynamic_at_vector_wrong_dimension_fail.cpp +++ b/test/histogram_dynamic_at_vector_wrong_dimension_fail.cpp @@ -9,6 +9,8 @@ using namespace boost::histogram; int main() { - auto h = make_dynamic_histogram(axis::integer<>(0, 2)); + auto v = std::vector>>(); + v.push_back(axis::integer<>(0, 2)); + auto h = make_histogram(v); h.at(std::vector({0, 0})); } diff --git a/test/histogram_dynamic_at_wrong_dimension_fail.cpp b/test/histogram_dynamic_at_wrong_dimension_fail.cpp index 49bd9902..24f3d546 100644 --- a/test/histogram_dynamic_at_wrong_dimension_fail.cpp +++ b/test/histogram_dynamic_at_wrong_dimension_fail.cpp @@ -5,9 +5,12 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include using namespace boost::histogram; int main() { - auto h = make_dynamic_histogram(axis::integer<>(0, 2)); + auto v = std::vector>>(); + v.push_back(axis::integer<>(0, 2)); + auto h = make_histogram(v); h.at(0, 0); } diff --git a/test/histogram_dynamic_reduce_wrong_order_fail.cpp b/test/histogram_dynamic_reduce_wrong_order_fail.cpp index 201f5ccf..1a180cea 100644 --- a/test/histogram_dynamic_reduce_wrong_order_fail.cpp +++ b/test/histogram_dynamic_reduce_wrong_order_fail.cpp @@ -5,10 +5,14 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include using namespace boost::histogram; int main() { - auto h = make_dynamic_histogram(axis::integer<>(0, 1), axis::integer<>(1, 2)); + auto a = std::vector>>(); + a.push_back(axis::integer<>(0, 1)); + a.push_back(axis::integer<>(1, 2)); + auto h = make_histogram(a); auto v = {0, 0}; h.reduce_to(v.begin(), v.end()); } diff --git a/test/histogram_dynamic_test.cpp b/test/histogram_dynamic_test.cpp index ddcbfd8b..b15e00a4 100644 --- a/test/histogram_dynamic_test.cpp +++ b/test/histogram_dynamic_test.cpp @@ -30,36 +30,36 @@ int main() { // init { - auto v = std::vector, axis::integer<>>>(); + auto v = std::vector, axis::integer<>>>(); v.push_back(axis::regular<>(4, -1, 1)); v.push_back(axis::integer<>(1, 7)); - auto h = make_dynamic_histogram(v.begin(), v.end()); - BOOST_TEST_EQ(h.dim(), 2); + auto h = make_histogram(v.begin(), v.end()); + BOOST_TEST_EQ(h.rank(), 2); BOOST_TEST_EQ(h.axis(0), v[0]); BOOST_TEST_EQ(h.axis(1), v[1]); - auto h2 = make_dynamic_histogram_with(array_storage(), v.begin(), v.end()); - BOOST_TEST_EQ(h2.dim(), 2); + auto h2 = make_histogram_with(array_storage(), v.begin(), v.end()); + BOOST_TEST_EQ(h2.rank(), 2); BOOST_TEST_EQ(h2.axis(0), v[0]); BOOST_TEST_EQ(h2.axis(1), v[1]); } // bad fill argument { - auto h = make_dynamic_histogram(axis::integer<>(0, 3)); + auto h = make(dynamic_tag(), axis::integer<>(0, 3)); BOOST_TEST_THROWS(h(std::string()), std::invalid_argument); } // axis methods { enum { A, B }; - auto c = make_dynamic_histogram(axis::category<>({A, B})); + auto c = make(dynamic_tag(), axis::category<>({A, B})); BOOST_TEST_THROWS(c.axis().lower(0), std::runtime_error); } // reduce { - auto h1 = make_dynamic_histogram(axis::integer<>(0, 2), axis::integer<>(0, 3)); + auto h1 = make(dynamic_tag(), axis::integer<>(0, 2), axis::integer<>(0, 3)); h1(0, 0); h1(0, 1); h1(1, 0); @@ -70,7 +70,7 @@ int main() { x = {0}; auto h1_0 = h1.reduce_to(x.begin(), x.end()); - BOOST_TEST_EQ(h1_0.dim(), 1); + BOOST_TEST_EQ(h1_0.rank(), 1); BOOST_TEST_EQ(sum(h1_0), 5); BOOST_TEST_EQ(h1_0.at(0), 2); BOOST_TEST_EQ(h1_0.at(1), 3); @@ -78,7 +78,7 @@ int main() { x = {1}; auto h1_1 = h1.reduce_to(x.begin(), x.end()); - BOOST_TEST_EQ(h1_1.dim(), 1); + BOOST_TEST_EQ(h1_1.rank(), 1); BOOST_TEST_EQ(sum(h1_1), 5); BOOST_TEST_EQ(h1_1.at(0), 2); BOOST_TEST_EQ(h1_1.at(1), 2); @@ -88,13 +88,13 @@ int main() { // histogram iterator { - auto h = make_dynamic_histogram(axis::integer<>(0, 3)); + auto h = make_s(dynamic_tag(), array_storage>(), axis::integer<>(0, 3)); const auto& a = h.axis(); h(weight(2), 0); h(1); h(1); auto it = h.begin(); - BOOST_TEST_EQ(it.dim(), 1); + BOOST_TEST_EQ(it.rank(), 1); BOOST_TEST_EQ(it.idx(0), 0); BOOST_TEST_EQ(it.bin(0), a[0]); diff --git a/test/histogram_static_at_vector_wrong_dimension_fail.cpp b/test/histogram_static_at_vector_wrong_dimension_fail.cpp index 8ee5ae05..9cdb7396 100644 --- a/test/histogram_static_at_vector_wrong_dimension_fail.cpp +++ b/test/histogram_static_at_vector_wrong_dimension_fail.cpp @@ -5,6 +5,8 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include + using namespace boost::histogram; int main() { diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 587b71ae..51ad13bc 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -62,37 +62,36 @@ void run_tests() { // init_3 { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, - axis::circular<>{3}); + axis::circular<>{2}); BOOST_TEST_EQ(h.rank(), 3); - BOOST_TEST_EQ(h.size(), 75); + BOOST_TEST_EQ(h.size(), 5 * 5 * 3); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, - axis::integer<>{-1, 2}, axis::circular<>{3}); + axis::integer<>{-1, 2}, axis::circular<>{2}); BOOST_TEST_EQ(h2, h); } // init_4 { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, - axis::circular<>{3}, axis::variable<>{-1, 0, 1}); + axis::circular<>{2}, axis::variable<>{-1, 0, 1}); BOOST_TEST_EQ(h.rank(), 4); - BOOST_TEST_EQ(h.size(), 300); + BOOST_TEST_EQ(h.size(), 5 * 5 * 3 * 4); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, - axis::integer<>{-1, 2}, axis::circular<>{3}, axis::variable<>{-1, 0, 1}); + axis::integer<>{-1, 2}, axis::circular<>{2}, axis::variable<>{-1, 0, 1}); BOOST_TEST_EQ(h2, h); } // init_5 { - enum { A, B, C }; auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, - axis::circular<>{3}, axis::variable<>{-1, 0, 1}, - axis::category<>{{A, B, C}}); + axis::circular<>{2}, axis::variable<>{-1, 0, 1}, + axis::category<>{{3, 1, 2}}); BOOST_TEST_EQ(h.rank(), 5); - BOOST_TEST_EQ(h.size(), 1200); + BOOST_TEST_EQ(h.size(), 5 * 5 * 3 * 4 * 4); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, - axis::integer<>{-1, 2}, axis::circular<>{3}, - axis::variable<>{-1, 0, 1}, axis::category<>{{A, B, C}}); + axis::integer<>{-1, 2}, axis::circular<>{2}, + axis::variable<>{-1, 0, 1}, axis::category<>{{3, 1, 2}}); BOOST_TEST_EQ(h2, h); } @@ -162,7 +161,7 @@ void run_tests() { BOOST_TEST_EQ(b.axis(0_c)[0].upper(), 2); BOOST_TEST_EQ(b.axis(1_c).size(), 2); BOOST_TEST_EQ(b.axis(1_c)[0].lower(), 1); - BOOST_TEST_EQ(b.axis(1_c)[0].upper(), 3); + BOOST_TEST_EQ(b.axis(1_c)[0].upper(), 2); b.axis(1_c).metadata() = "bar"; BOOST_TEST_EQ(b.axis(0_c).metadata(), "foo"); BOOST_TEST_EQ(b.axis(1_c).metadata(), "bar"); @@ -502,10 +501,12 @@ void run_tests() { std::ostringstream os; os << a; BOOST_TEST_EQ(os.str(), - "histogram(" - "\n regular(3, -1, 1, metadata='r', options=underflow_and_overflow)," - "\n integer(0, 2, metadata='i', options=underflow_and_overflow)," - "\n)"); + std::string( + "histogram(\n" + " regular(3, -1, 1, metadata=\"r\", options=underflow_and_overflow),\n" + " integer(0, 2, metadata=\"i\", options=underflow_and_overflow),\n" + ")" + )); } // histogram_reset @@ -810,18 +811,18 @@ void run_tests() { { tracing_allocator a(db); auto h = make_s(Tag(), array_storage>(a), - axis::integer<>(0, 1024)); + axis::integer<>(0, 1000)); h(0); } // int allocation for array_storage BOOST_TEST_EQ(db[typeid(int)].first, db[typeid(int)].second); - BOOST_TEST_GE(db[typeid(int)].first, 1024u); + BOOST_TEST_EQ(db[typeid(int)].first, 1002u); if (Tag()) { // axis::variant allocation, only for dynamic histogram using T = axis::variant>; BOOST_TEST_EQ(db[typeid(T)].first, db[typeid(T)].second); - BOOST_TEST_GE(db[typeid(T)].first, 1u); + BOOST_TEST_LE(db[typeid(T)].first, 1u); // zero if vector uses small-vector-optimisation } } } diff --git a/test/meta_test.cpp b/test/meta_test.cpp index 6e3e5c1f..c738f52e 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -173,7 +173,7 @@ int main() { auto j = {0, 1}; using J = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + BOOST_TEST_TRAIT_TRUE((std::is_same)); } // bool mask diff --git a/test/utility.hpp b/test/utility.hpp index a32981d1..6f8555bc 100644 --- a/test/utility.hpp +++ b/test/utility.hpp @@ -25,7 +25,7 @@ using i2 = boost::mp11::mp_size_t<2>; using i3 = boost::mp11::mp_size_t<3>; namespace std { -// never add to std, we only do it to get ADL working :( +// never add to std, we only do it here to get ADL working :( template ostream& operator<<(ostream& os, const vector& v) { os << "[ "; @@ -34,20 +34,10 @@ ostream& operator<<(ostream& os, const vector& v) { return os; } -namespace detail { - struct ostreamer { - ostream& os; - template - void operator()(const T& t) const { - os << t << " "; - } - }; -} - template ostream& operator<<(ostream& os, const tuple& t) { os << "[ "; - ::boost::mp11::tuple_for_each(t, detail::ostreamer{os}); + ::boost::mp11::tuple_for_each(t, [&os](const auto& x) { os << x << " "; }); os << "]"; return os; } From 0edf02fb5216cc4dcf90e0fb9430ab956b3329b5 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Tue, 23 Oct 2018 10:29:12 +0200 Subject: [PATCH 07/44] replaced overload with static_if --- .../histogram/axis/ostream_operators.hpp | 9 ++-- include/boost/histogram/axis/traits.hpp | 25 ++++------ include/boost/histogram/axis/variant.hpp | 47 +++++++++---------- include/boost/histogram/detail/meta.hpp | 22 +++++---- test/meta_test.cpp | 13 +++++ 5 files changed, 62 insertions(+), 54 deletions(-) diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 908cd057..9c7287cc 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -31,19 +31,18 @@ template const char* to_string(const axis::transform::q template void stream_metadata(OStream& os, const T& t) { - detail::overload( - [](std::true_type, OStream& os, const auto& t) { + detail::static_if>( + [&os](const auto& t) { std::ostringstream oss; oss << t; if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); } }, - [](std::false_type, OStream& os, const T&) { + [&os](const auto&) { using U = detail::rm_cvref; os << ", metadata=" << boost::core::demangled_name( BOOST_CORE_TYPEID(U) ); - } - )(detail::is_streamable(), os, t); + }, t); } template diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index 1c9937db..a7034233 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -12,16 +12,6 @@ namespace boost { namespace histogram { -namespace detail { - template - decltype(auto) metadata_impl(std::true_type, T&& x) { return x.metadata(); } - - template - copy_qualifiers metadata_impl(std::false_type, T&&) { - static axis::empty_metadata_type m; - return m; - } -} namespace axis { namespace traits { template @@ -29,15 +19,20 @@ namespace traits { template decltype(auto) metadata(T&& t) { - return detail::metadata_impl(detail::has_method_metadata>(), t); + return detail::static_if>( + [](auto&& x) -> decltype(auto) { return x.metadata(); }, + [](auto&&) -> detail::copy_qualifiers { + static axis::empty_metadata_type m; return m; + }, + t); } template option_type options(const T& t) { - return detail::overload( - [](std::true_type, const auto& x) { return x.options(); }, - [](std::false_type, const T&) { return axis::option_type::none; } - )(detail::has_method_options(), t); + return detail::static_if>( + [](const auto& x) { return x.options(); }, + [](const T&) { return axis::option_type::none; }, + t); } template diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 4de3a40b..9ea698e9 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -109,16 +109,15 @@ public: variant& operator=(const variant& u) { visit([this](const auto& u) { using U = detail::rm_cvref; - detail::overload( - [this](std::true_type, const auto& u) { this->operator=(u); }, - [](std::false_type, const auto&) { + detail::static_if>( + [this](const auto& u) { this->operator=(u); }, + [](const auto&) { throw std::runtime_error(detail::cat( boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), " is not a bounded type of ", boost::core::demangled_name( BOOST_CORE_TYPEID(variant) ) )); - } - )(mp11::mp_contains(), u); + }, u); }, u); return *this; } @@ -134,34 +133,32 @@ public: const metadata_type& metadata() const { return visit([](const auto& x) -> const metadata_type& { using U = decltype(traits::metadata(x)); - return detail::overload( - [](std::true_type, const auto& x) -> const metadata_type& { return traits::metadata(x); }, - [](std::false_type, const auto&) -> const metadata_type& { + return detail::static_if>( + [](const auto& x) -> const metadata_type& { return traits::metadata(x); }, + [](const auto&) -> const metadata_type& { throw std::runtime_error(detail::cat( "cannot return metadata of type ", boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), " through axis::variant interface which uses type ", boost::core::demangled_name( BOOST_CORE_TYPEID(const metadata_type&) ) )); - } - )(std::is_same(), x); + }, x); }, *this); } metadata_type& metadata() { return visit([](auto& x) -> metadata_type& { using U = decltype(traits::metadata(x)); - return detail::overload( - [](std::true_type, auto& x) -> metadata_type& { return traits::metadata(x); }, - [](std::false_type, auto&) -> metadata_type& { + return detail::static_if>( + [](auto& x) -> metadata_type& { return traits::metadata(x); }, + [](auto&) -> metadata_type& { throw std::runtime_error(detail::cat( "cannot return metadata of type ", - boost::core::demangled_name( BOOST_CORE_TYPEID(mp11::mp_identity) ), + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), " through axis::variant interface which uses type ", boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type&) ) )); - } - )(std::is_same(), x); + }, x); }, *this); } @@ -176,14 +173,13 @@ public: double lower(int idx) const { return visit([idx](const auto& x) { using T = detail::rm_cvref; - return detail::overload( - [idx](std::true_type, const auto& x) -> double { return x.lower(idx); }, - [](std::false_type, const auto&) -> double { + return detail::static_if>( + [idx](const auto& x) -> double { return x.lower(idx); }, + [](const auto&) -> double { throw std::runtime_error(detail::cat( boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), " has no lower method")); - } - )(detail::has_method_lower(), x); + }, x); }, *this); } @@ -252,14 +248,13 @@ std::basic_ostream& operator<<(std::basic_ostream& { visit([&os](const auto& x) { using T = detail::rm_cvref; - detail::overload( - [&os](std::true_type, const auto& x) { os << x; }, - [](std::false_type, const auto&) { + detail::static_if>( + [&os](const auto& x) { os << x; }, + [](const auto&) { throw std::runtime_error(detail::cat( boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), " is not streamable")); - } - )(detail::is_streamable(), x); + }, x); }, v); return os; } diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index cc5841f6..d0e61013 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -67,15 +67,21 @@ using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size using visitor_return_type = decltype(std::declval()(std::declval>>())); -template -struct overload_type : T1, T2 { - overload_type(T1 t1, T2 t2) : T1(t1), T2(t2) {} - using T1::operator(); - using T2::operator(); -}; +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 -overload_type overload(T1 t1, T2 t2) { return overload_type(t1, t2); } +template +constexpr decltype(auto) static_if(Ts&&... ts) +{ + return static_if_c(std::forward(ts)...); +} #define BOOST_HISTOGRAM_MAKE_SFINAE(name, cond) \ template \ diff --git a/test/meta_test.cpp b/test/meta_test.cpp index c738f52e..a478e113 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -280,6 +280,19 @@ int main() { BOOST_TEST_TRAIT_TRUE((std::is_same, const long&>)); } + // static_if + { + struct callable { int operator()() { return 1; }; }; + struct not_callable {}; + auto fcn = [](auto b, auto x) { + return static_if( + [](auto x) { return x(); }, + [](auto) { return 2; }, x); + }; + BOOST_TEST_EQ(fcn(std::true_type(), callable()), 1); + BOOST_TEST_EQ(fcn(std::false_type(), not_callable()), 2); + } + // is_axis_vector { using A = std::vector>; From e808a4f2369b239fc53db2366ba6f8c79c23c851 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Tue, 23 Oct 2018 23:01:57 +0200 Subject: [PATCH 08/44] fixing serialization --- .gitignore | 1 + CMakeLists.txt | 20 +- examples/guide_histogram_serialization.cpp | 13 +- histogram.sublime-project | 15 +- include/boost/histogram/axis/base.hpp | 19 +- include/boost/histogram/axis/types.hpp | 4 +- include/boost/histogram/axis/variant.hpp | 2 +- include/boost/histogram/detail/meta.hpp | 5 +- include/boost/histogram/histogram.hpp | 12 +- include/boost/histogram/serialization.hpp | 222 +++++++++--------- .../histogram/storage/adaptive_storage.hpp | 117 ++++----- test/adaptive_storage_serialization_test.cpp | 2 +- test/histogram_serialization_test.cpp | 12 +- 13 files changed, 204 insertions(+), 240 deletions(-) diff --git a/.gitignore b/.gitignore index ce5e62b5..37d22078 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *build* +histogram.sublime-workspace CMakeSettings.json doc/html/.buildinfo doc/html/.doctrees diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ca96c16..24946d5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,6 @@ set(MIN_BOOST_VERSION 1.66) # setup build option(BUILD_BENCHMARKS "Build benchmarks" OFF) option(TEST_SERIALIZATION "Test serialization code" OFF) -mark_as_advanced(BUILD_BENCHMARKS) -mark_as_advanced(TEST_SERIALIZATION) # serialization is optional if (TEST_SERIALIZATION) @@ -63,14 +61,13 @@ function(compiled_test SRC) -Wno-unused-local-typedef -D__STRICT_ANSI__) endif() - ## deactivate sanitizers for now, LeakSanitizer is crashing - # # activate sanitizers for clang builds - # if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # target_compile_options(${BASENAME} PRIVATE - # -fsanitize=address,undefined - # -fno-omit-frame-pointer) - # target_link_libraries(${BASENAME} PRIVATE -fsanitize=address,undefined) - # endif() + # activate sanitizers for clang builds + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(${BASENAME} PRIVATE + -fsanitize=address,undefined + -fno-omit-frame-pointer) + target_link_libraries(${BASENAME} PRIVATE -fsanitize=address,undefined) + endif() if (${BASENAME} MATCHES "_fail") add_test(${BASENAME} python ${PROJECT_SOURCE_DIR}/tools/pass_on_fail.py ${BASENAME}) @@ -128,9 +125,6 @@ if (TEST_SERIALIZATION) endif() if (BUILD_BENCHMARKS) - if (NOT ${CMAKE_BUILD_TYPE} EQUAL Release) - message(WARNING "Benchmarks should be build in Release mode") - endif() add_executable(speed_cpp test/speed_cpp.cpp) target_include_directories(speed_cpp PRIVATE include ${Boost_INCLUDE_DIR}) target_compile_definitions(speed_cpp PRIVATE -DBOOST_DISABLE_ASSERTS) diff --git a/examples/guide_histogram_serialization.cpp b/examples/guide_histogram_serialization.cpp index 7f6faffe..6271d5d0 100644 --- a/examples/guide_histogram_serialization.cpp +++ b/examples/guide_histogram_serialization.cpp @@ -5,15 +5,16 @@ #include #include // includes serialization code #include +#include namespace bh = boost::histogram; int main() { - auto a = bh::make_static_histogram(bh::axis::regular<>(3, -1, 1, "r"), - bh::axis::integer<>(0, 2, "i")); + auto a = bh::make_histogram(bh::axis::regular<>(3, -1, 1, "axis 0"), + bh::axis::integer<>(0, 2, "axis 1")); a(0.5, 1); - std::string buf; // holds persistent representation + std::string buf; // to hold persistent representation // store histogram { @@ -25,8 +26,7 @@ int main() { auto b = decltype(a)(); // create a default-constructed second histogram - std::cout << "before restore " << (a == b) << std::endl; - // prints: before restore 0 + assert(b != a); // b is empty, a is not // load histogram { @@ -35,8 +35,7 @@ int main() { ia >> b; } - std::cout << "after restore " << (a == b) << std::endl; - // prints: after restore 1 + assert(b == a); // now b is equal to a } //] diff --git a/histogram.sublime-project b/histogram.sublime-project index ae199ae8..c4e4d7aa 100644 --- a/histogram.sublime-project +++ b/histogram.sublime-project @@ -10,6 +10,19 @@ "ClangFormat": { "format_on_save": true + }, + + "trim_trailing_white_space_on_save": false, + "ensure_newline_at_eof_on_save": false, + }, + "build_systems": + [ + { + "name": "CMake", + "working_dir": "${project_path}/build", + "cmd": ["{ [ ! -e Makefile ] && cmake .. ; } && make -j4 && ctest"], + "shell": true, + "file_regex": "/([^/:]+):(\\d+):(\\d+): ", } - } + ] } diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index 5190c822..78b5c3c9 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -52,15 +52,10 @@ class base bool operator==(const data& rhs) const noexcept { return size == rhs.size && opt == rhs.opt && - equal_impl(detail::is_equal_comparable(), rhs); - } - - bool equal_impl(std::true_type, const metadata_type& rhs) const noexcept { - return static_cast(*this) == rhs; - } - - bool equal_impl(std::false_type, const metadata_type&) const noexcept { - return true; + detail::static_if>( + [&rhs](const auto& m) { return m == rhs; }, + [](const auto&) { return true; }, + static_cast(*this)); } }; @@ -74,9 +69,6 @@ public: /// Returns the metadata (const version). const metadata_type& metadata() const noexcept { return static_cast(data_); } - template - void serialize(Archive&, unsigned); - friend void swap(base& a, base& b) noexcept // ADL works with friend functions { std::swap(static_cast(a), static_cast(b)); @@ -84,6 +76,9 @@ public: std::swap(a.data_.opt, b.data_.opt); } + template + void serialize(Archive&, unsigned); + protected: base(unsigned size, metadata_type&& m, option_type opt) : data_(std::move(m), size, opt) diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index e224cec0..553371ae 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -35,7 +35,7 @@ struct identity { constexpr bool operator==(const identity&) const noexcept { return true; } template - void serialize(Archive&, unsigned) {} + void serialize(Archive&, unsigned) {} // noop }; template @@ -273,7 +273,9 @@ class variable : public base, struct data : allocator_type // empty base class optimization { typename std::allocator_traits::pointer x = nullptr; + using allocator_type::allocator_type; + data(const allocator_type& a) : allocator_type(a) {} data() = default; diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 9ea698e9..03e0b56d 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -209,7 +209,7 @@ public: } template - void serialize(Archive&, unsigned); + void serialize(Archive& ar, unsigned); template friend auto visit(Functor&& f, Variant&& v) diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index d0e61013..fa6b49d6 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -145,10 +145,7 @@ using is_axis_variant = typename is_axis_variant_impl::type; template using is_axis_vector = mp11::mp_all< is_random_access_container, - mp11::mp_any< - is_axis_variant>>, - is_axis>> - > + is_axis>> >; struct static_container_tag {}; diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index c97b22ac..f7bd21be 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -105,28 +105,28 @@ public: /// Get N-th axis (const version) template - auto axis(mp11::mp_size_t) const -> const detail::axis_at& { + decltype(auto) axis(mp11::mp_size_t) const { detail::range_check(axes_); return detail::axis_get(axes_); } /// Get N-th axis template - auto axis(mp11::mp_size_t) -> detail::axis_at& { + decltype(auto) axis(mp11::mp_size_t) { detail::range_check(axes_); return detail::axis_get(axes_); } /// Get first axis (convenience for 1-d histograms, const version) - auto axis() const -> const detail::axis_at& { return axis(mp11::mp_size_t<0>()); } + decltype(auto) axis() const { return axis(mp11::mp_size_t<0>()); } /// Get first axis (convenience for 1-d histograms) - auto axis() -> detail::axis_at& { return axis(mp11::mp_size_t<0>()); } + decltype(auto) axis() { return axis(mp11::mp_size_t<0>()); } /// Get N-th axis with runtime index (const version) template > - auto axis(std::size_t i) const -> const detail::container_element_type& { + decltype(auto) axis(std::size_t i) const { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; } @@ -134,7 +134,7 @@ public: /// Get N-th axis with runtime index template > - auto axis(std::size_t i) -> detail::container_element_type& { + decltype(auto) axis(std::size_t i) { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; } diff --git a/include/boost/histogram/serialization.hpp b/include/boost/histogram/serialization.hpp index 05812313..431a903d 100644 --- a/include/boost/histogram/serialization.hpp +++ b/include/boost/histogram/serialization.hpp @@ -8,57 +8,48 @@ #define BOOST_HISTOGRAM_SERIALIZATION_HPP #include -#include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include +#include /** \file boost/histogram/serialization.hpp * \brief Defines the serialization functions, to use with boost.serialize. * */ +namespace std { +template +void serialize(Archive& ar, tuple& t, unsigned /* version */) { + boost::mp11::tuple_for_each(t, [&ar](auto& x) { ar& x; }); +} +} // namespace std + namespace boost { +namespace container { +template +void serialize(Archive& ar, string& s, unsigned /* version */) { + auto size = s.size(); + ar& size; + if (Archive::is_loading::value) { + s.resize(size); + } + ar& boost::serialization::make_array(s.data(), size); +} +} // namespace container + namespace histogram { - -namespace detail { -template -struct serialize_t { - Archive& ar_; - explicit serialize_t(Archive& ar) : ar_(ar) {} - template - void operator()(T& t) const { - ar_& t; - } -}; - -struct serializer { - template - void operator()(T*, Buffer& b, Archive& ar) { - if (Archive::is_loading::value) { - // precondition: buffer is destroyed - b.set(b.template create()); - } - ar& boost::serialization::make_array(reinterpret_cast(b.ptr), b.size); - } - - template - void operator()(void*, Buffer& b, Archive&) { - if (Archive::is_loading::value) { b.ptr = nullptr; } - } -}; - -} // namespace detail - template template void weight_counter::serialize(Archive& ar, unsigned /* version */) { @@ -71,123 +62,124 @@ void serialize(Archive& ar, array_storage& s, unsigned /* version */) { ar& s.buffer; } +namespace detail { +struct serializer { + template + void operator()(T*, Buffer& b, Archive& ar) { + if (Archive::is_loading::value) { + // precondition: buffer is destroyed + b.set(b.template create()); + } + ar& boost::serialization::make_array(reinterpret_cast(b.ptr), b.size); + } + + template + void operator()(void*, Buffer& b, Archive&) { + if (Archive::is_loading::value) { + b.ptr = nullptr; + } + } +}; +} // namespace detail + template void serialize(Archive& ar, adaptive_storage& s, unsigned /* version */) { using S = adaptive_storage; - if (Archive::is_loading::value) { S::apply(typename S::destroyer(), s.buffer); } + if (Archive::is_loading::value) { + S::apply(typename S::destroyer(), s.buffer); + } ar& s.buffer.type; ar& s.buffer.size; S::apply(detail::serializer(), s.buffer, ar); } +template +template +void histogram::serialize(Archive& ar, unsigned /* version */) { + ar& axes_; + ar& storage_; +} + namespace axis { - template -void base::serialize(Archive& ar, unsigned /* version */) { - ar& size_; - ar& shape_; +void serialize(Archive&, empty_metadata_type&, unsigned /* version */) { +} // noop + +template +template +void base::serialize(Archive& ar, unsigned /* version */) { + ar& metadata(); + ar& data_.size; + ar& data_.opt; } -template +template template -void labeled_base::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object(*this); - auto size = label_.size(); - ar& size; - if (Archive::is_loading::value) { label_.resize(size); } - ar& serialization::make_array(label_.data(), size); -} - -namespace transform { -template -void pow::serialize(Archive& ar, unsigned /* version */) { +void transform::pow::serialize(Archive& ar, unsigned /* version */) { ar& power; } -} // namespace transform -template +template template -void regular::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object>(*this); - ar& boost::serialization::base_object(*this); - ar& min_; +void transform::quantity::serialize(Archive& ar, unsigned /* version */) { + ar& unit; +} + +template +template +void regular::serialize(Archive& ar, unsigned /* version */) { + ar& boost::serialization::base_object(*this); + ar& boost::serialization::base_object(data_); + ar& data_.min; + ar& data_.delta; +} + +template +template +void circular::serialize(Archive& ar, unsigned /* version */) { + ar& boost::serialization::base_object(*this); + ar& phase_; ar& delta_; } -template +template template -void circular::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object>(*this); - ar& phase_; - ar& perimeter_; +void variable::serialize(Archive& ar, unsigned /* version */) { + // destroy must happen before base serialization with old size + if (Archive::is_loading::value) detail::destroy_buffer(data_, data_.x, nx()); + ar& boost::serialization::base_object(*this); + if (Archive::is_loading::value) + data_.x = boost::histogram::detail::create_buffer(data_, nx()); + ar& boost::serialization::make_array(data_.x, nx()); } -template +template template -void variable::serialize(Archive& ar, unsigned /* version */) { - if (Archive::is_loading::value) { this->~variable(); } - - ar& boost::serialization::base_object>(*this); - - if (Archive::is_loading::value) { - value_allocator_type a(base_type::get_allocator()); - x_ = boost::histogram::detail::create_buffer(a, nx()); - } - - ar& boost::serialization::make_array(x_, base_type::size() + 1); -} - -template -template -void integer::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object>(*this); +void integer::serialize(Archive& ar, unsigned /* version */) { + ar& boost::serialization::base_object(*this); ar& min_; } -template +template template -void category::serialize(Archive& ar, unsigned /* version */) { - if (Archive::is_loading::value) { this->~category(); } - - ar& boost::serialization::base_object>(*this); - - if (Archive::is_loading::value) { - value_allocator_type a(base_type::get_allocator()); - x_ = boost::histogram::detail::create_buffer(a, nx()); - } - - ar& boost::serialization::make_array(x_, base_type::size()); +void category::serialize(Archive& ar, unsigned /* version */) { + // destroy must happen before base serialization with old size + if (Archive::is_loading::value) + detail::destroy_buffer(data_, data_.x, base_type::size()); + ar& boost::serialization::base_object(*this); + if (Archive::is_loading::value) + data_.x = boost::histogram::detail::create_buffer(data_, base_type::size()); + ar& boost::serialization::make_array(data_.x, base_type::size()); } template template -void any::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object>(*this); +void variant::serialize(Archive& ar, unsigned /* version */) { + ar& static_cast(*this); } +} // namespace axis -} // namespace axis - -namespace { -template -void serialize_axes(Archive& ar, std::tuple& axes) { - detail::serialize_t sh(ar); - mp11::tuple_for_each(axes, sh); -} - -template -void serialize_axes(Archive& ar, std::vector& axes) { - ar& axes; -} -} - -template -template -void histogram::serialize(Archive& ar, unsigned /* version */) { - serialize_axes(ar, axes_); - ar& storage_; -} - -} // namespace histogram -} // namespace boost +} // namespace histogram +} // namespace boost #endif diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index 342f2382..fdcdc45a 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -156,72 +156,43 @@ struct adaptive_storage { type = type_index(); ptr = p; } - - template - auto apply(F&& f, Ts&&... ts) - -> decltype(f(ptr, *this, std::forward(ts)...)) - { - // this is intentionally not a switch, the if-chain is faster in benchmarks - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - // b.type == 0 is intentionally the last in the chain, - // because it is rarely triggered - return f(ptr, *this, std::forward(ts)...); - } - - template - auto apply(F&& f, Ts&&... ts) const - -> decltype(f(ptr, *this, std::forward(ts)...)) - { - // this is intentionally not a switch, the if-chain is faster in benchmarks - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - if (type == type_index()) - return f(reinterpret_cast(ptr), *this, - std::forward(ts)...); - // b.type == 0 is intentionally the last in the chain, - // because it is rarely triggered - return f(ptr, *this, std::forward(ts)...); - } }; - ~adaptive_storage() { buffer.apply(destroyer()); } + template + static decltype(auto) apply(F&& f, B&& b, Ts&&... ts) + { + // this is intentionally not a switch, the if-chain is faster in benchmarks + if (b.type == type_index()) + return f(reinterpret_cast(b.ptr), b, + std::forward(ts)...); + if (b.type == type_index()) + return f(reinterpret_cast(b.ptr), b, + std::forward(ts)...); + if (b.type == type_index()) + return f(reinterpret_cast(b.ptr), b, + std::forward(ts)...); + if (b.type == type_index()) + return f(reinterpret_cast(b.ptr), b, + std::forward(ts)...); + if (b.type == type_index()) + return f(reinterpret_cast(b.ptr), b, + std::forward(ts)...); + if (b.type == type_index()) + return f(reinterpret_cast(b.ptr), b, + std::forward(ts)...); + // b.type == 0 is intentionally the last in the chain, + // because it is rarely triggered + return f(b.ptr, b, std::forward(ts)...); + } + + ~adaptive_storage() { apply(destroyer(), buffer); } adaptive_storage(const adaptive_storage& o) { - o.buffer.apply(replacer(), buffer); + apply(replacer(), o.buffer, buffer); } adaptive_storage& operator=(const adaptive_storage& o) { - if (this != &o) { o.buffer.apply(replacer(), buffer); } + if (this != &o) { apply(replacer(), o.buffer, buffer); } return *this; } @@ -248,7 +219,7 @@ struct adaptive_storage { template > adaptive_storage& operator=(const S& s) { // no check for self-assign needed, since S is different type - buffer.apply(destroyer()); + apply(destroyer(), buffer); buffer.alloc = s.get_allocator(); buffer.size = s.size(); buffer.set(buffer.template create()); @@ -263,7 +234,7 @@ struct adaptive_storage { allocator_type get_allocator() const { return buffer.alloc; } void reset(std::size_t s) { - buffer.apply(destroyer()); + apply(destroyer(), buffer); buffer.size = s; buffer.set(buffer.template create()); } @@ -272,28 +243,28 @@ struct adaptive_storage { void increase(std::size_t i) { BOOST_ASSERT(i < size()); - buffer.apply(increaser(), i); + apply(increaser(), buffer, i); } template void add(std::size_t i, const T& x) { BOOST_ASSERT(i < size()); - buffer.apply(adder(), i, x); + apply(adder(), buffer, i, x); } template void add(std::size_t i, const weight_type& x) { BOOST_ASSERT(i < size()); - buffer.apply(adder(), i, x.value); + apply(adder(), buffer, i, x.value); } const_reference operator[](std::size_t i) const { - return buffer.apply(getter(), i); + return apply(getter(), buffer, i); } bool operator==(const adaptive_storage& o) const { if (size() != o.size()) return false; - return buffer.apply(comparer(), o.buffer); + return apply(comparer(), buffer, o.buffer); } // precondition: storages have same size @@ -308,9 +279,9 @@ struct adaptive_storage { frequently in real applications. */ const auto copy = o; - copy.buffer.apply(buffer_adder(), buffer); + apply(buffer_adder(), copy.buffer, buffer); } else { - o.buffer.apply(buffer_adder(), buffer); + apply(buffer_adder(), o.buffer, buffer); } return *this; } @@ -324,7 +295,7 @@ struct adaptive_storage { } adaptive_storage& operator*=(const double x) { - buffer.apply(multiplier(), x); + apply(multiplier(), buffer, x); return *this; } @@ -354,7 +325,7 @@ struct adaptive_storage { if (b.size == ob.size && b.type == ob.type) { std::copy(optr, optr + ob.size, reinterpret_cast(b.ptr)); } else { - b.apply(destroyer()); + apply(destroyer(), b); b.alloc = ob.alloc; b.size = ob.size; b.set(b.template create(optr)); @@ -363,7 +334,7 @@ struct adaptive_storage { template void operator()(void*, const OBuffer& ob, Buffer& b) { - b.apply(destroyer()); + apply(destroyer(), b); b.type = 0; b.size = ob.size; } @@ -452,7 +423,7 @@ struct adaptive_storage { struct buffer_adder { template void operator()(T* tp, const OBuffer&, Buffer& b) { - for (std::size_t i = 0; i < b.size; ++i) { b.apply(adder(), i, tp[i]); } + for (std::size_t i = 0; i < b.size; ++i) { apply(adder(), b, i, tp[i]); } } template @@ -507,7 +478,7 @@ struct adaptive_storage { template bool operator()(const T* tp, const Buffer& b, const OBuffer& ob) { BOOST_ASSERT(b.size == ob.size); - return ob.apply(inner(), tp); + return apply(inner(), ob, tp); } }; diff --git a/test/adaptive_storage_serialization_test.cpp b/test/adaptive_storage_serialization_test.cpp index 00263501..d0140131 100644 --- a/test/adaptive_storage_serialization_test.cpp +++ b/test/adaptive_storage_serialization_test.cpp @@ -82,7 +82,7 @@ int main() { serialization_impl(); serialization_impl(); serialization_impl(); - serialization_impl(); + serialization_impl(); } return boost::report_errors(); diff --git a/test/histogram_serialization_test.cpp b/test/histogram_serialization_test.cpp index f767cc7c..ec85519f 100644 --- a/test/histogram_serialization_test.cpp +++ b/test/histogram_serialization_test.cpp @@ -21,13 +21,13 @@ template void run_tests() { // histogram_serialization { - enum { A, B, C }; auto a = make( - Tag(), axis::regular<>(3, -1, 1, "r"), axis::circular<>(4, 0.0, 1.0, "p"), - axis::regular(3, 1, 100, "lr"), - axis::regular(3, 1, 100, "pr", axis::uoflow_type::on, 0.5), - axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "v"), axis::category<>{A, B, C}, - axis::integer<>(0, 2, "i")); + Tag(), axis::regular<>(3, -1, 1, "axis 0"), axis::circular<>(4, 0.0, 1.0, "axis 1"), + axis::regular>(3, 1, 100, "axis 2"), + axis::regular>(3, 1, 100, "axis 3", axis::option_type::overflow, 0.5), + axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"), + axis::category<>{3, 1, 2}, + axis::integer(0, 2)); a(0.5, 0.2, 20, 20, 0.25, 1, 1); std::string buf; { From 3d866690d7e2175d8e165f051939c0440eb91edf Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Tue, 23 Oct 2018 23:11:27 +0200 Subject: [PATCH 09/44] upgrade to newer VS for C++14 support --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 07d369cf..493be21e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,7 +4,7 @@ shallow_clone: true -image: Visual Studio 2015 +image: Visual Studio 2017 branches: only: From 3ee08c7d9028bebc0de13b696ecbd99821250a4a Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Tue, 23 Oct 2018 23:27:07 +0200 Subject: [PATCH 10/44] fix travis --- .travis.yml | 8 ++++---- CMakeLists.txt | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff9fb51a..a3d49998 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,18 +18,18 @@ branches: matrix: include: - os: linux - compiler: gcc + compiler: gcc-5 env: SERIAL=OFF - os: linux - compiler: gcc + compiler: gcc-5 env: SERIAL=ON - os: linux - compiler: clang + compiler: clang-3.4 env: SERIAL=ON - os: osx env: SERIAL=OFF - os: linux # coverage build - compiler: gcc + compiler: gcc-5 env: GCOV=gcov git: diff --git a/CMakeLists.txt b/CMakeLists.txt index 24946d5f..ed4a4a1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,23 +41,21 @@ function(compiled_test SRC) target_include_directories(${BASENAME} PUBLIC include ${Boost_INCLUDE_DIR}) if(CMAKE_BUILD_TYPE MATCHES coverage) - target_compile_options(${BASENAME} PRIVATE -O0 -g --coverage) + target_compile_options(${BASENAME} PRIVATE --coverage) target_link_libraries(${BASENAME} PRIVATE --coverage) - else() - target_compile_options(${BASENAME} PRIVATE -O0 -g) endif() # max warnings if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(${BASENAME} PRIVATE -D_SCL_SECURE_NO_WARNINGS) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(${BASENAME} PRIVATE -Wall -Wextra) + target_compile_options(${BASENAME} PRIVATE -Wall -Wextra -g -O0) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6) # -fpermissive needed for cpp_int in gcc-6 target_compile_options(${BASENAME} PRIVATE -fpermissive) endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options(${BASENAME} PRIVATE -Wall -Wextra + target_compile_options(${BASENAME} PRIVATE -Wall -Wextra -g -O0 -Wno-unused-local-typedef -D__STRICT_ANSI__) endif() From c717bfe08da22db33ce6bb2189b26f02b8bf9fbe Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Tue, 23 Oct 2018 23:59:56 +0200 Subject: [PATCH 11/44] fix travis --- .travis.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a3d49998..cb7a3fd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,13 @@ branches: - master - develop +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + matrix: include: - os: linux @@ -24,7 +31,7 @@ matrix: compiler: gcc-5 env: SERIAL=ON - os: linux - compiler: clang-3.4 + compiler: clang env: SERIAL=ON - os: osx env: SERIAL=OFF From 02f091492a6d35cf3c434d6fda92d88f9074fefe Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 00:09:26 +0200 Subject: [PATCH 12/44] fix travis --- .travis.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index cb7a3fd3..22c2aa65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,29 +15,25 @@ branches: - master - develop -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 - matrix: include: - os: linux - compiler: gcc-5 + compiler: clang env: SERIAL=OFF - - os: linux - compiler: gcc-5 - env: SERIAL=ON - os: linux compiler: clang env: SERIAL=ON - os: osx env: SERIAL=OFF - os: linux # coverage build - compiler: gcc-5 - env: GCOV=gcov + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + compiler: gcc + env: GCOV=gcov-5 CC=gcc-5 CXX=g++-5 git: depth: 1 From e7124cc9d56ce4abcf9ab60606238ecd8f4e0795 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 00:12:09 +0200 Subject: [PATCH 13/44] fix travis --- .travis.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 22c2aa65..ca71e9a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,14 @@ branches: - master - develop +# g++-5 also needed for clang to see C++14 stdlib +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + matrix: include: - os: linux @@ -26,14 +34,8 @@ matrix: - os: osx env: SERIAL=OFF - os: linux # coverage build - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 compiler: gcc - env: GCOV=gcov-5 CC=gcc-5 CXX=g++-5 + env: GCOV=gcov CC=gcc-5 CXX=g++-5 git: depth: 1 From 4dbb55ef99b87fbaa8ec34dc1937de8c29defe1d Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 00:43:24 +0200 Subject: [PATCH 14/44] fix --- .travis.yml | 1 - include/boost/histogram/histogram_fwd.hpp | 54 +++++++++---------- include/boost/histogram/serialization.hpp | 19 ++----- .../histogram/storage/weight_counter.hpp | 27 ++++------ 4 files changed, 41 insertions(+), 60 deletions(-) diff --git a/.travis.yml b/.travis.yml index ca71e9a4..5106b070 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,6 @@ matrix: - os: osx env: SERIAL=OFF - os: linux # coverage build - compiler: gcc env: GCOV=gcov CC=gcc-5 CXX=g++-5 git: diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index f5dc394f..11100d3e 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -7,15 +7,16 @@ #ifndef BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP #define BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP -#include // for std::allocator -#include -#include +#include + +namespace std { +template +class allocator; +} // namespace std namespace boost { namespace histogram { - namespace axis { - struct empty_metadata_type {}; enum class option_type { @@ -25,39 +26,39 @@ enum class option_type { }; namespace transform { -template struct identity; -template struct log; -template struct sqrt; -template struct pow; -template struct quantity; -} // namespace transform +template +struct identity; +template +struct log; +template +struct sqrt; +template +struct pow; +template +struct quantity; +} // namespace transform template , - typename MetaData = boost::container::string> + typename MetaData = std::string> class regular; -template +template class circular; -template , - typename MetaData = boost::container::string> +template , + typename MetaData = std::string> class variable; -template +template class integer; -template , - typename MetaData = boost::container::string> +template , + typename MetaData = std::string> class category; template class variant; - -} // namespace axis +} // namespace axis template > struct adaptive_storage; @@ -69,8 +70,7 @@ using default_storage = adaptive_storage<>; template class histogram; - -} // namespace histogram -} // namespace boost +} // namespace histogram +} // namespace boost #endif diff --git a/include/boost/histogram/serialization.hpp b/include/boost/histogram/serialization.hpp index 431a903d..dd2af862 100644 --- a/include/boost/histogram/serialization.hpp +++ b/include/boost/histogram/serialization.hpp @@ -7,21 +7,20 @@ #ifndef BOOST_HISTOGRAM_SERIALIZATION_HPP #define BOOST_HISTOGRAM_SERIALIZATION_HPP -#include #include #include #include #include -#include #include #include #include #include #include #include -#include +#include #include #include +#include #include /** \file boost/histogram/serialization.hpp @@ -32,23 +31,11 @@ namespace std { template void serialize(Archive& ar, tuple& t, unsigned /* version */) { - boost::mp11::tuple_for_each(t, [&ar](auto& x) { ar& x; }); + ::boost::mp11::tuple_for_each(t, [&ar](auto& x) { ar& x; }); } } // namespace std namespace boost { -namespace container { -template -void serialize(Archive& ar, string& s, unsigned /* version */) { - auto size = s.size(); - ar& size; - if (Archive::is_loading::value) { - s.resize(size); - } - ar& boost::serialization::make_array(s.data(), size); -} -} // namespace container - namespace histogram { template template diff --git a/include/boost/histogram/storage/weight_counter.hpp b/include/boost/histogram/storage/weight_counter.hpp index e3bd083f..177574bf 100644 --- a/include/boost/histogram/storage/weight_counter.hpp +++ b/include/boost/histogram/storage/weight_counter.hpp @@ -11,17 +11,12 @@ #include namespace boost { - -namespace serialization { -class access; -} // namespace serialization - namespace histogram { /// Double counter which holds a sum of weights and a sum of squared weights -template +template class weight_counter { -public: + public: /// Beware: For performance reasons counters are not initialized weight_counter() = default; weight_counter(const weight_counter&) = default; @@ -30,10 +25,10 @@ public: weight_counter& operator=(weight_counter&&) = default; weight_counter(const RealType& value, const RealType& variance) noexcept - : w(value), - w2(variance) {} + : w(value), w2(variance) {} - explicit weight_counter(const RealType& value) noexcept : w(value), w2(value) {} + explicit weight_counter(const RealType& value) noexcept + : w(value), w2(value) {} weight_counter& operator++() noexcept { ++w; @@ -73,7 +68,9 @@ public: return w == rhs.w && w2 == rhs.w2; } - bool operator!=(const weight_counter& rhs) const noexcept { return !operator==(rhs); } + bool operator!=(const weight_counter& rhs) const noexcept { + return !operator==(rhs); + } template bool operator==(const weight_counter& rhs) const noexcept { @@ -105,12 +102,10 @@ public: return static_cast(w); } -private: - friend class ::boost::serialization::access; - template void serialize(Archive&, unsigned /* version */); + private: RealType w, w2; }; @@ -166,7 +161,7 @@ weight_counter operator+(const T& a, const weight_counter& b) noexcept { return r += a; } -} // namespace histogram -} // namespace boost +} // namespace histogram +} // namespace boost #endif From edc68bc4532ed9cc5f4d4339a2fa3c27f9a941b2 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 01:05:09 +0200 Subject: [PATCH 15/44] fix --- .travis.yml | 6 +- include/boost/histogram/axis/types.hpp | 212 +++++++++++----------- include/boost/histogram/histogram_fwd.hpp | 6 +- 3 files changed, 107 insertions(+), 117 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5106b070..6ee49d95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,11 +34,15 @@ matrix: - os: osx env: SERIAL=OFF - os: linux # coverage build - env: GCOV=gcov CC=gcc-5 CXX=g++-5 + env: + - MATRIX_EVAL="GCOV=gcov-5 && CC=gcc-5 && CXX=g++-5" git: depth: 1 +before_install: + - eval "${MATRIX_EVAL}" + # Install packages (pre-installed: pytest) install: - source tools/travis_install_boost.sh diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index 553371ae..fd347a0e 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ struct identity { constexpr bool operator==(const identity&) const noexcept { return true; } template - void serialize(Archive&, unsigned) {} // noop + void serialize(Archive&, unsigned) {} // noop }; template @@ -71,7 +72,8 @@ struct quantity { quantity(const Unit& u) : unit(u) {} - using Dimensionless = decltype(std::declval() / std::declval()); + using Dimensionless = + decltype(std::declval() / std::declval()); Dimensionless forward(Quantity x) const { return x / unit; } Quantity inverse(Dimensionless x) const { return x * unit; } @@ -80,7 +82,7 @@ struct quantity { template void serialize(Archive&, unsigned); }; -} // namespace transform +} // namespace transform /** Axis for equidistant intervals on the real line. * @@ -89,37 +91,33 @@ struct quantity { */ template class regular : public base, - public iterator_mixin> -{ + public iterator_mixin> { using base_type = base; using transform_type = Transform; using value_type = detail::arg_type<-1, decltype(&transform_type::forward)>; - using internal_type = detail::return_type< - decltype(&transform_type::forward) - >; + using internal_type = detail::return_type; static_assert(std::is_floating_point::value, "type returned by forward transform must be floating point"); using metadata_type = MetaData; - struct data : transform_type // empty base class optimization + struct data : transform_type // empty base class optimization { internal_type min = 0, delta = 1; data(const transform_type& t, unsigned n, value_type b, value_type e) - : transform_type(t) - , min(this->forward(b)) - , delta((this->forward(e) - this->forward(b))/n) - {} + : transform_type(t), + min(this->forward(b)), + delta((this->forward(e) - this->forward(b)) / n) {} data() = default; bool operator==(const data& rhs) const noexcept { return transform_type::operator==(rhs) && min == rhs.min && - delta == rhs.delta; + delta == rhs.delta; } }; using bin_type = interval_view; -public: + public: /** Construct axis with n bins over real range [begin, end). * * \param n number of bins. @@ -133,11 +131,10 @@ public: metadata_type m = metadata_type(), option_type o = option_type::underflow_and_overflow, transform_type trans = transform_type()) - : base_type(n, std::move(m), o) - , data_(std::move(trans), n, start, stop) - { + : base_type(n, std::move(m), o), data_(std::move(trans), n, start, stop) { if (!std::isfinite(data_.min) || !std::isfinite(data_.delta)) - throw std::invalid_argument("forward transform of lower or upper invalid"); + throw std::invalid_argument( + "forward transform of lower or upper invalid"); } regular() = default; @@ -152,11 +149,12 @@ public: else return -1; } - return base_type::size(); // also returned if z is NaN + return base_type::size(); // also returned if z is NaN // const auto lt_max = z < base_type::size(); // const auto ge_zero = z >= 0; - // return lt_max * (ge_zero * static_cast(z) - !ge_zero) + !lt_max * base_type::size(); + // return lt_max * (ge_zero * static_cast(z) - !ge_zero) + !lt_max * + // base_type::size(); } /// Returns lower edge of bin. @@ -168,7 +166,8 @@ public: else if (z > 1) x = std::numeric_limits::infinity(); else { - x = (1 - z) * data_.min + z * (data_.min + data_.delta * base_type::size()); + x = (1 - z) * data_.min + + z * (data_.min + data_.delta * base_type::size()); } return data_.inverse(x); } @@ -184,7 +183,7 @@ public: template void serialize(Archive&, unsigned); -private: + private: data data_; }; @@ -201,7 +200,8 @@ class circular : public base, using value_type = RealType; using metadata_type = MetaData; using bin_type = interval_view; -public: + + public: // two_pi can be found in boost/math, but it is defined here to reduce deps static constexpr value_type two_pi() { return 6.283185307179586; } @@ -217,10 +217,12 @@ public: value_type perimeter = two_pi(), metadata_type m = metadata_type(), option_type o = option_type::overflow) - : base_type(n, std::move(m), o == option_type::underflow_and_overflow ? - option_type::overflow : o) - , phase_(phase), delta_(perimeter / n) - { + : base_type(n, std::move(m), + o == option_type::underflow_and_overflow + ? option_type::overflow + : o), + phase_(phase), + delta_(perimeter / n) { if (!std::isfinite(phase) || !(perimeter > 0)) throw std::invalid_argument("invalid phase or perimeter"); } @@ -238,21 +240,18 @@ public: } /// Returns lower edge of bin. - value_type lower(int i) const noexcept { - return phase_ + i * delta_; - } + value_type lower(int i) const noexcept { return phase_ + i * delta_; } bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } bool operator==(const circular& o) const noexcept { - return base_type::operator==(o) && - phase_ == o.phase_ && delta_ == o.delta_; + return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_; } template void serialize(Archive&, unsigned); -private: + private: value_type phase_ = 0.0, delta_ = 1.0; }; @@ -262,15 +261,16 @@ private: * domain allows it, prefer a regular axis, possibly with a transform. */ template -class variable : public base, - public iterator_mixin> { +class variable + : public base, + public iterator_mixin> { using base_type = base; using value_type = RealType; using allocator_type = Allocator; using metadata_type = MetaData; using bin_type = interval_view; - struct data : allocator_type // empty base class optimization + struct data : allocator_type // empty base class optimization { typename std::allocator_traits::pointer x = nullptr; @@ -279,8 +279,7 @@ class variable : public base, data(const allocator_type& a) : allocator_type(a) {} data() = default; - friend void swap(data& a, data& b) noexcept - { + friend void swap(data& a, data& b) noexcept { std::swap(a.x, b.x); auto tmp = static_cast(a); a = static_cast(b); @@ -288,7 +287,7 @@ class variable : public base, } }; -public: + public: /** Construct an axis from iterator range of bin edges. * * \param begin begin of edge sequence. @@ -297,15 +296,13 @@ public: * \param options extra bin options. * \param allocator allocator instance to use. */ - template > - variable(Iterator begin, Iterator end, - metadata_type m = metadata_type(), + template > + variable(Iterator begin, Iterator end, metadata_type m = metadata_type(), option_type o = option_type::underflow_and_overflow, allocator_type a = allocator_type()) - : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o) - , data_(std::move(a)) - { + : base_type(begin == end ? 0 : std::distance(begin, end) - 1, + std::move(m), o), + data_(std::move(a)) { using AT = std::allocator_traits; data_.x = AT::allocate(data_, nx()); try { @@ -314,8 +311,9 @@ public: AT::construct(data_, xit, *begin++); while (begin != end) { if (*begin <= *xit) { - ++xit; // to make sure catch code works - throw std::invalid_argument("input sequence must be strictly ascending"); + ++xit; // to make sure catch code works + throw std::invalid_argument( + "input sequence must be strictly ascending"); } ++xit; AT::construct(data_, xit, *begin++); @@ -340,15 +338,13 @@ public: * \param allocator allocator instance to use. */ template > - variable(const T& t, - metadata_type m = metadata_type(), + variable(const T& t, metadata_type m = metadata_type(), option_type o = option_type::underflow_and_overflow, allocator_type a = allocator_type()) : variable(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} template - variable(std::initializer_list t, - metadata_type m = metadata_type(), + variable(std::initializer_list t, metadata_type m = metadata_type(), option_type o = option_type::underflow_and_overflow, allocator_type a = allocator_type()) : variable(t.begin(), t.end(), std::move(m), o, std::move(a)) {} @@ -374,25 +370,19 @@ public: return *this; } - variable(variable&& o) - : base_type(std::move(o)) - , data_(std::move(o.data_)) - { + variable(variable&& o) : base_type(std::move(o)), data_(std::move(o.data_)) { o.data_.x = nullptr; } variable& operator=(variable&& o) { if (this != &o) { - std::swap(static_cast(*this), - static_cast(o)); + std::swap(static_cast(*this), static_cast(o)); std::swap(data_, o.data_); } return *this; } - ~variable() { - detail::destroy_buffer(data_, data_.x, nx()); - } + ~variable() { detail::destroy_buffer(data_, data_.x, nx()); } /// Returns the bin index for the passed argument. int operator()(value_type x) const noexcept { @@ -401,8 +391,12 @@ public: /// Returns the starting edge of the bin. value_type lower(int i) const noexcept { - if (i < 0) { return -std::numeric_limits::infinity(); } - if (i > static_cast(base_type::size())) { return std::numeric_limits::infinity(); } + if (i < 0) { + return -std::numeric_limits::infinity(); + } + if (i > static_cast(base_type::size())) { + return std::numeric_limits::infinity(); + } return data_.x[i]; } @@ -410,13 +404,13 @@ public: bool operator==(const variable& o) const noexcept { return base_type::operator==(o) && - std::equal(data_.x, data_.x + nx(), o.data_.x); + std::equal(data_.x, data_.x + nx(), o.data_.x); } template void serialize(Archive&, unsigned); -private: + private: int nx() const { return base_type::size() + 1; } data data_; }; @@ -433,8 +427,8 @@ class integer : public base, using value_type = IntType; using metadata_type = MetaData; using bin_type = interval_view; -public: + public: /** Construct axis over a semi-open integer interval [begin, end). * * \param begin first integer of covered range. @@ -442,11 +436,12 @@ public: * \param metadata description of the axis. * \param options extra bin options. */ - integer(value_type begin, value_type end, - metadata_type m = metadata_type(), + integer(value_type begin, value_type end, metadata_type m = metadata_type(), option_type o = option_type::underflow_and_overflow) : base_type(end - begin, std::move(m), o), min_(begin) { - if (begin >= end) { throw std::invalid_argument("begin < end required"); } + if (begin >= end) { + throw std::invalid_argument("begin < end required"); + } } integer() = default; @@ -458,13 +453,19 @@ public: /// Returns the bin index for the passed argument. int operator()(value_type x) const noexcept { const int z = x - min_; - return z >= 0 ? (z > static_cast(base_type::size()) ? base_type::size() : z) : -1; + return z >= 0 ? (z > static_cast(base_type::size()) ? base_type::size() + : z) + : -1; } /// Returns lower edge of the integral bin. value_type lower(int i) const noexcept { - if (i < 0) { return std::numeric_limits::min(); } - if (i > static_cast(base_type::size())) { return std::numeric_limits::max(); } + if (i < 0) { + return std::numeric_limits::min(); + } + if (i > static_cast(base_type::size())) { + return std::numeric_limits::max(); + } return min_ + i; } @@ -477,7 +478,7 @@ public: template void serialize(Archive&, unsigned); -private: + private: value_type min_ = 0; }; @@ -490,8 +491,9 @@ private: * the best case. The value types must be equal-comparable. */ template -class category : public base, - public iterator_mixin> { +class category + : public base, + public iterator_mixin> { using base_type = base; using metadata_type = MetaData; using value_type = ValueType; @@ -504,8 +506,7 @@ class category : public base, data(const allocator_type& a) : allocator_type(a) {} data() = default; - friend void swap(data& a, data& b) noexcept - { + friend void swap(data& a, data& b) noexcept { std::swap(a.x, b.x); auto tmp = static_cast(a); a = static_cast(b); @@ -513,7 +514,7 @@ class category : public base, } }; -public: + public: /** Construct an axis from iterator range of categories. * * \param begin begin of category range of unique values. @@ -522,15 +523,12 @@ public: * \param options extra bin options. * \param allocator allocator instance to use. */ - template > - category(Iterator begin, Iterator end, - metadata_type m = metadata_type(), + template > + category(Iterator begin, Iterator end, metadata_type m = metadata_type(), option_type o = option_type::overflow, allocator_type a = allocator_type()) - : base_type(std::distance(begin, end), std::move(m), o) - , data_(std::move(a)) - { + : base_type(std::distance(begin, end), std::move(m), o), + data_(std::move(a)) { data_.x = detail::create_buffer_from_iter(data_, base_type::size(), begin); } @@ -539,28 +537,23 @@ public: * \param seq sequence of unique values. * \param metadata description of the axis. */ - template > - category(const T& t, - metadata_type m = metadata_type(), + template > + category(const T& t, metadata_type m = metadata_type(), option_type o = option_type::overflow, allocator_type a = allocator_type()) - : category(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} + : category(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} template - category(std::initializer_list t, - metadata_type m = metadata_type(), + category(std::initializer_list t, metadata_type m = metadata_type(), option_type o = option_type::overflow, allocator_type a = allocator_type()) - : category(t.begin(), t.end(), std::move(m), o, std::move(a)) {} + : category(t.begin(), t.end(), std::move(m), o, std::move(a)) {} category() = default; - category(const category& o) - : base_type(o) - , data_(o.data_) - { - data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); + category(const category& o) : base_type(o), data_(o.data_) { + data_.x = + detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); } category& operator=(const category& o) { @@ -569,7 +562,8 @@ public: detail::destroy_buffer(data_, data_.x, base_type::size()); base_type::operator=(o); data_ = o.data_; - data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); + data_.x = detail::create_buffer_from_iter(data_, base_type::size(), + o.data_.x); } else { base_type::operator=(o); std::copy(o.data_.x, o.data_.x + base_type::size(), data_.x); @@ -578,9 +572,7 @@ public: return *this; } - category(category&& o) - : base_type(std::move(o)) - , data_(std::move(o.data_)) { + category(category&& o) : base_type(std::move(o)), data_(std::move(o.data_)) { o.data_.x = nullptr; } @@ -592,9 +584,7 @@ public: return *this; } - ~category() { - detail::destroy_buffer(data_, data_.x, base_type::size()); - } + ~category() { detail::destroy_buffer(data_, data_.x, base_type::size()); } /// Returns the bin index for the passed argument. int operator()(const value_type& x) const noexcept { @@ -614,17 +604,17 @@ public: bool operator==(const category& o) const noexcept { return base_type::operator==(o) && - std::equal(data_.x, data_.x + base_type::size(), o.data_.x); + std::equal(data_.x, data_.x + base_type::size(), o.data_.x); } template void serialize(Archive&, unsigned); -private: + private: data data_; }; -} // namespace axis -} // namespace histogram -} // namespace boost +} // namespace axis +} // namespace histogram +} // namespace boost #endif diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 11100d3e..0144d19c 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -7,13 +7,9 @@ #ifndef BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP #define BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP +#include // for std::allocator #include -namespace std { -template -class allocator; -} // namespace std - namespace boost { namespace histogram { namespace axis { From 9e77938960b431983ff1d8e0745349aac890477d Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 01:10:40 +0200 Subject: [PATCH 16/44] ws --- histogram.sublime-project | 1 + .../histogram/axis/ostream_operators.hpp | 75 ++++++++++++------- .../histogram/storage/weight_counter.hpp | 15 ++-- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/histogram.sublime-project b/histogram.sublime-project index c4e4d7aa..ea8acab4 100644 --- a/histogram.sublime-project +++ b/histogram.sublime-project @@ -9,6 +9,7 @@ { "ClangFormat": { + "style": "File", "format_on_save": true }, diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 9c7287cc..89be86cc 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -9,49 +9,69 @@ #ifndef BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP #define BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP +#include #include #include #include #include #include -#include #include +#include #include -#include namespace boost { namespace histogram { namespace detail { -template const char* to_string(const axis::transform::identity&) { return ""; } -template const char* to_string(const axis::transform::log&) { return "_log"; } -template const char* to_string(const axis::transform::sqrt&) { return "_sqrt"; } -template const char* to_string(const axis::transform::pow&) { return "_pow"; } -template const char* to_string(const axis::transform::quantity&) { return "_quantity"; } +template +const char* to_string(const axis::transform::identity&) { + return ""; +} +template +const char* to_string(const axis::transform::log&) { + return "_log"; +} +template +const char* to_string(const axis::transform::sqrt&) { + return "_sqrt"; +} +template +const char* to_string(const axis::transform::pow&) { + return "_pow"; +} +template +const char* to_string(const axis::transform::quantity&) { + return "_quantity"; +} template void stream_metadata(OStream& os, const T& t) { detail::static_if>( - [&os](const auto& t) { - std::ostringstream oss; - oss << t; - if (!oss.str().empty()) { - os << ", metadata=" << std::quoted(oss.str()); - } - }, - [&os](const auto&) { - using U = detail::rm_cvref; - os << ", metadata=" << boost::core::demangled_name( BOOST_CORE_TYPEID(U) ); - }, t); + [&os](const auto& t) { + std::ostringstream oss; + oss << t; + if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); } + }, + [&os](const auto&) { + using U = detail::rm_cvref; + os << ", metadata=" << boost::core::demangled_name(BOOST_CORE_TYPEID(U)); + }, + t); } template void stream_options(OStream& os, const axis::option_type o) { os << ", options="; switch (o) { - case axis::option_type::none: os << "none"; break; - case axis::option_type::overflow: os << "overflow"; break; - case axis::option_type::underflow_and_overflow: os << "underflow_and_overflow"; break; + case axis::option_type::none: + os << "none"; + break; + case axis::option_type::overflow: + os << "overflow"; + break; + case axis::option_type::underflow_and_overflow: + os << "underflow_and_overflow"; + break; } } @@ -64,7 +84,7 @@ void stream_transform(OStream& os, const axis::transform::pow& t) { } template -void stream_transform(OStream& os, const axis::transform::quantity& t) { +void stream_transform(OStream& os, const axis::transform::quantity& t) { os << ", unit=" << t.unit; } @@ -107,8 +127,7 @@ std::basic_ostream& operator<<(std::basic_ostream& template std::basic_ostream& operator<<(std::basic_ostream& os, const circular& a) { - os << "circular(" << a.size() - << ", " << a.lower(0) << ", " << a.lower(a.size()); + os << "circular(" << a.size() << ", " << a.lower(0) << ", " << a.lower(a.size()); detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); os << ")"; @@ -140,7 +159,9 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, const category& a) { os << "category("; - for (unsigned i = 0; i < a.size(); ++i) { os << a[i] << (i == (a.size() - 1) ? "" : ", "); } + for (unsigned i = 0; i < a.size(); ++i) { + os << a[i] << (i == (a.size() - 1) ? "" : ", "); + } detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); os << ")"; @@ -148,8 +169,8 @@ std::basic_ostream& operator<<(std::basic_ostream& } template -std::basic_ostream& operator<<( - std::basic_ostream& os, const category& a) { +std::basic_ostream& operator<<(std::basic_ostream& os, + const category& a) { os << "category("; for (unsigned i = 0; i < a.size(); ++i) { os << std::quoted(a.value(i)); diff --git a/include/boost/histogram/storage/weight_counter.hpp b/include/boost/histogram/storage/weight_counter.hpp index 177574bf..ab80dd5a 100644 --- a/include/boost/histogram/storage/weight_counter.hpp +++ b/include/boost/histogram/storage/weight_counter.hpp @@ -16,7 +16,7 @@ namespace histogram { /// Double counter which holds a sum of weights and a sum of squared weights template class weight_counter { - public: +public: /// Beware: For performance reasons counters are not initialized weight_counter() = default; weight_counter(const weight_counter&) = default; @@ -27,8 +27,7 @@ class weight_counter { weight_counter(const RealType& value, const RealType& variance) noexcept : w(value), w2(variance) {} - explicit weight_counter(const RealType& value) noexcept - : w(value), w2(value) {} + explicit weight_counter(const RealType& value) noexcept : w(value), w2(value) {} weight_counter& operator++() noexcept { ++w; @@ -68,9 +67,7 @@ class weight_counter { return w == rhs.w && w2 == rhs.w2; } - bool operator!=(const weight_counter& rhs) const noexcept { - return !operator==(rhs); - } + bool operator!=(const weight_counter& rhs) const noexcept { return !operator==(rhs); } template bool operator==(const weight_counter& rhs) const noexcept { @@ -105,7 +102,7 @@ class weight_counter { template void serialize(Archive&, unsigned /* version */); - private: +private: RealType w, w2; }; @@ -161,7 +158,7 @@ weight_counter operator+(const T& a, const weight_counter& b) noexcept { return r += a; } -} // namespace histogram -} // namespace boost +} // namespace histogram +} // namespace boost #endif From 2fee4e457dea36f15a60aa44cffd6c744a36d38f Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 01:15:30 +0200 Subject: [PATCH 17/44] fix win --- include/boost/histogram/axis/types.hpp | 136 ++++++++++--------------- 1 file changed, 55 insertions(+), 81 deletions(-) diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index fd347a0e..518f7d78 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -36,7 +36,7 @@ struct identity { constexpr bool operator==(const identity&) const noexcept { return true; } template - void serialize(Archive&, unsigned) {} // noop + void serialize(Archive&, unsigned) {} // noop }; template @@ -72,8 +72,7 @@ struct quantity { quantity(const Unit& u) : unit(u) {} - using Dimensionless = - decltype(std::declval() / std::declval()); + using Dimensionless = decltype(std::declval() / std::declval()); Dimensionless forward(Quantity x) const { return x / unit; } Quantity inverse(Dimensionless x) const { return x * unit; } @@ -82,7 +81,7 @@ struct quantity { template void serialize(Archive&, unsigned); }; -} // namespace transform +} // namespace transform /** Axis for equidistant intervals on the real line. * @@ -99,25 +98,24 @@ class regular : public base, static_assert(std::is_floating_point::value, "type returned by forward transform must be floating point"); using metadata_type = MetaData; - struct data : transform_type // empty base class optimization + struct data : transform_type // empty base class optimization { internal_type min = 0, delta = 1; data(const transform_type& t, unsigned n, value_type b, value_type e) - : transform_type(t), - min(this->forward(b)), - delta((this->forward(e) - this->forward(b)) / n) {} + : transform_type(t) + , min(this->forward(b)) + , delta((this->forward(e) - this->forward(b)) / n) {} data() = default; bool operator==(const data& rhs) const noexcept { - return transform_type::operator==(rhs) && min == rhs.min && - delta == rhs.delta; + return transform_type::operator==(rhs) && min == rhs.min && delta == rhs.delta; } }; using bin_type = interval_view; - public: +public: /** Construct axis with n bins over real range [begin, end). * * \param n number of bins. @@ -133,8 +131,7 @@ class regular : public base, transform_type trans = transform_type()) : base_type(n, std::move(m), o), data_(std::move(trans), n, start, stop) { if (!std::isfinite(data_.min) || !std::isfinite(data_.delta)) - throw std::invalid_argument( - "forward transform of lower or upper invalid"); + throw std::invalid_argument("forward transform of lower or upper invalid"); } regular() = default; @@ -149,7 +146,7 @@ class regular : public base, else return -1; } - return base_type::size(); // also returned if z is NaN + return base_type::size(); // also returned if z is NaN // const auto lt_max = z < base_type::size(); // const auto ge_zero = z >= 0; @@ -166,8 +163,7 @@ class regular : public base, else if (z > 1) x = std::numeric_limits::infinity(); else { - x = (1 - z) * data_.min + - z * (data_.min + data_.delta * base_type::size()); + x = (1 - z) * data_.min + z * (data_.min + data_.delta * base_type::size()); } return data_.inverse(x); } @@ -183,7 +179,7 @@ class regular : public base, template void serialize(Archive&, unsigned); - private: +private: data data_; }; @@ -201,7 +197,7 @@ class circular : public base, using metadata_type = MetaData; using bin_type = interval_view; - public: +public: // two_pi can be found in boost/math, but it is defined here to reduce deps static constexpr value_type two_pi() { return 6.283185307179586; } @@ -213,16 +209,13 @@ class circular : public base, * \param metadata description of the axis. * \param options extra bin options. */ - explicit circular(unsigned n, value_type phase = 0.0, - value_type perimeter = two_pi(), + explicit circular(unsigned n, value_type phase = 0.0, value_type perimeter = two_pi(), metadata_type m = metadata_type(), option_type o = option_type::overflow) : base_type(n, std::move(m), - o == option_type::underflow_and_overflow - ? option_type::overflow - : o), - phase_(phase), - delta_(perimeter / n) { + o == option_type::underflow_and_overflow ? option_type::overflow : o) + , phase_(phase) + , delta_(perimeter / n) { if (!std::isfinite(phase) || !(perimeter > 0)) throw std::invalid_argument("invalid phase or perimeter"); } @@ -251,7 +244,7 @@ class circular : public base, template void serialize(Archive&, unsigned); - private: +private: value_type phase_ = 0.0, delta_ = 1.0; }; @@ -261,33 +254,32 @@ class circular : public base, * domain allows it, prefer a regular axis, possibly with a transform. */ template -class variable - : public base, - public iterator_mixin> { +class variable : public base, + public iterator_mixin> { using base_type = base; using value_type = RealType; using allocator_type = Allocator; using metadata_type = MetaData; using bin_type = interval_view; - struct data : allocator_type // empty base class optimization + struct data : Allocator // empty base class optimization { - typename std::allocator_traits::pointer x = nullptr; + typename std::allocator_traits::pointer x = nullptr; - using allocator_type::allocator_type; + using Allocator::Allocator; - data(const allocator_type& a) : allocator_type(a) {} + data(const Allocator& a) : Allocator(a) {} data() = default; friend void swap(data& a, data& b) noexcept { std::swap(a.x, b.x); - auto tmp = static_cast(a); - a = static_cast(b); - b = static_cast(tmp); + auto tmp = static_cast(a); + a = static_cast(b); + b = static_cast(tmp); } }; - public: +public: /** Construct an axis from iterator range of bin edges. * * \param begin begin of edge sequence. @@ -300,9 +292,8 @@ class variable variable(Iterator begin, Iterator end, metadata_type m = metadata_type(), option_type o = option_type::underflow_and_overflow, allocator_type a = allocator_type()) - : base_type(begin == end ? 0 : std::distance(begin, end) - 1, - std::move(m), o), - data_(std::move(a)) { + : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o) + , data_(std::move(a)) { using AT = std::allocator_traits; data_.x = AT::allocate(data_, nx()); try { @@ -311,9 +302,8 @@ class variable AT::construct(data_, xit, *begin++); while (begin != end) { if (*begin <= *xit) { - ++xit; // to make sure catch code works - throw std::invalid_argument( - "input sequence must be strictly ascending"); + ++xit; // to make sure catch code works + throw std::invalid_argument("input sequence must be strictly ascending"); } ++xit; AT::construct(data_, xit, *begin++); @@ -391,9 +381,7 @@ class variable /// Returns the starting edge of the bin. value_type lower(int i) const noexcept { - if (i < 0) { - return -std::numeric_limits::infinity(); - } + if (i < 0) { return -std::numeric_limits::infinity(); } if (i > static_cast(base_type::size())) { return std::numeric_limits::infinity(); } @@ -403,14 +391,13 @@ class variable bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } bool operator==(const variable& o) const noexcept { - return base_type::operator==(o) && - std::equal(data_.x, data_.x + nx(), o.data_.x); + return base_type::operator==(o) && std::equal(data_.x, data_.x + nx(), o.data_.x); } template void serialize(Archive&, unsigned); - private: +private: int nx() const { return base_type::size() + 1; } data data_; }; @@ -421,14 +408,13 @@ class variable * faster than a regular. */ template -class integer : public base, - public iterator_mixin> { +class integer : public base, public iterator_mixin> { using base_type = base; using value_type = IntType; using metadata_type = MetaData; using bin_type = interval_view; - public: +public: /** Construct axis over a semi-open integer interval [begin, end). * * \param begin first integer of covered range. @@ -439,9 +425,7 @@ class integer : public base, integer(value_type begin, value_type end, metadata_type m = metadata_type(), option_type o = option_type::underflow_and_overflow) : base_type(end - begin, std::move(m), o), min_(begin) { - if (begin >= end) { - throw std::invalid_argument("begin < end required"); - } + if (begin >= end) { throw std::invalid_argument("begin < end required"); } } integer() = default; @@ -453,16 +437,13 @@ class integer : public base, /// Returns the bin index for the passed argument. int operator()(value_type x) const noexcept { const int z = x - min_; - return z >= 0 ? (z > static_cast(base_type::size()) ? base_type::size() - : z) + return z >= 0 ? (z > static_cast(base_type::size()) ? base_type::size() : z) : -1; } /// Returns lower edge of the integral bin. value_type lower(int i) const noexcept { - if (i < 0) { - return std::numeric_limits::min(); - } + if (i < 0) { return std::numeric_limits::min(); } if (i > static_cast(base_type::size())) { return std::numeric_limits::max(); } @@ -478,7 +459,7 @@ class integer : public base, template void serialize(Archive&, unsigned); - private: +private: value_type min_ = 0; }; @@ -491,9 +472,8 @@ class integer : public base, * the best case. The value types must be equal-comparable. */ template -class category - : public base, - public iterator_mixin> { +class category : public base, + public iterator_mixin> { using base_type = base; using metadata_type = MetaData; using value_type = ValueType; @@ -514,7 +494,7 @@ class category } }; - public: +public: /** Construct an axis from iterator range of categories. * * \param begin begin of category range of unique values. @@ -525,10 +505,8 @@ class category */ template > category(Iterator begin, Iterator end, metadata_type m = metadata_type(), - option_type o = option_type::overflow, - allocator_type a = allocator_type()) - : base_type(std::distance(begin, end), std::move(m), o), - data_(std::move(a)) { + option_type o = option_type::overflow, allocator_type a = allocator_type()) + : base_type(std::distance(begin, end), std::move(m), o), data_(std::move(a)) { data_.x = detail::create_buffer_from_iter(data_, base_type::size(), begin); } @@ -539,21 +517,18 @@ class category */ template > category(const T& t, metadata_type m = metadata_type(), - option_type o = option_type::overflow, - allocator_type a = allocator_type()) + option_type o = option_type::overflow, allocator_type a = allocator_type()) : category(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} template category(std::initializer_list t, metadata_type m = metadata_type(), - option_type o = option_type::overflow, - allocator_type a = allocator_type()) + option_type o = option_type::overflow, allocator_type a = allocator_type()) : category(t.begin(), t.end(), std::move(m), o, std::move(a)) {} category() = default; category(const category& o) : base_type(o), data_(o.data_) { - data_.x = - detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); + data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); } category& operator=(const category& o) { @@ -562,8 +537,7 @@ class category detail::destroy_buffer(data_, data_.x, base_type::size()); base_type::operator=(o); data_ = o.data_; - data_.x = detail::create_buffer_from_iter(data_, base_type::size(), - o.data_.x); + data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); } else { base_type::operator=(o); std::copy(o.data_.x, o.data_.x + base_type::size(), data_.x); @@ -610,11 +584,11 @@ class category template void serialize(Archive&, unsigned); - private: +private: data data_; }; -} // namespace axis -} // namespace histogram -} // namespace boost +} // namespace axis +} // namespace histogram +} // namespace boost #endif From fce320bd2a94275543e531bf349af0cf4c7908f5 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 01:18:25 +0200 Subject: [PATCH 18/44] fix win --- include/boost/histogram/axis/types.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index 518f7d78..c4a63bfb 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -480,17 +480,17 @@ class category : public base, using allocator_type = Allocator; using bin_type = value_view; - struct data : allocator_type { - typename std::allocator_traits::pointer x = nullptr; - using allocator_type::allocator_type; - data(const allocator_type& a) : allocator_type(a) {} + struct data : Allocator { + typename std::allocator_traits::pointer x = nullptr; + using Allocator::Allocator; + data(const Allocator& a) : Allocator(a) {} data() = default; friend void swap(data& a, data& b) noexcept { std::swap(a.x, b.x); - auto tmp = static_cast(a); - a = static_cast(b); - b = static_cast(tmp); + auto tmp = static_cast(a); + a = static_cast(b); + b = static_cast(tmp); } }; From 5222899c3d9a0544685682e554de0f263d5bf201 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 01:22:04 +0200 Subject: [PATCH 19/44] fix win --- include/boost/histogram/axis/types.hpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index c4a63bfb..d2938c32 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -98,22 +98,25 @@ class regular : public base, static_assert(std::is_floating_point::value, "type returned by forward transform must be floating point"); using metadata_type = MetaData; - struct data : transform_type // empty base class optimization + using bin_type = interval_view; + + struct data : Transform // empty base class optimization, + // MSVC fails if transform_type is used here + { internal_type min = 0, delta = 1; - data(const transform_type& t, unsigned n, value_type b, value_type e) - : transform_type(t) + data(const Transform& t, unsigned n, value_type b, value_type e) + : Transform(t) , min(this->forward(b)) , delta((this->forward(e) - this->forward(b)) / n) {} data() = default; bool operator==(const data& rhs) const noexcept { - return transform_type::operator==(rhs) && min == rhs.min && delta == rhs.delta; + return Transform::operator==(rhs) && min == rhs.min && delta == rhs.delta; } }; - using bin_type = interval_view; public: /** Construct axis with n bins over real range [begin, end). @@ -262,7 +265,8 @@ class variable : public base, using metadata_type = MetaData; using bin_type = interval_view; - struct data : Allocator // empty base class optimization + struct data : Allocator // empty base class optimization, + // MSVC fails if allocator_type is used here { typename std::allocator_traits::pointer x = nullptr; @@ -480,7 +484,9 @@ class category : public base, using allocator_type = Allocator; using bin_type = value_view; - struct data : Allocator { + struct data : Allocator // empty base class optimization, + // MSVC fails if allocator_type is used here + { typename std::allocator_traits::pointer x = nullptr; using Allocator::Allocator; data(const Allocator& a) : Allocator(a) {} From 964059484c760abdc3fba40ec573e791b290b75c Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 24 Oct 2018 01:28:29 +0200 Subject: [PATCH 20/44] fix win --- include/boost/histogram/histogram_fwd.hpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 0144d19c..3de54686 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -7,9 +7,14 @@ #ifndef BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP #define BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP -#include // for std::allocator +#include // for std::allocator #include +namespace std { +template +class allocator; +} + namespace boost { namespace histogram { namespace axis { @@ -32,10 +37,9 @@ template struct pow; template struct quantity; -} // namespace transform +} // namespace transform -template , - typename MetaData = std::string> +template , typename MetaData = std::string> class regular; template @@ -54,7 +58,7 @@ class category; template class variant; -} // namespace axis +} // namespace axis template > struct adaptive_storage; @@ -66,7 +70,7 @@ using default_storage = adaptive_storage<>; template class histogram; -} // namespace histogram -} // namespace boost +} // namespace histogram +} // namespace boost #endif From 81bbbf8b86d68c939a53b1affb29cebc3644a0e6 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 00:23:41 +0200 Subject: [PATCH 21/44] refactoring axis types into separate functions, make them use compressed_pair instead of crappy custom empty base optimization --- examples/getting_started_listing_02.cpp | 31 +- include/boost/histogram.hpp | 6 +- include/boost/histogram/axis/base.hpp | 117 +--- include/boost/histogram/axis/category.hpp | 155 +++++ include/boost/histogram/axis/circular.hpp | 92 +++ include/boost/histogram/axis/integer.hpp | 86 +++ .../boost/histogram/axis/interval_view.hpp | 24 +- include/boost/histogram/axis/iterator.hpp | 43 +- .../histogram/axis/ostream_operators.hpp | 94 ++- include/boost/histogram/axis/regular.hpp | 169 +++++ include/boost/histogram/axis/types.hpp | 600 ------------------ include/boost/histogram/axis/value_view.hpp | 16 +- include/boost/histogram/axis/variable.hpp | 182 ++++++ include/boost/histogram/axis/variant.hpp | 243 +++---- .../histogram/detail/compressed_pair.hpp | 74 +++ include/boost/histogram/detail/meta.hpp | 95 ++- include/boost/histogram/histogram_fwd.hpp | 16 +- include/boost/histogram/serialization.hpp | 67 +- test/axis_test.cpp | 198 +++--- test/histogram_dynamic_test.cpp | 8 +- test/histogram_serialization_test.cpp | 23 +- test/meta_test.cpp | 81 ++- 22 files changed, 1264 insertions(+), 1156 deletions(-) create mode 100644 include/boost/histogram/axis/category.hpp create mode 100644 include/boost/histogram/axis/circular.hpp create mode 100644 include/boost/histogram/axis/integer.hpp create mode 100644 include/boost/histogram/axis/regular.hpp delete mode 100644 include/boost/histogram/axis/types.hpp create mode 100644 include/boost/histogram/axis/variable.hpp create mode 100644 include/boost/histogram/detail/compressed_pair.hpp diff --git a/examples/getting_started_listing_02.cpp b/examples/getting_started_listing_02.cpp index ce95ee23..d4c61d4b 100644 --- a/examples/getting_started_listing_02.cpp +++ b/examples/getting_started_listing_02.cpp @@ -13,22 +13,18 @@ int main() { - in addition, the factory also accepts iterators over a sequence of axis::variant, the polymorphic type that can hold concrete axis types */ - std::vector< - bh::axis::variant< - bh::axis::regular<>, - bh::axis::category - > - > axes; + std::vector, bh::axis::category > > + axes; axes.emplace_back(bh::axis::category({"red", "blue"})); - axes.emplace_back(bh::axis::regular<>(5, 0, 1, "x")); - axes.emplace_back(bh::axis::regular<>(5, 0, 1, "y")); + axes.emplace_back(bh::axis::regular<>(3, 0, 1, "x")); + axes.emplace_back(bh::axis::regular<>(3, 0, 1, "y")); auto h = bh::make_histogram(axes.begin(), axes.end()); // fill histogram with data, usually this happens in a loop h("red", 0.1, 0.2); - h("blue", 0.3, 0.4); - h("red", 0.5, 0.6); - h("red", 0.7, 0.8); + h("blue", 0.7, 0.3); + h("red", 0.3, 0.7); + h("red", 0.7, 0.7); /* Print dynamic histogram by iterating over bins. @@ -42,14 +38,17 @@ int main() { for (auto xbin : h.axis(1)) { // columns const auto v = h.at(cbin, xbin, ybin); if (v) - std::printf("%4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n", - cbin.value().c_str(), - xbin.lower(), xbin.upper(), - ybin.lower(), ybin.upper(), - v); + std::printf("(%i, %i, %i) %4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n", + cbin.idx(), xbin.idx(), ybin.idx(), cbin.value().c_str(), + xbin.lower(), xbin.upper(), ybin.lower(), ybin.upper(), v); } } } + + assert(h.at(0, 0, 0) == 1); + assert(h.at(0, 0, 2) == 1); + assert(h.at(0, 2, 2) == 1); + assert(h.at(1, 2, 0) == 1); } //] diff --git a/include/boost/histogram.hpp b/include/boost/histogram.hpp index 5e05664d..43f0b9ff 100644 --- a/include/boost/histogram.hpp +++ b/include/boost/histogram.hpp @@ -7,8 +7,12 @@ #ifndef BOOST_HISTOGRAM_HPP #define BOOST_HISTOGRAM_HPP +#include +#include +#include +#include +#include #include -#include #include #include #include diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index 78b5c3c9..1e79ddae 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -7,13 +7,11 @@ #ifndef BOOST_HISTOGRAM_AXIS_BASE_HPP #define BOOST_HISTOGRAM_AXIS_BASE_HPP -#include -#include #include +#include #include -#include -#include #include +#include #include namespace boost { @@ -22,107 +20,62 @@ namespace axis { /// Base class for all axes template -class base -{ +class base { using metadata_type = MetaData; - struct data : metadata_type // empty base class optimization - { - int size = 0; - option_type opt = option_type::none; - - data() = default; - data(const data&) = default; - data& operator=(const data&) = default; - data(data&& rhs) - : metadata_type(std::move(rhs)) - , size(rhs.size), opt(rhs.opt) - { rhs.size = 0; rhs.opt = option_type::none; } - data& operator=(data&& rhs) { - if (this != &rhs) { - metadata_type::operator=(std::move(rhs)); - size = rhs.size; - opt = rhs.opt; - rhs.size = 0; - rhs.opt = option_type::none; - } - return *this; - } - data(metadata_type&& m, int s, option_type o) - : metadata_type(std::move(m)), size(s), opt(o) {} - - bool operator==(const data& rhs) const noexcept { - return size == rhs.size && opt == rhs.opt && - detail::static_if>( - [&rhs](const auto& m) { return m == rhs; }, - [](const auto&) { return true; }, - static_cast(*this)); - } - }; public: /// Returns the number of bins, without extra bins. - unsigned size() const noexcept { return data_.size; } + unsigned size() const noexcept { return size_meta_.first(); } /// Returns the options. - option_type options() const noexcept { return data_.opt; } + option_type options() const noexcept { return opt_; } /// Returns the metadata. - metadata_type& metadata() noexcept { return static_cast(data_); } + metadata_type& metadata() noexcept { return size_meta_.second(); } /// Returns the metadata (const version). - const metadata_type& metadata() const noexcept { return static_cast(data_); } + const metadata_type& metadata() const noexcept { return size_meta_.second(); } friend void swap(base& a, base& b) noexcept // ADL works with friend functions { - std::swap(static_cast(a), static_cast(b)); - std::swap(a.data_.size, b.data_.size); - std::swap(a.data_.opt, b.data_.opt); + using std::swap; + swap(a.size_meta_, b.size_meta_); + swap(a.opt_, b.opt_); } template void serialize(Archive&, unsigned); protected: - base(unsigned size, metadata_type&& m, option_type opt) - : data_(std::move(m), size, opt) - { - if (size == 0) { throw std::invalid_argument("bins > 0 required"); } - const auto max_index = static_cast(std::numeric_limits::max() - - static_cast(data_.opt)); - if (size > max_index) - throw std::invalid_argument( - detail::cat("bins <= ", max_index, " required") - ); + base(unsigned n, metadata_type&& m, option_type opt) + : size_meta_(n, std::move(m)), opt_(opt) { + if (size() == 0) { throw std::invalid_argument("bins > 0 required"); } + const auto max_index = + static_cast(std::numeric_limits::max() - static_cast(opt_)); + if (size() > max_index) + throw std::invalid_argument(detail::cat("bins <= ", max_index, " required")); } - base() = default; + base() : size_meta_(0), opt_(option_type::none) {} + base(const base&) = default; + base& operator=(const base&) = default; + base(base&& rhs) : size_meta_(std::move(rhs.size_meta_)), opt_(rhs.opt_) {} + base& operator=(base&& rhs) { + if (this != &rhs) { + size_meta_ = std::move(rhs.size_meta_); + opt_ = rhs.opt_; + } + return *this; + } bool operator==(const base& rhs) const noexcept { - return data_ == rhs.data_; + return size() == rhs.size() && opt_ == rhs.opt_ && + detail::static_if>( + [&rhs](const auto& m) { return m == rhs.metadata(); }, + [](const auto&) { return true; }, metadata()); } private: - data data_; -}; - -/// Uses CRTP to inject iterator logic into Derived. -template -class iterator_mixin { -public: - using const_iterator = iterator_over; - using const_reverse_iterator = boost::reverse_iterator; - - const_iterator begin() const noexcept { - return const_iterator(*static_cast(this), 0); - } - const_iterator end() const noexcept { - return const_iterator(*static_cast(this), - static_cast(this)->size()); - } - const_reverse_iterator rbegin() const noexcept { - return boost::make_reverse_iterator(end()); - } - const_reverse_iterator rend() const noexcept { - return boost::make_reverse_iterator(begin()); - } -}; + detail::compressed_pair size_meta_; + option_type opt_; +}; // namespace axis } // namespace axis } // namespace histogram diff --git a/include/boost/histogram/axis/category.hpp b/include/boost/histogram/axis/category.hpp new file mode 100644 index 00000000..f62b2f46 --- /dev/null +++ b/include/boost/histogram/axis/category.hpp @@ -0,0 +1,155 @@ +// 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_AXIS_CATEGORY_HPP +#define BOOST_HISTOGRAM_AXIS_CATEGORY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { +namespace axis { +/** Axis which maps unique values to bins (one on one). + * + * The axis maps a set of values to bins, following the order of + * arguments in the constructor. There is an optional overflow bin + * for this axis, which counts values that are not part of the set. + * Binning is a O(n) operation for n values in the worst case and O(1) in + * the best case. The value types must be equal-comparable. + */ +template +class category : public base, + public iterator_mixin> { + using base_type = base; + using metadata_type = MetaData; + using value_type = T; + using allocator_type = Allocator; + +public: + /** Construct from iterator range of unique values. + * + * \param begin begin of category range of unique values. + * \param end end of category range of unique values. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template > + category(It begin, It end, metadata_type m = metadata_type(), + option_type o = option_type::overflow, allocator_type a = allocator_type()) + : base_type(std::distance(begin, end), std::move(m), o), x_(nullptr, std::move(a)) { + x_.first() = detail::create_buffer_from_iter(x_.second(), base_type::size(), begin); + } + + /** Construct axis from iterable sequence of unique values. + * + * \param seq sequence of unique values. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template > + category(const C& iterable, metadata_type m = metadata_type(), + option_type o = option_type::overflow, allocator_type a = allocator_type()) + : category(std::begin(iterable), std::end(iterable), std::move(m), o, + std::move(a)) {} + + /** Construct axis from an initializer list of unique values. + * + * \param seq sequence of unique values. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template + category(std::initializer_list l, metadata_type m = metadata_type(), + option_type o = option_type::overflow, allocator_type a = allocator_type()) + : category(l.begin(), l.end(), std::move(m), o, std::move(a)) {} + + category() : x_(nullptr) {} + + category(const category& o) : base_type(o), x_(o.x_) { + x_.first() = + detail::create_buffer_from_iter(x_.second(), base_type::size(), o.x_.first()); + } + + category& operator=(const category& o) { + if (this != &o) { + if (base_type::size() != o.size()) { + detail::destroy_buffer(x_.second(), x_.first(), base_type::size()); + base_type::operator=(o); + x_ = o.x_; + x_.first() = + detail::create_buffer_from_iter(x_.second(), base_type::size(), o.x_.first()); + } else { + base_type::operator=(o); + std::copy(o.x_.first(), o.x_.first() + base_type::size(), x_.first()); + } + } + return *this; + } + + category(category&& o) : category() { + using std::swap; + swap(static_cast(*this), static_cast(o)); + swap(x_, o.x_); + } + + category& operator=(category&& o) { + if (this != &o) { + using std::swap; + swap(static_cast(*this), static_cast(o)); + swap(x_, o.x_); + } + return *this; + } + + ~category() { detail::destroy_buffer(x_.second(), x_.first(), base_type::size()); } + + /// Returns the bin index for the passed argument. + int operator()(const value_type& x) const noexcept { + const auto begin = x_.first(); + const auto end = begin + base_type::size(); + return std::distance(begin, std::find(begin, end, x)); + } + + /// Returns the value for the bin index (performs a range check). + const value_type& value(unsigned idx) const { + if (idx >= base_type::size()) throw std::out_of_range("category index out of range"); + return x_.first()[idx]; + } + + auto operator[](int idx) const noexcept { return value_view(idx, *this); } + + bool operator==(const category& o) const noexcept { + return base_type::operator==(o) && + std::equal(x_.first(), x_.first() + base_type::size(), o.x_.first()); + } + + bool operator!=(const category& o) const noexcept { return !operator==(o); } + + template + void serialize(Archive&, unsigned); + +private: + using pointer = typename std::allocator_traits::pointer; + detail::compressed_pair x_; +}; +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/circular.hpp b/include/boost/histogram/axis/circular.hpp new file mode 100644 index 00000000..434df99d --- /dev/null +++ b/include/boost/histogram/axis/circular.hpp @@ -0,0 +1,92 @@ +// 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_AXIS_CIRCULAR_HPP +#define BOOST_HISTOGRAM_AXIS_CIRCULAR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { +namespace axis { + +// two_pi can be found in boost/math, but it is defined here to reduce deps +constexpr double two_pi = 6.283185307179586; + +/** Axis for real values on a circle. + * + * The axis is circular and wraps around reaching the perimeter value. + * It has no underflow bin and the overflow bin merely counts special + * values like NaN and infinity. Binning is a O(1) operation. + */ +template +class circular : public base, + public iterator_mixin> { + using base_type = base; + using value_type = RealType; + using metadata_type = MetaData; + +public: + /** Construct n bins with an optional offset. + * + * \param n number of bins. + * \param phase starting phase. + * \param perimeter range after which value wraps around. + * \param metadata description of the axis. + * \param options extra bin options. + */ + circular(unsigned n, RealType phase = 0, RealType perimeter = two_pi, + MetaData m = MetaData(), option_type o = option_type::overflow) + : base_type(n, std::move(m), + o == option_type::underflow_and_overflow ? option_type::overflow : o) + , phase_(phase) + , delta_(perimeter / n) { + if (!std::isfinite(phase) || !(perimeter > 0)) + throw std::invalid_argument("invalid phase or perimeter"); + } + + circular() = default; + + /// Returns the bin index for the passed argument. + int operator()(value_type x) const noexcept { + const auto z = std::floor((x - phase_) / delta_); + if (std::isfinite(z)) { + const auto i = static_cast(z) % base_type::size(); + return i + (i < 0) * base_type::size(); + } + return base_type::size(); + } + + /// Returns axis value for fractional index. + value_type value(value_type i) const noexcept { return phase_ + i * delta_; } + + auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + + bool operator==(const circular& o) const noexcept { + return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_; + } + + bool operator!=(const circular<>& o) const noexcept { return !operator==(o); } + + template + void serialize(Archive&, unsigned); + +private: + value_type phase_ = 0.0, delta_ = 1.0; +}; +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/integer.hpp b/include/boost/histogram/axis/integer.hpp new file mode 100644 index 00000000..d2b30816 --- /dev/null +++ b/include/boost/histogram/axis/integer.hpp @@ -0,0 +1,86 @@ +// 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_AXIS_INTEGER_HPP +#define BOOST_HISTOGRAM_AXIS_INTEGER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { +namespace axis { +/** Axis for an interval of integer values with unit steps. + * + * Binning is a O(1) operation. This axis operates + * faster than a regular axis. + */ +template +class integer : public base, public iterator_mixin> { + using base_type = base; + using value_type = IntType; + using metadata_type = MetaData; + using bin_type = interval_view; + +public: + /** Construct over semi-open integer interval [start, stop). + * + * \param start first integer of covered range. + * \param stop one past last integer of covered range. + * \param metadata description of the axis. + * \param options extra bin options. + */ + integer(value_type start, value_type stop, metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow) + : base_type(stop - start, std::move(m), o), min_(start) { + if (start >= stop) { throw std::invalid_argument("start < stop required"); } + } + + integer() = default; + + /// Returns the bin index for the passed argument. + int operator()(value_type x) const noexcept { + x = std::floor(x - min_); + return x >= 0 ? (x > static_cast(base_type::size()) ? base_type::size() + : static_cast(x)) + : -1; + } + + /// Returns axis value for index. + value_type value(value_type i) const noexcept { + if (i < 0) { return std::numeric_limits::min(); } + if (i > static_cast(base_type::size())) { + return std::numeric_limits::max(); + } + return min_ + i; + } + + bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } + + bool operator==(const integer& o) const noexcept { + return base_type::operator==(o) && min_ == o.min_; + } + + bool operator!=(const integer& o) const noexcept { return !operator==(o); } + + template + void serialize(Archive&, unsigned); + +private: + value_type min_ = 0; +}; +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/interval_view.hpp b/include/boost/histogram/axis/interval_view.hpp index 3abe9e2b..1787e03b 100644 --- a/include/boost/histogram/axis/interval_view.hpp +++ b/include/boost/histogram/axis/interval_view.hpp @@ -7,8 +7,6 @@ #ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP #define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP -#include - namespace boost { namespace histogram { namespace axis { @@ -18,29 +16,17 @@ class interval_view { public: interval_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} - interval_view(const interval_view&) = default; - interval_view& operator=(const interval_view&) = default; - interval_view(interval_view&&) = default; - interval_view& operator=(interval_view&&) = default; - int idx() const noexcept { return idx_; } - auto lower() const noexcept -> decltype(std::declval().lower(0)) { - return axis_.lower(idx_); - } - auto upper() const noexcept -> decltype(std::declval().lower(0)) { - return axis_.lower(idx_ + 1); - } - auto width() const noexcept -> decltype(upper() - lower()) { - return upper() - lower(); - } + decltype(auto) lower() const noexcept { return axis_.value(idx_); } + decltype(auto) upper() const noexcept { return axis_.value(idx_ + 1); } + decltype(auto) center() const noexcept { return axis_.value(idx_ + 0.5); } + decltype(auto) width() const noexcept { return upper() - lower(); } bool operator==(const interval_view& rhs) const noexcept { return idx_ == rhs.idx_ && axis_ == rhs.axis_; } - bool operator!=(const interval_view& rhs) const noexcept { - return !operator==(rhs); - } + bool operator!=(const interval_view& rhs) const noexcept { return !operator==(rhs); } explicit operator int() const noexcept { return idx_; } diff --git a/include/boost/histogram/axis/iterator.hpp b/include/boost/histogram/axis/iterator.hpp index 4c34a354..227114b0 100644 --- a/include/boost/histogram/axis/iterator.hpp +++ b/include/boost/histogram/axis/iterator.hpp @@ -8,6 +8,7 @@ #define BOOST_HISTOGRAM_AXIS_ITERATOR_HPP #include +#include namespace boost { namespace histogram { @@ -15,16 +16,11 @@ namespace axis { template class iterator_over - : public iterator_facade< - iterator_over, - decltype(std::declval()[0]), - random_access_traversal_tag, - decltype(std::declval()[0]), - int - > { + : public iterator_facade, decltype(std::declval()[0]), + random_access_traversal_tag, + decltype(std::declval()[0]), int> { public: - explicit iterator_over(const Axis& axis, int idx) - : axis_(axis), idx_(idx) {} + explicit iterator_over(const Axis& axis, int idx) : axis_(axis), idx_(idx) {} iterator_over(const iterator_over&) = default; iterator_over& operator=(const iterator_over&) = default; @@ -33,14 +29,11 @@ protected: void increment() noexcept { ++idx_; } void decrement() noexcept { --idx_; } void advance(int n) noexcept { idx_ += n; } - int distance_to(const iterator_over& other) const noexcept { - return other.idx_ - idx_; - } + int distance_to(const iterator_over& other) const noexcept { return other.idx_ - idx_; } bool equal(const iterator_over& other) const noexcept { return &axis_ == &other.axis_ && idx_ == other.idx_; } - decltype(std::declval()[0]) - dereference() const { return axis_[idx_]; } + decltype(std::declval()[0]) dereference() const { return axis_[idx_]; } friend class ::boost::iterator_core_access; @@ -48,6 +41,28 @@ protected: int idx_; }; +/// Uses CRTP to inject iterator logic into Derived. +template +class iterator_mixin { +public: + using const_iterator = iterator_over; + using const_reverse_iterator = boost::reverse_iterator; + + const_iterator begin() const noexcept { + return const_iterator(*static_cast(this), 0); + } + const_iterator end() const noexcept { + return const_iterator(*static_cast(this), + static_cast(this)->size()); + } + const_reverse_iterator rbegin() const noexcept { + return boost::make_reverse_iterator(end()); + } + const_reverse_iterator rend() const noexcept { + return boost::make_reverse_iterator(begin()); + } +}; + } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 89be86cc..6773096d 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -10,9 +10,13 @@ #define BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP #include +#include +#include +#include #include -#include +#include #include +#include #include #include #include @@ -39,10 +43,6 @@ template const char* to_string(const axis::transform::pow&) { return "_pow"; } -template -const char* to_string(const axis::transform::quantity&) { - return "_quantity"; -} template void stream_metadata(OStream& os, const T& t) { @@ -83,40 +83,45 @@ void stream_transform(OStream& os, const axis::transform::pow& t) { os << ", power=" << t.power; } -template -void stream_transform(OStream& os, const axis::transform::quantity& t) { - os << ", unit=" << t.unit; +template +void stream_value(OStream& os, const T& t) { + os << t; +} + +template +void stream_value(OStream& os, const std::basic_string& t) { + os << std::quoted(t); } } // namespace detail namespace axis { -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const empty_metadata_type&) { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const empty_metadata_type&) { return os; // do nothing } -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const interval_view& i) { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const interval_view& i) { os << "[" << i.lower() << ", " << i.upper() << ")"; return os; } -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const value_view& i) { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const value_view& i) { os << i.value(); return os; } -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const regular& a) { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const regular& a) { os << "regular" << detail::to_string(a.transform()) << "(" << a.size() << ", " - << a.lower(0) << ", " << a.lower(a.size()); + << a.value(0) << ", " << a.value(a.size()); detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); detail::stream_transform(os, a.transform()); @@ -124,56 +129,43 @@ std::basic_ostream& operator<<(std::basic_ostream& return os; } -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const circular& a) { - os << "circular(" << a.size() << ", " << a.lower(0) << ", " << a.lower(a.size()); +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const circular& a) { + os << "circular(" << a.size() << ", " << a.value(0) << ", " << a.value(a.size()); detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); os << ")"; return os; } -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const variable& a) { - os << "variable(" << a.lower(0); - for (unsigned i = 1; i <= a.size(); ++i) { os << ", " << a.lower(i); } +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const variable& a) { + os << "variable(" << a.value(0); + for (unsigned i = 1; i <= a.size(); ++i) { os << ", " << a.value(i); } detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); os << ")"; return os; } -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const integer& a) { - os << "integer(" << a.lower(0) << ", " << a.lower(a.size()); +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const integer& a) { + os << "integer(" << a.value(0) << ", " << a.value(a.size()); detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); os << ")"; return os; } -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const category& a) { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const category& a) { os << "category("; for (unsigned i = 0; i < a.size(); ++i) { - os << a[i] << (i == (a.size() - 1) ? "" : ", "); - } - detail::stream_metadata(os, a.metadata()); - detail::stream_options(os, a.options()); - os << ")"; - return os; -} - -template -std::basic_ostream& operator<<(std::basic_ostream& os, - const category& a) { - os << "category("; - for (unsigned i = 0; i < a.size(); ++i) { - os << std::quoted(a.value(i)); + detail::stream_value(os, a.value(i)); os << (i == (a.size() - 1) ? "" : ", "); } detail::stream_metadata(os, a.metadata()); diff --git a/include/boost/histogram/axis/regular.hpp b/include/boost/histogram/axis/regular.hpp new file mode 100644 index 00000000..29ac4bd5 --- /dev/null +++ b/include/boost/histogram/axis/regular.hpp @@ -0,0 +1,169 @@ +// 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_AXIS_REGULAR_HPP +#define BOOST_HISTOGRAM_AXIS_REGULAR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { +namespace axis { +namespace transform { +template +struct identity { + static T forward(T x) { return x; } + static T inverse(T x) { return x; } + + bool operator==(const identity&) const noexcept { return true; } + template + void serialize(Archive&, unsigned) {} // noop +}; + +template +struct log : identity { + static T forward(T x) { return std::log(x); } + static T inverse(T x) { return std::exp(x); } +}; + +template +struct sqrt : identity { + static T forward(T x) { return std::sqrt(x); } + static T inverse(T x) { return x * x; } +}; + +template +struct pow { + using U = mp11::mp_if, double, T>; + U power = 1; + + pow() = default; + pow(U p) : power(p) {} + + U forward(U v) const { return std::pow(v, power); } + U inverse(U v) const { return std::pow(v, 1.0 / power); } + + bool operator==(const pow& o) const noexcept { return power == o.power; } + template + void serialize(Archive&, unsigned); +}; +} // namespace transform + +/** Axis for equidistant intervals on the real line. + * + * The most common binning strategy. + * Very fast. Binning is a O(1) operation. + */ +template +class regular : public base, + public iterator_mixin>, + protected Transform { + using base_type = base; + using transform_type = Transform; + using external_type = detail::return_type; + using internal_type = detail::return_type; + static_assert(!std::is_integral::value, + "type returned by forward transform cannot be integral"); + using metadata_type = MetaData; + +public: + /** Construct n bins over real transformed range [begin, end). + * + * \param trans transform instance to use. + * \param n number of bins. + * \param start low edge of first bin. + * \param stop high edge of last bin. + * \param metadata description of the axis. + * \param options extra bin options. + */ + regular(transform_type trans, unsigned n, external_type start, external_type stop, + metadata_type m = {}, option_type o = option_type::underflow_and_overflow) + : base_type(n, std::move(m), o) + , transform_type(std::move(trans)) + , min_(this->forward(start)) + , delta_((this->forward(stop) - this->forward(start)) / n) { + if (!std::isfinite(min_) || !std::isfinite(delta_)) + throw std::invalid_argument("forward transform of lower or upper invalid"); + } + + /** Construct n bins over real range [begin, end). + * + * \param n number of bins. + * \param start low edge of first bin. + * \param stop high edge of last bin. + * \param metadata description of the axis. + * \param options extra bin options. + */ + regular(unsigned n, external_type start, external_type stop, metadata_type m = {}, + option_type o = option_type::underflow_and_overflow) + : regular({}, n, start, stop, std::move(m), o) {} + + regular() = default; + + /// Returns instance of the transform type + const transform_type& transform() const { return *this; } + + /// Returns the bin index for the passed argument. + int operator()(external_type x) const noexcept { + // Runs in hot loop, please measure impact of changes + const auto z = (this->forward(x) - min_) / delta_; + if (z < base_type::size()) { + if (z >= 0) + return static_cast(z); + else + return -1; + } + return base_type::size(); // also returned if z is NaN + + // const auto lt_max = z < base_type::size(); + // const auto ge_zero = z >= 0; + // return lt_max * (ge_zero * static_cast(z) - !ge_zero) + !lt_max * + // base_type::size(); + } + + /// Returns axis value for fractional index. + external_type value(internal_type i) const noexcept { + i /= base_type::size(); + if (i < 0) + i = std::copysign(std::numeric_limits::infinity(), -delta_); + else if (i > 1) + i = std::copysign(std::numeric_limits::infinity(), delta_); + else { + i = (1 - i) * min_ + i * (min_ + delta_ * base_type::size()); + } + return this->inverse(i); + } + + /// Access bin at index + auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + + bool operator==(const regular& o) const noexcept { + return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ && + delta_ == o.delta_; + } + + bool operator!=(const regular& o) const noexcept { return !operator==(o); } + + template + void serialize(Archive&, unsigned); + +private: + internal_type min_, delta_; +}; +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp deleted file mode 100644 index d2938c32..00000000 --- a/include/boost/histogram/axis/types.hpp +++ /dev/null @@ -1,600 +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_AXIS_TYPES_HPP -#define BOOST_HISTOGRAM_AXIS_TYPES_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace histogram { - -namespace axis { - -namespace transform { - -template -struct identity { - static T forward(T x) { return x; } - static T inverse(T x) { return x; } - - constexpr bool operator==(const identity&) const noexcept { return true; } - template - void serialize(Archive&, unsigned) {} // noop -}; - -template -struct log : public identity { - static T forward(T x) { return std::log(x); } - static T inverse(T x) { return std::exp(x); } -}; - -template -struct sqrt : public identity { - static T forward(T x) { return std::sqrt(x); } - static T inverse(T x) { return x * x; } -}; - -template -struct pow { - T power = 1.0; - - pow() = default; - pow(T p) : power(p) {} - - T forward(T v) const { return std::pow(v, power); } - T inverse(T v) const { return std::pow(v, 1.0 / power); } - - bool operator==(const pow& o) const noexcept { return power == o.power; } - template - void serialize(Archive&, unsigned); -}; - -template -struct quantity { - Unit unit; - - quantity(const Unit& u) : unit(u) {} - - using Dimensionless = decltype(std::declval() / std::declval()); - - Dimensionless forward(Quantity x) const { return x / unit; } - Quantity inverse(Dimensionless x) const { return x * unit; } - - bool operator==(const quantity& o) const noexcept { return unit == o.unit; } - template - void serialize(Archive&, unsigned); -}; -} // namespace transform - -/** Axis for equidistant intervals on the real line. - * - * The most common binning strategy. - * Very fast. Binning is a O(1) operation. - */ -template -class regular : public base, - public iterator_mixin> { - using base_type = base; - using transform_type = Transform; - using value_type = detail::arg_type<-1, decltype(&transform_type::forward)>; - using internal_type = detail::return_type; - static_assert(std::is_floating_point::value, - "type returned by forward transform must be floating point"); - using metadata_type = MetaData; - using bin_type = interval_view; - - struct data : Transform // empty base class optimization, - // MSVC fails if transform_type is used here - - { - internal_type min = 0, delta = 1; - - data(const Transform& t, unsigned n, value_type b, value_type e) - : Transform(t) - , min(this->forward(b)) - , delta((this->forward(e) - this->forward(b)) / n) {} - - data() = default; - - bool operator==(const data& rhs) const noexcept { - return Transform::operator==(rhs) && min == rhs.min && delta == rhs.delta; - } - }; - -public: - /** Construct axis with n bins over real range [begin, end). - * - * \param n number of bins. - * \param start low edge of first bin. - * \param stop high edge of last bin. - * \param metadata description of the axis. - * \param options extra bin options. - * \param trans transform instance to use. - */ - regular(unsigned n, value_type start, value_type stop, - metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow, - transform_type trans = transform_type()) - : base_type(n, std::move(m), o), data_(std::move(trans), n, start, stop) { - if (!std::isfinite(data_.min) || !std::isfinite(data_.delta)) - throw std::invalid_argument("forward transform of lower or upper invalid"); - } - - regular() = default; - - /// Returns the bin index for the passed argument. - int operator()(value_type x) const noexcept { - // Runs in hot loop, please measure impact of changes - const auto z = (data_.forward(x) - data_.min) / data_.delta; - if (z < base_type::size()) { - if (z >= 0) - return static_cast(z); - else - return -1; - } - return base_type::size(); // also returned if z is NaN - - // const auto lt_max = z < base_type::size(); - // const auto ge_zero = z >= 0; - // return lt_max * (ge_zero * static_cast(z) - !ge_zero) + !lt_max * - // base_type::size(); - } - - /// Returns lower edge of bin. - value_type lower(int idx) const noexcept { - const auto z = internal_type(idx) / base_type::size(); - internal_type x; - if (z < 0) - x = -std::numeric_limits::infinity(); - else if (z > 1) - x = std::numeric_limits::infinity(); - else { - x = (1 - z) * data_.min + z * (data_.min + data_.delta * base_type::size()); - } - return data_.inverse(x); - } - - const transform_type& transform() const { return data_; } - - bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } - - bool operator==(const regular& o) const noexcept { - return base_type::operator==(o) && data_ == o.data_; - } - - template - void serialize(Archive&, unsigned); - -private: - data data_; -}; - -/** Axis for real values on a circle. - * - * The axis is circular and wraps around reaching the - * perimeter value. Therefore, there are no overflow/underflow - * bins for this axis. Binning is a O(1) operation. - */ -template -class circular : public base, - public iterator_mixin> { - using base_type = base; - using value_type = RealType; - using metadata_type = MetaData; - using bin_type = interval_view; - -public: - // two_pi can be found in boost/math, but it is defined here to reduce deps - static constexpr value_type two_pi() { return 6.283185307179586; } - - /** Constructor for n bins with an optional offset. - * - * \param n number of bins. - * \param phase starting phase. - * \param perimeter range after which value wraps around. - * \param metadata description of the axis. - * \param options extra bin options. - */ - explicit circular(unsigned n, value_type phase = 0.0, value_type perimeter = two_pi(), - metadata_type m = metadata_type(), - option_type o = option_type::overflow) - : base_type(n, std::move(m), - o == option_type::underflow_and_overflow ? option_type::overflow : o) - , phase_(phase) - , delta_(perimeter / n) { - if (!std::isfinite(phase) || !(perimeter > 0)) - throw std::invalid_argument("invalid phase or perimeter"); - } - - circular() = default; - - /// Returns the bin index for the passed argument. - int operator()(value_type x) const noexcept { - const auto z = std::floor((x - phase_) / delta_); - if (std::isfinite(z)) { - const auto i = static_cast(z) % base_type::size(); - return i + (i < 0) * base_type::size(); - } - return base_type::size(); - } - - /// Returns lower edge of bin. - value_type lower(int i) const noexcept { return phase_ + i * delta_; } - - bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } - - bool operator==(const circular& o) const noexcept { - return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_; - } - - template - void serialize(Archive&, unsigned); - -private: - value_type phase_ = 0.0, delta_ = 1.0; -}; - -/** Axis for non-equidistant bins on the real line. - * - * Binning is a O(log(N)) operation. If speed matters and the problem - * domain allows it, prefer a regular axis, possibly with a transform. - */ -template -class variable : public base, - public iterator_mixin> { - using base_type = base; - using value_type = RealType; - using allocator_type = Allocator; - using metadata_type = MetaData; - using bin_type = interval_view; - - struct data : Allocator // empty base class optimization, - // MSVC fails if allocator_type is used here - { - typename std::allocator_traits::pointer x = nullptr; - - using Allocator::Allocator; - - data(const Allocator& a) : Allocator(a) {} - data() = default; - - friend void swap(data& a, data& b) noexcept { - std::swap(a.x, b.x); - auto tmp = static_cast(a); - a = static_cast(b); - b = static_cast(tmp); - } - }; - -public: - /** Construct an axis from iterator range of bin edges. - * - * \param begin begin of edge sequence. - * \param end end of edge sequence. - * \param metadata description of the axis. - * \param options extra bin options. - * \param allocator allocator instance to use. - */ - template > - variable(Iterator begin, Iterator end, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow, - allocator_type a = allocator_type()) - : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o) - , data_(std::move(a)) { - using AT = std::allocator_traits; - data_.x = AT::allocate(data_, nx()); - try { - auto xit = data_.x; - try { - AT::construct(data_, xit, *begin++); - while (begin != end) { - if (*begin <= *xit) { - ++xit; // to make sure catch code works - throw std::invalid_argument("input sequence must be strictly ascending"); - } - ++xit; - AT::construct(data_, xit, *begin++); - } - } catch (...) { - // release resources that were already acquired before rethrowing - while (xit != data_.x) AT::destroy(data_, --xit); - throw; - } - } catch (...) { - // release resources that were already acquired before rethrowing - AT::deallocate(data_, data_.x, nx()); - throw; - } - } - - /** Construct an axis from iterable range of bin edges. - * - * \param iterable iterable range of bin edges. - * \param metadata description of the axis. - * \param options extra bin options. - * \param allocator allocator instance to use. - */ - template > - variable(const T& t, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow, - allocator_type a = allocator_type()) - : variable(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} - - template - variable(std::initializer_list t, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow, - allocator_type a = allocator_type()) - : variable(t.begin(), t.end(), std::move(m), o, std::move(a)) {} - - variable() = default; - - variable(const variable& o) : base_type(o), data_(o.data_) { - data_.x = detail::create_buffer_from_iter(data_, nx(), o.data_.x); - } - - variable& operator=(const variable& o) { - if (this != &o) { - if (base_type::size() != o.size()) { - detail::destroy_buffer(data_, data_.x, nx()); - data_ = o.data_; - base_type::operator=(o); - data_.x = detail::create_buffer_from_iter(data_, nx(), o.data_.x); - } else { - base_type::operator=(o); - std::copy(o.data_.x, o.data_.x + nx(), data_.x); - } - } - return *this; - } - - variable(variable&& o) : base_type(std::move(o)), data_(std::move(o.data_)) { - o.data_.x = nullptr; - } - - variable& operator=(variable&& o) { - if (this != &o) { - std::swap(static_cast(*this), static_cast(o)); - std::swap(data_, o.data_); - } - return *this; - } - - ~variable() { detail::destroy_buffer(data_, data_.x, nx()); } - - /// Returns the bin index for the passed argument. - int operator()(value_type x) const noexcept { - return std::upper_bound(data_.x, data_.x + nx(), x) - data_.x - 1; - } - - /// Returns the starting edge of the bin. - value_type lower(int i) const noexcept { - if (i < 0) { return -std::numeric_limits::infinity(); } - if (i > static_cast(base_type::size())) { - return std::numeric_limits::infinity(); - } - return data_.x[i]; - } - - bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } - - bool operator==(const variable& o) const noexcept { - return base_type::operator==(o) && std::equal(data_.x, data_.x + nx(), o.data_.x); - } - - template - void serialize(Archive&, unsigned); - -private: - int nx() const { return base_type::size() + 1; } - data data_; -}; - -/** Axis for an interval of integral values with unit steps. - * - * Binning is a O(1) operation. This axis operates - * faster than a regular. - */ -template -class integer : public base, public iterator_mixin> { - using base_type = base; - using value_type = IntType; - using metadata_type = MetaData; - using bin_type = interval_view; - -public: - /** Construct axis over a semi-open integer interval [begin, end). - * - * \param begin first integer of covered range. - * \param end one past last integer of covered range. - * \param metadata description of the axis. - * \param options extra bin options. - */ - integer(value_type begin, value_type end, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow) - : base_type(end - begin, std::move(m), o), min_(begin) { - if (begin >= end) { throw std::invalid_argument("begin < end required"); } - } - - integer() = default; - integer(const integer&) = default; - integer& operator=(const integer&) = default; - integer(integer&&) = default; - integer& operator=(integer&&) = default; - - /// Returns the bin index for the passed argument. - int operator()(value_type x) const noexcept { - const int z = x - min_; - return z >= 0 ? (z > static_cast(base_type::size()) ? base_type::size() : z) - : -1; - } - - /// Returns lower edge of the integral bin. - value_type lower(int i) const noexcept { - if (i < 0) { return std::numeric_limits::min(); } - if (i > static_cast(base_type::size())) { - return std::numeric_limits::max(); - } - return min_ + i; - } - - bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } - - bool operator==(const integer& o) const noexcept { - return base_type::operator==(o) && min_ == o.min_; - } - - template - void serialize(Archive&, unsigned); - -private: - value_type min_ = 0; -}; - -/** Axis which maps unique values to bins (one on one). - * - * The axis maps a set of values to bins, following the order of - * arguments in the constructor. There is an optional overflow bin - * for this axis, which counts values that are not part of the set. - * Binning is a O(n) operation for n values in the worst case and O(1) in - * the best case. The value types must be equal-comparable. - */ -template -class category : public base, - public iterator_mixin> { - using base_type = base; - using metadata_type = MetaData; - using value_type = ValueType; - using allocator_type = Allocator; - using bin_type = value_view; - - struct data : Allocator // empty base class optimization, - // MSVC fails if allocator_type is used here - { - typename std::allocator_traits::pointer x = nullptr; - using Allocator::Allocator; - data(const Allocator& a) : Allocator(a) {} - data() = default; - - friend void swap(data& a, data& b) noexcept { - std::swap(a.x, b.x); - auto tmp = static_cast(a); - a = static_cast(b); - b = static_cast(tmp); - } - }; - -public: - /** Construct an axis from iterator range of categories. - * - * \param begin begin of category range of unique values. - * \param end end of category range of unique values. - * \param metadata description of the axis. - * \param options extra bin options. - * \param allocator allocator instance to use. - */ - template > - category(Iterator begin, Iterator end, metadata_type m = metadata_type(), - option_type o = option_type::overflow, allocator_type a = allocator_type()) - : base_type(std::distance(begin, end), std::move(m), o), data_(std::move(a)) { - data_.x = detail::create_buffer_from_iter(data_, base_type::size(), begin); - } - - /** Construct from an initializer list of strings. - * - * \param seq sequence of unique values. - * \param metadata description of the axis. - */ - template > - category(const T& t, metadata_type m = metadata_type(), - option_type o = option_type::overflow, allocator_type a = allocator_type()) - : category(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} - - template - category(std::initializer_list t, metadata_type m = metadata_type(), - option_type o = option_type::overflow, allocator_type a = allocator_type()) - : category(t.begin(), t.end(), std::move(m), o, std::move(a)) {} - - category() = default; - - category(const category& o) : base_type(o), data_(o.data_) { - data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); - } - - category& operator=(const category& o) { - if (this != &o) { - if (base_type::size() != o.size()) { - detail::destroy_buffer(data_, data_.x, base_type::size()); - base_type::operator=(o); - data_ = o.data_; - data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x); - } else { - base_type::operator=(o); - std::copy(o.data_.x, o.data_.x + base_type::size(), data_.x); - } - } - return *this; - } - - category(category&& o) : base_type(std::move(o)), data_(std::move(o.data_)) { - o.data_.x = nullptr; - } - - category& operator=(category&& o) { - if (this != &o) { - std::swap(static_cast(*this), static_cast(o)); - std::swap(data_, o.data_); - } - return *this; - } - - ~category() { detail::destroy_buffer(data_, data_.x, base_type::size()); } - - /// Returns the bin index for the passed argument. - int operator()(const value_type& x) const noexcept { - const auto begin = data_.x; - const auto end = begin + base_type::size(); - return std::distance(begin, std::find(begin, end, x)); - } - - /// Returns the value for the bin index (performs a range check). - const value_type& value(int idx) const { - if (idx < 0 || idx >= static_cast(base_type::size())) - throw std::out_of_range("category index out of range"); - return data_.x[idx]; - } - - bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } - - bool operator==(const category& o) const noexcept { - return base_type::operator==(o) && - std::equal(data_.x, data_.x + base_type::size(), o.data_.x); - } - - template - void serialize(Archive&, unsigned); - -private: - data data_; -}; -} // namespace axis -} // namespace histogram -} // namespace boost - -#endif diff --git a/include/boost/histogram/axis/value_view.hpp b/include/boost/histogram/axis/value_view.hpp index 814b97fd..b24ce606 100644 --- a/include/boost/histogram/axis/value_view.hpp +++ b/include/boost/histogram/axis/value_view.hpp @@ -7,9 +7,6 @@ #ifndef BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP #define BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP -#include -#include - namespace boost { namespace histogram { namespace axis { @@ -19,23 +16,14 @@ class value_view { public: value_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} - value_view(const value_view&) = default; - value_view& operator=(const value_view&) = default; - value_view(value_view&&) = default; - value_view& operator=(value_view&&) = default; - int idx() const noexcept { return idx_; } - auto value() const -> decltype(std::declval().value(0)) { - return axis_.value(idx_); - } + decltype(auto) value() const { return axis_.value(idx_); } bool operator==(const value_view& rhs) const noexcept { return idx_ == rhs.idx_ && axis_ == rhs.axis_; } - bool operator!=(const value_view& rhs) const noexcept { - return !operator==(rhs); - } + bool operator!=(const value_view& rhs) const noexcept { return !operator==(rhs); } explicit operator int() const noexcept { return idx_; } diff --git a/include/boost/histogram/axis/variable.hpp b/include/boost/histogram/axis/variable.hpp new file mode 100644 index 00000000..e358b19b --- /dev/null +++ b/include/boost/histogram/axis/variable.hpp @@ -0,0 +1,182 @@ +// 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_AXIS_VARIABLE_HPP +#define BOOST_HISTOGRAM_AXIS_VARIABLE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { +namespace axis { +/** Axis for non-equidistant bins on the real line. + * + * Binning is a O(log(N)) operation. If speed matters and the problem + * domain allows it, prefer a regular axis, possibly with a transform. + */ +template +class variable : public base, + public iterator_mixin> { + using base_type = base; + using allocator_type = Allocator; + using metadata_type = MetaData; + using value_type = RealType; + +public: + /** Construct from iterator range of bin edges. + * + * \param begin begin of edge sequence. + * \param end end of edge sequence. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template > + variable(It begin, It end, metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o) + , x_(nullptr, std::move(a)) { + using AT = std::allocator_traits; + x_.first() = AT::allocate(x_.second(), nx()); + try { + auto xit = x_.first(); + try { + AT::construct(x_.second(), xit, *begin++); + while (begin != end) { + if (*begin <= *xit) { + ++xit; // to make sure catch code works + throw std::invalid_argument("input sequence must be strictly ascending"); + } + ++xit; + AT::construct(x_.second(), xit, *begin++); + } + } catch (...) { + // release resources that were already acquired before rethrowing + while (xit != x_.first()) AT::destroy(x_.second(), --xit); + throw; + } + } catch (...) { + // release resources that were already acquired before rethrowing + AT::deallocate(x_.second(), x_.first(), nx()); + throw; + } + } + + /** Construct variable axis from iterable range of bin edges. + * + * \param iterable iterable range of bin edges. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template > + variable(const U& iterable, metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : variable(std::begin(iterable), std::end(iterable), std::move(m), o, + std::move(a)) {} + + /** Construct variable axis from initializer list of bin edges. + * + * \param edgelist list of of bin edges. + * \param metadata description of the axis. + * \param options extra bin options. + * \param allocator allocator instance to use. + */ + template + variable(const std::initializer_list& l, metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : variable(l.begin(), l.end(), std::move(m), o, std::move(a)) {} + + variable() : x_(nullptr) {} + + variable(const variable& o) : base_type(o), x_(o.x_) { + x_.first() = detail::create_buffer_from_iter(x_.second(), nx(), o.x_.first()); + } + + variable& operator=(const variable& o) { + if (this != &o) { + if (base_type::size() == o.size()) { + base_type::operator=(o); + std::copy(o.x_.first(), o.x_.first() + nx(), x_.first()); + } else { + detail::destroy_buffer(x_.second(), x_.first(), nx()); + base_type::operator=(o); + x_.second() = o.x_.second(); + x_.first() = detail::create_buffer_from_iter(x_.second(), nx(), o.x_.first()); + } + } + return *this; + } + + variable(variable&& o) : variable() { + using std::swap; + swap(static_cast(*this), static_cast(o)); + swap(x_, o.x_); + } + + variable& operator=(variable&& o) { + if (this != &o) { + using std::swap; + swap(static_cast(*this), static_cast(o)); + swap(x_, o.x_); + } + return *this; + } + + ~variable() { detail::destroy_buffer(x_.second(), x_.first(), nx()); } + + /// Returns the bin index for the passed argument. + int operator()(value_type x) const noexcept { + const auto p = x_.first(); + return std::upper_bound(p, p + nx(), x) - p - 1; + } + + /// Returns axis value for fractional index. + value_type value(value_type i) const noexcept { + if (i < 0) { return -std::numeric_limits::infinity(); } + if (i > base_type::size()) { return std::numeric_limits::infinity(); } + value_type z; + const int k = std::modf(i, &z); + return (1.0 - z) * x_.first()[k] + z * x_.first()[k + 1]; + } + + auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + + bool operator==(const variable& o) const noexcept { + return base_type::operator==(o) && + std::equal(x_.first(), x_.first() + nx(), o.x_.first()); + } + + bool operator!=(const variable<>& o) const noexcept { return !operator==(o); } + + template + void serialize(Archive&, unsigned); + +private: + int nx() const { return base_type::size() + 1; } + using pointer = typename std::allocator_traits::pointer; + detail::compressed_pair x_; +}; +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 03e0b56d..b3e0b05e 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -7,6 +7,7 @@ #ifndef BOOST_HISTOGRAM_AXIS_VARIANT_HPP #define BOOST_HISTOGRAM_AXIS_VARIANT_HPP +#include #include #include #include @@ -16,44 +17,15 @@ #include #include #include -#include +#include #include #include #include -#include namespace boost { namespace histogram { namespace detail { - -struct call_visitor { - const double x; - call_visitor(const double arg) : x(arg) {} - template - int operator()(const T& t) const { - using args = axis::traits::args; - return impl(mp11::mp_bool< - (mp11::mp_size::value == 1 && - std::is_convertible>::value) - >(), t); - } - template - int impl(std::true_type, const T& a) const { - return a(x); - } - template - int impl(std::false_type, const T&) const { - using args = axis::traits::args; - throw std::invalid_argument(detail::cat( - "cannot convert double to ", - boost::core::demangled_name( BOOST_CORE_TYPEID(args) ), - " for ", - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ) - )); - } -}; - template struct functor_wrapper : public boost::static_visitor { F& fcn; @@ -64,25 +36,22 @@ struct functor_wrapper : public boost::static_visitor { return fcn(std::forward(t)); } }; - } // namespace detail namespace axis { /// Polymorphic axis type template -class variant - : private boost::variant - , public iterator_mixin> -{ +class variant : private boost::variant, public iterator_mixin> { using base_type = boost::variant; using bin_type = interval_view; using first_bounded_type = mp11::mp_first; - using metadata_type = detail::rm_cvref()))>; + using metadata_type = + detail::rm_cvref()))>; template using requires_bounded_type = - mp11::mp_if>, void>; + mp11::mp_if>, void>; public: variant() = default; @@ -107,18 +76,20 @@ public: template variant& operator=(const variant& u) { - visit([this](const auto& u) { - using U = detail::rm_cvref; - detail::static_if>( - [this](const auto& u) { this->operator=(u); }, - [](const auto&) { - throw std::runtime_error(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), - " is not a bounded type of ", - boost::core::demangled_name( BOOST_CORE_TYPEID(variant) ) - )); - }, u); - }, u); + visit( + [this](const auto& u) { + using U = detail::rm_cvref; + detail::static_if>( + [this](const auto& u) { this->operator=(u); }, + [](const auto&) { + throw std::runtime_error( + detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(U)), + " is not a bounded type of ", + boost::core::demangled_name(BOOST_CORE_TYPEID(variant)))); + }, + u); + }, + u); return *this; } @@ -131,56 +102,102 @@ public: } const metadata_type& metadata() const { - return visit([](const auto& x) -> const metadata_type& { - using U = decltype(traits::metadata(x)); - return detail::static_if>( - [](const auto& x) -> const metadata_type& { return traits::metadata(x); }, - [](const auto&) -> const metadata_type& { - throw std::runtime_error(detail::cat( - "cannot return metadata of type ", - boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), - " through axis::variant interface which uses type ", - boost::core::demangled_name( BOOST_CORE_TYPEID(const metadata_type&) ) - )); - }, x); - }, *this); + return visit( + [](const auto& x) -> const metadata_type& { + using U = decltype(traits::metadata(x)); + return detail::static_if>( + [](const auto& x) -> const metadata_type& { return traits::metadata(x); }, + [](const auto&) -> const metadata_type& { + throw std::runtime_error(detail::cat( + "cannot return metadata of type ", + boost::core::demangled_name(BOOST_CORE_TYPEID(U)), + " through axis::variant interface which uses type ", + boost::core::demangled_name(BOOST_CORE_TYPEID(const metadata_type&)), + "; use boost::histogram::axis::get to obtain a reference " + "of this axis type")); + }, + x); + }, + *this); } metadata_type& metadata() { - return visit([](auto& x) -> metadata_type& { - using U = decltype(traits::metadata(x)); - return detail::static_if>( - [](auto& x) -> metadata_type& { return traits::metadata(x); }, - [](auto&) -> metadata_type& { - throw std::runtime_error(detail::cat( - "cannot return metadata of type ", - boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), - " through axis::variant interface which uses type ", - boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type&) ) - )); - }, x); - }, *this); + return visit( + [](auto& x) -> metadata_type& { + using U = decltype(traits::metadata(x)); + return detail::static_if>( + [](auto& x) -> metadata_type& { return traits::metadata(x); }, + [](auto&) -> metadata_type& { + throw std::runtime_error(detail::cat( + "cannot return metadata of type ", + boost::core::demangled_name(BOOST_CORE_TYPEID(U)), + " through axis::variant interface which uses type ", + boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type&)), + "; use boost::histogram::axis::get to obtain a reference " + "of this axis type")); + }, + x); + }, + *this); } // Only works for axes with compatible call signature // and will throw a invalid_argument exception otherwise int operator()(double x) const { - return visit(detail::call_visitor(x), *this); + return visit( + [x](const auto& a) { + using T = detail::rm_cvref; + using args = axis::traits::args; + return detail::static_if_c<( + mp11::mp_size::value == 1 && + std::is_convertible>::value)>( + [x](const auto& a) -> int { return a(x); }, + [](const auto& a) -> int { + throw std::invalid_argument(detail::cat( + "cannot convert double to ", + boost::core::demangled_name(BOOST_CORE_TYPEID(args)), " for ", + boost::core::demangled_name(BOOST_CORE_TYPEID(decltype(a))), + "; use boost::histogram::axis::get to obtain a reference " + "of this axis type")); + }, + a); + }, + *this); } - // Only works for axes with a lower method - // and will throw a runtime_error otherwise - double lower(int idx) const { - return visit([idx](const auto& x) { - using T = detail::rm_cvref; - return detail::static_if>( - [idx](const auto& x) -> double { return x.lower(idx); }, - [](const auto&) -> double { - throw std::runtime_error(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " has no lower method")); - }, x); - }, *this); + // Only works for axes with value method that returns something convertible to + // double and will throw a runtime_error otherwise + double value(double idx) const { + return visit( + [idx](const auto& a) { + using T = detail::rm_cvref; + return detail::static_if>( + [idx](const auto& a) -> double { + using T = detail::rm_cvref; + using U = detail::return_type; + return detail::static_if>( + [idx](const auto& a) -> double { return a.value(idx); }, + [](const auto&) -> double { + throw std::runtime_error(detail::cat( + "return value ", + boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " of ", + boost::core::demangled_name(BOOST_CORE_TYPEID(T)), + "::value(double) is not convertible to double; use " + "boost::histogram::axis::get to obtain a reference " + "of this axis type")); + }, + a); + }, + [](const auto&) -> double { + throw std::runtime_error( + detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)), + " has no value method; use " + "boost::histogram::axis::get to obtain a reference " + "of this axis type")); + }, + a); + }, + *this); } // this only works for axes with compatible bin type @@ -213,7 +230,7 @@ public: template friend auto visit(Functor&& f, Variant&& v) - -> detail::visitor_return_type; + -> detail::visitor_return_type; template friend T& get(variant& v); @@ -229,33 +246,34 @@ public: template friend const T* get(const variant* v); -}; +}; // namespace histogram template -auto visit(Functor&& f, Variant&& v) - -> detail::visitor_return_type -{ +auto visit(Functor&& f, Variant&& v) -> detail::visitor_return_type { using R = detail::visitor_return_type; - return boost::apply_visitor(detail::functor_wrapper(f), - static_cast< - detail::copy_qualifiers::base_type> - >(v)); + return boost::apply_visitor( + detail::functor_wrapper(f), + static_cast::base_type>>( + v)); } template -std::basic_ostream& operator<<(std::basic_ostream& os, const variant& v) -{ - visit([&os](const auto& x) { - using T = detail::rm_cvref; - detail::static_if>( - [&os](const auto& x) { os << x; }, - [](const auto&) { - throw std::runtime_error(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " is not streamable")); - }, x); - }, v); +std::basic_ostream& operator<<(std::basic_ostream& os, + const variant& v) { + visit( + [&os](const auto& x) { + using T = detail::rm_cvref; + detail::static_if>( + [&os](const auto& x) { os << x; }, + [](const auto&) { + throw std::runtime_error( + detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)), + " is not streamable")); + }, + x); + }, + v); return os; } @@ -285,8 +303,7 @@ const T* get(const variant* v) { } // pass-through if T is an axis instead of a variant -template >, +template >, typename = detail::requires_same>> U get(U&& u) { return std::forward(u); diff --git a/include/boost/histogram/detail/compressed_pair.hpp b/include/boost/histogram/detail/compressed_pair.hpp new file mode 100644 index 00000000..70d32c4d --- /dev/null +++ b/include/boost/histogram/detail/compressed_pair.hpp @@ -0,0 +1,74 @@ +// 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) + +#ifndef BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP +#define BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP + +#include +#include + +namespace boost { +namespace histogram { +namespace detail { + +template +class compressed_pair_impl; + +template +class compressed_pair_impl : protected T2 { +public: + template + compressed_pair_impl(U1&& u1, U2&& u2) + : T2(std::forward(u2)), first_(std::forward(u1)) {} + template + compressed_pair_impl(U1&& u1) : first_(std::forward(u1)) {} + compressed_pair_impl() = default; + + T1& first() { return first_; } + T2& second() { return static_cast(*this); } + const T1& first() const { return first_; } + const T2& second() const { return static_cast(*this); } + +private: + T1 first_; +}; + +template +class compressed_pair_impl { +public: + template + compressed_pair_impl(U1&& u1, U2&& u2) + : first_(std::forward(u1)), second_(std::forward(u2)) {} + template + compressed_pair_impl(U1&& u1) : first_(std::forward(u1)) {} + compressed_pair_impl() = default; + + T1& first() { return first_; } + T2& second() { return second_; } + const T1& first() const { return first_; } + const T2& second() const { return second_; } + +private: + T1 first_; + T2 second_; +}; + +template +void swap(compressed_pair_impl& a, compressed_pair_impl& b) { + using std::swap; + swap(a.first(), b.first()); + swap(a.second(), b.second()); +} + +template +using compressed_pair = + compressed_pair_impl::value && std::is_empty::value)>; + +} // namespace detail +} // namespace histogram +} // namespace boost + +#endif \ No newline at end of file diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index fa6b49d6..2962b4a6 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -7,10 +7,10 @@ #ifndef BOOST_HISTOGRAM_DETAIL_META_HPP #define BOOST_HISTOGRAM_DETAIL_META_HPP -#include -#include #include #include +#include +#include #include #include #include @@ -49,37 +49,32 @@ template using container_element_type = mp11::mp_first>; template -using unqualified_iterator_value_type = rm_cvref::value_type>; +using unqualified_iterator_value_type = + rm_cvref::value_type>; template using return_type = typename boost::callable_traits::return_type::type; template -using args_type = mp11::mp_if< - std::is_member_function_pointer, - mp11::mp_pop_front>, - boost::callable_traits::args_t ->; +using args_type = mp11::mp_if, + mp11::mp_pop_front>, + boost::callable_traits::args_t>; -template -using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size>::value + N : N)>; +template +using arg_type = typename mp11::mp_at_c, N>; template -using visitor_return_type = decltype(std::declval()(std::declval>>())); +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)...); +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) -{ +constexpr decltype(auto) static_if(Ts&&... ts) { return static_if_c(std::forward(ts)...); } @@ -100,33 +95,29 @@ constexpr decltype(auto) static_if(Ts&&... ts) BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support, (std::declval().value(), std::declval().variance())); -BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, - (std::declval().lower(0))); +BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, (std::declval().value(0))); -BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, - (static_cast(std::declval().options()))); +BOOST_HISTOGRAM_MAKE_SFINAE( + has_method_options, (static_cast(std::declval().options()))); -BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, - (std::declval().metadata())); +BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval().metadata())); + +BOOST_HISTOGRAM_MAKE_SFINAE(is_transform, (&T::forward, &T::inverse)); BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container, (std::declval()[0], std::declval().size())); -BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, - (std::get<0>(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, (std::get<0>(std::declval()))); -BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, - (static_cast(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, (static_cast(std::declval()))); BOOST_HISTOGRAM_MAKE_SFINAE(is_equal_comparable, (std::declval() == std::declval())); -BOOST_HISTOGRAM_MAKE_SFINAE(is_axis, - (std::declval().size(), &T::operator())); +BOOST_HISTOGRAM_MAKE_SFINAE(is_axis, (std::declval().size(), &T::operator())); -BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable, - (std::begin(std::declval()), - std::end(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable, (std::begin(std::declval()), + std::end(std::declval()))); BOOST_HISTOGRAM_MAKE_SFINAE(is_streamable, (std::declval() << std::declval())); @@ -137,16 +128,14 @@ struct is_axis_variant_impl : std::false_type {}; template struct is_axis_variant_impl> : std::true_type {}; -} +} // namespace template using is_axis_variant = typename is_axis_variant_impl::type; template -using is_axis_vector = mp11::mp_all< - is_random_access_container, - is_axis>> - >; +using is_axis_vector = mp11::mp_all, + is_axis>>>; struct static_container_tag {}; struct iterable_container_tag {}; @@ -155,8 +144,8 @@ struct no_container_tag {}; template using classify_container = typename std::conditional< is_iterable::value, iterable_container_tag, - typename std::conditional::value, - static_container_tag, no_container_tag>::type>::type; + typename std::conditional::value, static_container_tag, + no_container_tag>::type>::type; namespace { struct bool_mask_impl { @@ -167,7 +156,7 @@ struct bool_mask_impl { b[Int::value] = v; } }; -} +} // namespace template std::vector bool_mask(unsigned n, bool v) { @@ -191,26 +180,26 @@ struct requires_iterable {}; template , void>> struct requires_static_container {}; -template , void>> +template , void>> struct requires_axis {}; template ::value || - is_axis_variant::value), void>> + typename = + mp11::mp_if_c<(is_axis::value || is_axis_variant::value), void>> struct requires_axis_or_axis_variant {}; -template , +template , typename = mp11::mp_if_c<(is_random_access_container::value && - (is_axis::value || - is_axis_variant::value)), void>> + (is_axis::value || is_axis_variant::value)), + void>> struct requires_axis_vector {}; -template , void>> +template , void>> struct requires_same {}; +template , void>> +struct requires_transform {}; + } // namespace detail } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 3de54686..f9988790 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -10,11 +10,6 @@ #include // for std::allocator #include -namespace std { -template -class allocator; -} - namespace boost { namespace histogram { namespace axis { @@ -35,21 +30,20 @@ template struct sqrt; template struct pow; -template -struct quantity; } // namespace transform -template , typename MetaData = std::string> +template , + typename MetaData = std::string> class regular; -template +template class circular; -template , +template , typename MetaData = std::string> class variable; -template +template class integer; template , diff --git a/include/boost/histogram/serialization.hpp b/include/boost/histogram/serialization.hpp index dd2af862..9a37c34a 100644 --- a/include/boost/histogram/serialization.hpp +++ b/include/boost/histogram/serialization.hpp @@ -8,7 +8,11 @@ #define BOOST_HISTOGRAM_SERIALIZATION_HPP #include -#include +#include +#include +#include +#include +#include #include #include #include @@ -33,7 +37,7 @@ template void serialize(Archive& ar, tuple& t, unsigned /* version */) { ::boost::mp11::tuple_for_each(t, [&ar](auto& x) { ar& x; }); } -} // namespace std +} // namespace std namespace boost { namespace histogram { @@ -62,19 +66,15 @@ struct serializer { template void operator()(void*, Buffer& b, Archive&) { - if (Archive::is_loading::value) { - b.ptr = nullptr; - } + if (Archive::is_loading::value) { b.ptr = nullptr; } } }; -} // namespace detail +} // namespace detail template void serialize(Archive& ar, adaptive_storage& s, unsigned /* version */) { using S = adaptive_storage; - if (Archive::is_loading::value) { - S::apply(typename S::destroyer(), s.buffer); - } + if (Archive::is_loading::value) { S::apply(typename S::destroyer(), s.buffer); } ar& s.buffer.type; ar& s.buffer.size; S::apply(detail::serializer(), s.buffer, ar); @@ -89,15 +89,14 @@ void histogram::serialize(Archive& ar, unsigned /* version */) { namespace axis { template -void serialize(Archive&, empty_metadata_type&, unsigned /* version */) { -} // noop +void serialize(Archive&, empty_metadata_type&, unsigned /* version */) {} // noop template template void base::serialize(Archive& ar, unsigned /* version */) { - ar& metadata(); - ar& data_.size; - ar& data_.opt; + ar& size_meta_.first(); + ar& size_meta_.second(); + ar& opt_; } template @@ -106,25 +105,19 @@ void transform::pow::serialize(Archive& ar, unsigned /* version */) { ar& power; } -template -template -void transform::quantity::serialize(Archive& ar, unsigned /* version */) { - ar& unit; -} - template template void regular::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object(*this); - ar& boost::serialization::base_object(data_); - ar& data_.min; - ar& data_.delta; + ar& static_cast(*this); + ar& static_cast(*this); + ar& min_; + ar& delta_; } template template void circular::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object(*this); + ar& static_cast(*this); ar& phase_; ar& delta_; } @@ -133,17 +126,17 @@ template template void variable::serialize(Archive& ar, unsigned /* version */) { // destroy must happen before base serialization with old size - if (Archive::is_loading::value) detail::destroy_buffer(data_, data_.x, nx()); - ar& boost::serialization::base_object(*this); + if (Archive::is_loading::value) detail::destroy_buffer(x_.second(), x_.first(), nx()); + ar& static_cast(*this); if (Archive::is_loading::value) - data_.x = boost::histogram::detail::create_buffer(data_, nx()); - ar& boost::serialization::make_array(data_.x, nx()); + x_.first() = boost::histogram::detail::create_buffer(x_.second(), nx()); + ar& boost::serialization::make_array(x_.first(), nx()); } template template void integer::serialize(Archive& ar, unsigned /* version */) { - ar& boost::serialization::base_object(*this); + ar& static_cast(*this); ar& min_; } @@ -152,11 +145,11 @@ template void category::serialize(Archive& ar, unsigned /* version */) { // destroy must happen before base serialization with old size if (Archive::is_loading::value) - detail::destroy_buffer(data_, data_.x, base_type::size()); - ar& boost::serialization::base_object(*this); + detail::destroy_buffer(x_.second(), x_.first(), base_type::size()); + ar& static_cast(*this); if (Archive::is_loading::value) - data_.x = boost::histogram::detail::create_buffer(data_, base_type::size()); - ar& boost::serialization::make_array(data_.x, base_type::size()); + x_.first() = boost::histogram::detail::create_buffer(x_.second(), base_type::size()); + ar& boost::serialization::make_array(x_.first(), base_type::size()); } template @@ -164,9 +157,9 @@ template void variant::serialize(Archive& ar, unsigned /* version */) { ar& static_cast(*this); } -} // namespace axis +} // namespace axis -} // namespace histogram -} // namespace boost +} // namespace histogram +} // namespace boost #endif diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 3df4c12c..dfd4899d 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -7,12 +7,18 @@ #include #include #include -#include +#include +#include +#include #include -#include +#include +#include +#include #include -#include #include +#include +#include +#include #include #include #include @@ -38,38 +44,10 @@ void test_axis_iterator(const Axis& a, int begin, int end) { } } -// quantity with unit for testing -template -struct quantity { - double value; -}; - -struct length { - double value; -}; - -auto meter = length{1.0}; - -template -double operator/(const quantity& a, const Unit& u) { - return a.value / u.value; -} - -template -quantity operator*(double x, const Unit& u) { - return quantity{x * u.value}; -} - -template -quantity operator-(const quantity& a, const quantity& b) { - return quantity{a.value - b.value}; -} - int main() { // bad_ctors { BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument); - // BOOST_TEST_THROWS(axis::regular<>(1, 1, -1), std::invalid_argument); BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); BOOST_TEST_THROWS(axis::variable<>(std::vector()), std::invalid_argument); BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); @@ -83,16 +61,15 @@ int main() { BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); axis::regular<> b; - BOOST_TEST_NOT(a == b); + BOOST_TEST_NE(a, b); b = a; BOOST_TEST_EQ(a, b); b = b; BOOST_TEST_EQ(a, b); axis::regular<> c = std::move(b); - BOOST_TEST(c == a); - BOOST_TEST_NOT(b == a); + BOOST_TEST_EQ(c, a); axis::regular<> d; - BOOST_TEST_NOT(c == d); + BOOST_TEST_NE(c, d); d = std::move(c); BOOST_TEST_EQ(d, a); BOOST_TEST_EQ(a(-10.), -1); @@ -110,15 +87,21 @@ int main() { // regular axis with inverted range { - axis::regular<> a(2, 1, -1); + axis::regular<> a{2, 1, -2}; + BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits::infinity()); BOOST_TEST_EQ(a[0].lower(), 1); - BOOST_TEST_EQ(a[1].lower(), 0); - BOOST_TEST_EQ(a[2].lower(), -1); + BOOST_TEST_EQ(a[1].lower(), -0.5); + BOOST_TEST_EQ(a[2].lower(), -2); + BOOST_TEST_EQ(a[2].upper(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(a(2), -1); + BOOST_TEST_EQ(a(1.001), -1); BOOST_TEST_EQ(a(1), 0); - BOOST_TEST_EQ(a(0), 1); - BOOST_TEST_EQ(a(-1), 2); + BOOST_TEST_EQ(a(0), 0); + BOOST_TEST_EQ(a(-0.499), 0); + BOOST_TEST_EQ(a(-0.5), 1); + BOOST_TEST_EQ(a(-1), 1); BOOST_TEST_EQ(a(-2), 2); + BOOST_TEST_EQ(a(-20), 2); } // axis::regular with log transform @@ -160,25 +143,27 @@ int main() { BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); } - // axis::regular with quantity transform + // axis::regular with quantity { - axis::regular, length>> b{ - 2, 0*meter, 2*meter, {}, axis::option_type::underflow_and_overflow, meter - }; - BOOST_TEST_EQ(b[-1].lower()/meter, -std::numeric_limits::infinity()); - BOOST_TEST_IS_CLOSE(b[0].lower()/meter, 0.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[1].lower()/meter, 1.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[2].lower()/meter, 2.0, 1e-9); - BOOST_TEST_EQ(b[2].upper()/meter, std::numeric_limits::infinity()); + using namespace boost::units; + using namespace boost::units::si; + using Q = quantity; - BOOST_TEST_EQ(b(-1*meter), -1); // produces NaN in conversion - BOOST_TEST_EQ(b(0*meter), 0); - BOOST_TEST_EQ(b(0.99*meter), 0); - BOOST_TEST_EQ(b(1*meter), 1); - BOOST_TEST_EQ(b(1.99*meter), 1); - BOOST_TEST_EQ(b(2*meter), 2); - BOOST_TEST_EQ(b(100*meter), 2); - BOOST_TEST_EQ(b(std::numeric_limits::infinity()*meter), 2); + // axis::regular> b(2, 0 * meter, 2 * meter); + // BOOST_TEST_EQ(b[-1].lower() / meter, -std::numeric_limits::infinity()); + // BOOST_TEST_IS_CLOSE(b[0].lower() / meter, 0.0, 1e-9); + // BOOST_TEST_IS_CLOSE(b[1].lower() / meter, 1.0, 1e-9); + // BOOST_TEST_IS_CLOSE(b[2].lower() / meter, 2.0, 1e-9); + // BOOST_TEST_EQ(b[2].upper() / meter, std::numeric_limits::infinity()); + + // BOOST_TEST_EQ(b(-1 * meter), -1); // produces NaN in conversion + // BOOST_TEST_EQ(b(0 * meter), 0); + // BOOST_TEST_EQ(b(0.99 * meter), 0); + // BOOST_TEST_EQ(b(1 * meter), 1); + // BOOST_TEST_EQ(b(1.99 * meter), 1); + // BOOST_TEST_EQ(b(2 * meter), 2); + // BOOST_TEST_EQ(b(100 * meter), 2); + // BOOST_TEST_EQ(b(std::numeric_limits::infinity() * meter), 2); } // axis::circular @@ -186,16 +171,15 @@ int main() { axis::circular<> a{4, 0, 1}; BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1); axis::circular<> b; - BOOST_TEST_NOT(a == b); + BOOST_TEST_NE(a, b); b = a; BOOST_TEST_EQ(a, b); b = b; BOOST_TEST_EQ(a, b); axis::circular<> c = std::move(b); - BOOST_TEST(c == a); - BOOST_TEST_NOT(b == a); + BOOST_TEST_EQ(c, a); axis::circular<> d; - BOOST_TEST_NOT(c == d); + BOOST_TEST_NE(c, d); d = std::move(c); BOOST_TEST_EQ(d, a); BOOST_TEST_EQ(a(-1.0 * 3), 0); @@ -215,25 +199,25 @@ int main() { BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); axis::variable<> b; - BOOST_TEST_NOT(a == b); + BOOST_TEST_NE(a, b); b = a; BOOST_TEST_EQ(a, b); b = b; BOOST_TEST_EQ(a, b); axis::variable<> c = std::move(b); - BOOST_TEST(c == a); - BOOST_TEST_NOT(b == a); + BOOST_TEST_EQ(c, a); + BOOST_TEST_NE(b, a); axis::variable<> d; - BOOST_TEST_NOT(c == d); + BOOST_TEST_NE(c, d); d = std::move(c); BOOST_TEST_EQ(d, a); axis::variable<> e{-2, 0, 2}; - BOOST_TEST_NOT(a == e); - BOOST_TEST_EQ(a(-10.), -1); - BOOST_TEST_EQ(a(-1.), 0); - BOOST_TEST_EQ(a(0.), 1); - BOOST_TEST_EQ(a(1.), 2); - BOOST_TEST_EQ(a(10.), 2); + BOOST_TEST_NE(a, e); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(10), 2); BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 2); BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 2); @@ -245,16 +229,15 @@ int main() { BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits::min()); BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::max()); axis::integer<> b; - BOOST_TEST_NOT(a == b); + BOOST_TEST_NE(a, b); b = a; BOOST_TEST_EQ(a, b); b = b; BOOST_TEST_EQ(a, b); axis::integer<> c = std::move(b); - BOOST_TEST(c == a); - BOOST_TEST_NOT(b == a); + BOOST_TEST_EQ(c, a); axis::integer<> d; - BOOST_TEST_NOT(c == d); + BOOST_TEST_NE(c, d); d = std::move(c); BOOST_TEST_EQ(d, a); BOOST_TEST_EQ(a(-10), -1); @@ -271,19 +254,19 @@ int main() { std::string A("A"), B("B"), C("C"), other; axis::category a({A, B, C}); axis::category b; - BOOST_TEST_NOT(a == b); + BOOST_TEST_NE(a, b); b = a; BOOST_TEST_EQ(a, b); b = axis::category{{B, A, C}}; - BOOST_TEST_NOT(a == b); + BOOST_TEST_NE(a, b); b = a; b = b; BOOST_TEST_EQ(a, b); axis::category c = std::move(b); - BOOST_TEST(c == a); - BOOST_TEST_NOT(b == a); + BOOST_TEST_EQ(c, a); + BOOST_TEST_NE(b, a); axis::category d; - BOOST_TEST_NOT(c == d); + BOOST_TEST_NE(c, d); d = std::move(c); BOOST_TEST_EQ(d, a); BOOST_TEST_EQ(a.size(), 3); @@ -301,13 +284,15 @@ int main() { { enum { A, B, C }; test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5); - test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); + test_axis_iterator( + axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3); test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); - // BOOST_TEST_THROWS(axis::variant>(axis::category<>({A, B, C}))[0].lower(), + // BOOST_TEST_THROWS(axis::variant>(axis::category<>({A, B, + // C}))[0].lower(), // std::runtime_error); } @@ -355,14 +340,15 @@ int main() { struct user_defined {}; - test(axis::regular<>{2, -1, 1, "regular1"}, + namespace tr = axis::transform; + test(axis::regular<>(2, -1, 1, "regular1"), "regular(2, -1, 1, metadata=\"regular1\", options=underflow_and_overflow)"); - test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), + test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), "regular_log(2, 1, 10, metadata=\"regular2\", options=none)"); - test(axis::regular>(2, 1, 10, "regular3", axis::option_type::overflow, 0.5), - "regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=0.5)"); - test(axis::regular>(2, 1, 10, "regular4", axis::option_type::none, -0.5), - "regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-0.5)"); + test(axis::regular>(1.5, 2, 1, 10, "regular3", axis::option_type::overflow), + "regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=1.5)"); + test(axis::regular>(-1.5, 2, 1, 10, "regular4", axis::option_type::none), + "regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-1.5)"); test(axis::circular(4, 0.1, 1.0), "circular(4, 0.1, 1.1, options=overflow)"); test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none), @@ -387,8 +373,9 @@ int main() { BOOST_TEST_EQ(axis(10), 0); BOOST_TEST_EQ(axis.size(), 1); BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); - BOOST_TEST_THROWS(axis.lower(0), std::runtime_error); - BOOST_TEST_TRAIT_TRUE((std::is_same)); + BOOST_TEST_THROWS(axis.value(0), std::runtime_error); + BOOST_TEST_TRAIT_TRUE( + (std::is_same)); } // bin_type streamable @@ -409,18 +396,13 @@ int main() { // axis::variant equal_comparable { enum { A, B, C }; - using variant = axis::variant< - axis::regular<>, - axis::regular>, - axis::circular<>, - axis::variable<>, - axis::category<>, - axis::integer<> - >; + using variant = axis::variant, axis::regular>, + axis::circular<>, axis::variable<>, axis::category<>, + axis::integer<>>; std::vector axes; axes.push_back(axis::regular<>{2, -1, 1}); - axes.push_back( - axis::regular>(2, 1, 4, "", axis::option_type::underflow_and_overflow, 0.5)); + axes.push_back(axis::regular>( + 0.5, 2, 1, 4, "", axis::option_type::underflow_and_overflow)); axes.push_back(axis::circular<>{4}); axes.push_back(axis::variable<>{-1, 0, 1}); axes.push_back(axis::category<>({A, B, C})); @@ -435,16 +417,17 @@ int main() { // axis::variant value_to_index_failure { - axis::variant> x = axis::category({"A", "B"}, "category"); + axis::variant> x = + axis::category({"A", "B"}, "category"); auto cx = axis::get>(x); - // BOOST_TEST_EQ(cx(b), 1); + BOOST_TEST_EQ(cx("B"), 1); } // sequence equality { enum { A, B, C }; - std::vector< - axis::variant, axis::variable<>, axis::category<>, axis::integer<>>> + std::vector, axis::variable<>, axis::category<>, + axis::integer<>>> std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, axis::category<>{A, B, C}}; @@ -480,8 +463,8 @@ int main() { // sequence assign { enum { A, B, C, D }; - std::vector< - axis::variant, axis::variable<>, axis::category<>, axis::integer<>>> + std::vector, axis::variable<>, axis::category<>, + axis::integer<>>> std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, axis::category<>{A, B, C}}; @@ -561,7 +544,8 @@ int main() { axes.reserve(5); axes.emplace_back(T1(1, 0, 1, M(3, 'c', a))); axes.emplace_back(T2(2)); - axes.emplace_back(T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a)); + axes.emplace_back( + T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a)); axes.emplace_back(T4(0, 4)); axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); } diff --git a/test/histogram_dynamic_test.cpp b/test/histogram_dynamic_test.cpp index b15e00a4..e75fd9b6 100644 --- a/test/histogram_dynamic_test.cpp +++ b/test/histogram_dynamic_test.cpp @@ -52,9 +52,8 @@ int main() { // axis methods { - enum { A, B }; - auto c = make(dynamic_tag(), axis::category<>({A, B})); - BOOST_TEST_THROWS(c.axis().lower(0), std::runtime_error); + auto c = make(dynamic_tag(), axis::category({"A", "B"})); + BOOST_TEST_THROWS(c.axis().value(0), std::runtime_error); } // reduce @@ -88,7 +87,8 @@ int main() { // histogram iterator { - auto h = make_s(dynamic_tag(), array_storage>(), axis::integer<>(0, 3)); + auto h = + make_s(dynamic_tag(), array_storage>(), axis::integer<>(0, 3)); const auto& a = h.axis(); h(weight(2), 0); h(1); diff --git a/test/histogram_serialization_test.cpp b/test/histogram_serialization_test.cpp index ec85519f..b25bef24 100644 --- a/test/histogram_serialization_test.cpp +++ b/test/histogram_serialization_test.cpp @@ -4,15 +4,15 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) +#include +#include #include #include #include -#include -#include -#include #include -#include +#include #include +#include #include "utility.hpp" using namespace boost::histogram; @@ -21,13 +21,14 @@ template void run_tests() { // histogram_serialization { - auto a = make( - Tag(), axis::regular<>(3, -1, 1, "axis 0"), axis::circular<>(4, 0.0, 1.0, "axis 1"), - axis::regular>(3, 1, 100, "axis 2"), - axis::regular>(3, 1, 100, "axis 3", axis::option_type::overflow, 0.5), - axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"), - axis::category<>{3, 1, 2}, - axis::integer(0, 2)); + auto a = make(Tag(), axis::regular<>(3, -1, 1, "axis 0"), + axis::circular<>(4, 0.0, 1.0, "axis 1"), + axis::regular>(3, 1, 100, "axis 2"), + axis::regular>(0.5, 3, 1, 100, "axis 3", + axis::option_type::overflow), + axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"), + axis::category<>{3, 1, 2}, + axis::integer(0, 2)); a(0.5, 0.2, 20, 20, 0.25, 1, 1); std::string buf; { diff --git a/test/meta_test.cpp b/test/meta_test.cpp index a478e113..b5b2b2d4 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -6,16 +6,16 @@ #include #include +#include +#include #include #include -#include -#include #include +#include #include #include #include #include -#include #include "utility.hpp" namespace bh = boost::histogram; @@ -64,21 +64,27 @@ int main() { BOOST_TEST_TRAIT_TRUE((has_variance_support)); } - // has_method_lower + // has_method_value { struct A {}; - struct B { void lower(int) {} }; + struct B { + void value(int) {} + }; - BOOST_TEST_TRAIT_FALSE((has_method_lower)); - BOOST_TEST_TRAIT_TRUE((has_method_lower)); + BOOST_TEST_TRAIT_FALSE((has_method_value)); + BOOST_TEST_TRAIT_TRUE((has_method_value)); } // has_method_options { struct NotOptions {}; struct A {}; - struct B { NotOptions options(); }; - struct C { bh::axis::option_type options(); }; + struct B { + NotOptions options(); + }; + struct C { + bh::axis::option_type options(); + }; BOOST_TEST_TRAIT_FALSE((has_method_options)); BOOST_TEST_TRAIT_FALSE((has_method_options)); @@ -88,16 +94,32 @@ int main() { // has_method_metadata { struct A {}; - struct B { void metadata(); }; + struct B { + void metadata(); + }; BOOST_TEST_TRAIT_FALSE((has_method_metadata)); BOOST_TEST_TRAIT_TRUE((has_method_metadata)); } + // is_transform + { + struct A {}; + struct B { + double forward(double); + double inverse(double); + }; + + BOOST_TEST_TRAIT_FALSE((is_transform)); + BOOST_TEST_TRAIT_TRUE((is_transform)); + } + // is_equal_comparable { struct A {}; - struct B { bool operator==(const B&); }; + struct B { + bool operator==(const B&); + }; BOOST_TEST_TRAIT_TRUE((is_equal_comparable)); BOOST_TEST_TRAIT_FALSE((is_equal_comparable)); BOOST_TEST_TRAIT_TRUE((is_equal_comparable)); @@ -106,9 +128,16 @@ int main() { // is_axis { struct A {}; - struct B { int operator()(double); unsigned size() const; }; - struct C { int operator()(double); }; - struct D { unsigned size(); }; + struct B { + int operator()(double); + unsigned size() const; + }; + struct C { + int operator()(double); + }; + struct D { + unsigned size(); + }; BOOST_TEST_TRAIT_FALSE((is_axis)); BOOST_TEST_TRAIT_TRUE((is_axis)); BOOST_TEST_TRAIT_FALSE((is_axis)); @@ -266,8 +295,10 @@ int main() { int f2(long) const; }; - BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, std::tuple>)); } // visitor_return_type @@ -275,19 +306,23 @@ int main() { using V1 = bh::axis::variant; using V2 = bh::axis::variant&; using V3 = const bh::axis::variant&; - BOOST_TEST_TRAIT_TRUE((std::is_same, char>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int&>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, const long&>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, char>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, int&>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, const long&>)); } // static_if { - struct callable { int operator()() { return 1; }; }; + struct callable { + int operator()() { return 1; }; + }; struct not_callable {}; auto fcn = [](auto b, auto x) { - return static_if( - [](auto x) { return x(); }, - [](auto) { return 2; }, x); + return static_if([](auto x) { return x(); }, [](auto) { return 2; }, + x); }; BOOST_TEST_EQ(fcn(std::true_type(), callable()), 1); BOOST_TEST_EQ(fcn(std::false_type(), not_callable()), 2); From a5cd46d24b4b8fa1b236abead68e725c6d01cd7f Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 00:32:35 +0200 Subject: [PATCH 22/44] win fix --- test/axis_test.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/axis_test.cpp b/test/axis_test.cpp index dfd4899d..ebfeb755 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -47,12 +47,13 @@ void test_axis_iterator(const Axis& a, int begin, int end) { int main() { // bad_ctors { + auto empty = std::vector(0); BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument); BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); - BOOST_TEST_THROWS(axis::variable<>(std::vector()), std::invalid_argument); + BOOST_TEST_THROWS((axis::variable<>(empty)), std::invalid_argument); BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); - BOOST_TEST_THROWS(axis::category<>(std::vector()), std::invalid_argument); + BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument); } // axis::regular From ea99ae9a2a656274d7954d75c4ba7b5580f7e5c2 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 01:02:13 +0200 Subject: [PATCH 23/44] silence warning --- include/boost/histogram/axis/variant.hpp | 4 +- .../histogram/storage/adaptive_storage.hpp | 99 +++++++++---------- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index b3e0b05e..5b35432e 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -176,7 +176,9 @@ public: using T = detail::rm_cvref; using U = detail::return_type; return detail::static_if>( - [idx](const auto& a) -> double { return a.value(idx); }, + [idx](const auto& a) -> double { + return static_cast(a.value(idx)); + }, [](const auto&) -> double { throw std::runtime_error(detail::cat( "return value ", diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index fdcdc45a..53af8bda 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -9,14 +9,13 @@ #include #include -#include #include #include #include #include #include -#include #include +#include #include #include #include @@ -58,7 +57,8 @@ struct make_unsigned_impl { }; template -using make_unsigned = typename make_unsigned_impl::type>::type; +using make_unsigned = + typename make_unsigned_impl::type>::type; template bool safe_radd(T& t, const U& u) { @@ -80,7 +80,7 @@ bool safe_radd(T& t, const boost::multiprecision::number& u) { t += static_cast(u); // static_cast to suppress conversion warning return true; } -} +} // namespace detail template struct adaptive_storage { @@ -93,18 +93,13 @@ struct adaptive_storage { using element_type = double; using const_reference = double; - using mp_int = boost::multiprecision::number< - boost::multiprecision::cpp_int_backend< - 0, 0, boost::multiprecision::signed_magnitude, - boost::multiprecision::unchecked, + using mp_int = boost::multiprecision::number::template rebind_alloc< - boost::multiprecision::limb_type - > - > - >; + boost::multiprecision::limb_type>>>; - using types = mp11::mp_list; + using types = + mp11::mp_list; template static constexpr char type_index() { @@ -122,22 +117,22 @@ struct adaptive_storage { template T* create_impl(T*, const U* init) { - using alloc_type = typename std::allocator_traits< - allocator_type>::template rebind_alloc; + using alloc_type = + typename std::allocator_traits::template rebind_alloc; alloc_type a(alloc); // rebind allocator - return init ? detail::create_buffer_from_iter(a, size, init) : - detail::create_buffer(a, size, 0); + return init ? detail::create_buffer_from_iter(a, size, init) + : detail::create_buffer(a, size, 0); } template mp_int* create_impl(mp_int*, const U* init) { - using alloc_type = typename std::allocator_traits< - allocator_type>::template rebind_alloc; + using alloc_type = + typename std::allocator_traits::template rebind_alloc; alloc_type a(alloc); // rebound allocator for buffer // mp_int has no ctor with an allocator instance, cannot pass state :( // typename mp_int::backend_type::allocator_type a2(alloc); - return init ? detail::create_buffer_from_iter(a, size, init) : - detail::create_buffer(a, size, 0); + return init ? detail::create_buffer_from_iter(a, size, init) + : detail::create_buffer(a, size, 0); } void* create_impl(void*, const void* init) { @@ -159,27 +154,20 @@ struct adaptive_storage { }; template - static decltype(auto) apply(F&& f, B&& b, Ts&&... ts) - { + static decltype(auto) apply(F&& f, B&& b, Ts&&... ts) { // this is intentionally not a switch, the if-chain is faster in benchmarks if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), b, - std::forward(ts)...); + return f(reinterpret_cast(b.ptr), b, std::forward(ts)...); if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), b, - std::forward(ts)...); + return f(reinterpret_cast(b.ptr), b, std::forward(ts)...); if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), b, - std::forward(ts)...); + return f(reinterpret_cast(b.ptr), b, std::forward(ts)...); if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), b, - std::forward(ts)...); + return f(reinterpret_cast(b.ptr), b, std::forward(ts)...); if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), b, - std::forward(ts)...); + return f(reinterpret_cast(b.ptr), b, std::forward(ts)...); if (b.type == type_index()) - return f(reinterpret_cast(b.ptr), b, - std::forward(ts)...); + return f(reinterpret_cast(b.ptr), b, std::forward(ts)...); // b.type == 0 is intentionally the last in the chain, // because it is rarely triggered return f(b.ptr, b, std::forward(ts)...); @@ -187,9 +175,7 @@ struct adaptive_storage { ~adaptive_storage() { apply(destroyer(), buffer); } - adaptive_storage(const adaptive_storage& o) { - apply(replacer(), o.buffer, buffer); - } + adaptive_storage(const adaptive_storage& o) { apply(replacer(), o.buffer, buffer); } adaptive_storage& operator=(const adaptive_storage& o) { if (this != &o) { apply(replacer(), o.buffer, buffer); } @@ -258,9 +244,7 @@ struct adaptive_storage { apply(adder(), buffer, i, x.value); } - const_reference operator[](std::size_t i) const { - return apply(getter(), buffer, i); - } + const_reference operator[](std::size_t i) const { return apply(getter(), buffer, i); } bool operator==(const adaptive_storage& o) const { if (size() != o.size()) return false; @@ -309,8 +293,8 @@ struct adaptive_storage { struct destroyer { template void operator()(T* tp, Buffer& b) { - using alloc_type = typename std::allocator_traits< - allocator_type>::template rebind_alloc; + using alloc_type = + typename std::allocator_traits::template rebind_alloc; alloc_type a(b.alloc); // rebind allocator detail::destroy_buffer(a, tp, b.size); } @@ -372,7 +356,8 @@ struct adaptive_storage { struct adder { template - void if_U_is_integral(std::true_type, mp_int* tp, Buffer&, std::size_t i, const U& x) { + void if_U_is_integral(std::true_type, mp_int* tp, Buffer&, std::size_t i, + const U& x) { tp[i] += static_cast(x); } @@ -397,9 +382,9 @@ struct adaptive_storage { template void operator()(T* tp, Buffer& b, std::size_t i, const U& x) { - if_U_is_integral(mp11::mp_bool< - (std::is_integral::value || - std::is_same::value)>(), tp, b, i, x); + if_U_is_integral( + mp11::mp_bool<(std::is_integral::value || std::is_same::value)>(), + tp, b, i, x); } template @@ -447,11 +432,15 @@ struct adaptive_storage { struct inner { struct cmp { template - bool operator()(const T& t, const U& u) { return t == u; } + bool operator()(const T& t, const U& u) { + return t == u; + } bool operator()(const mp_int& t, const double& u) { - return static_cast(t) == u; } + return static_cast(t) == u; + } bool operator()(const double& t, const mp_int& u) { - return t == static_cast(u); } + return t == static_cast(u); + } }; template @@ -485,7 +474,15 @@ struct adaptive_storage { struct multiplier { template void operator()(T* tp, Buffer& b, const double x) { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) // possible loss of data +#endif + // potential lossy conversion that cannot be avoided auto ptr = b.template create(tp); +#ifdef _MSC_VER +#pragma warning(pop) +#endif destroyer()(tp, b); b.set(ptr); operator()(reinterpret_cast(b.ptr), b, x); From 107feaf646298bbfa541e28b6bd13d03c3deed79 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 01:24:43 +0200 Subject: [PATCH 24/44] test for quantity transform --- include/boost/histogram/axis/regular.hpp | 12 ++++++-- .../histogram/storage/adaptive_storage.hpp | 4 +++ test/axis_test.cpp | 28 +++++++++---------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/include/boost/histogram/axis/regular.hpp b/include/boost/histogram/axis/regular.hpp index 29ac4bd5..1f1f364e 100644 --- a/include/boost/histogram/axis/regular.hpp +++ b/include/boost/histogram/axis/regular.hpp @@ -47,10 +47,10 @@ struct sqrt : identity { template struct pow { using U = mp11::mp_if, double, T>; - U power = 1; + U power = 1.0; - pow() = default; pow(U p) : power(p) {} + pow() = default; U forward(U v) const { return std::pow(v, power); } U inverse(U v) const { return std::pow(v, 1.0 / power); } @@ -59,6 +59,14 @@ struct pow { template void serialize(Archive&, unsigned); }; + +template +struct unit { + using T = typename Q::value_type; + using U = typename Q::unit_type; + T forward(Q x) const { return x / U(); } + Q inverse(T x) const { return x * U(); } +}; } // namespace transform /** Axis for equidistant intervals on the real line. diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index 53af8bda..b0128697 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -15,7 +15,11 @@ #include #include #include +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +// warning-ignore required in Boost-1.66 for cpp_int.hpp:822 #include +#pragma clang diagnostic pop #include #include #include diff --git a/test/axis_test.cpp b/test/axis_test.cpp index ebfeb755..08107f59 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -150,21 +150,21 @@ int main() { using namespace boost::units::si; using Q = quantity; - // axis::regular> b(2, 0 * meter, 2 * meter); - // BOOST_TEST_EQ(b[-1].lower() / meter, -std::numeric_limits::infinity()); - // BOOST_TEST_IS_CLOSE(b[0].lower() / meter, 0.0, 1e-9); - // BOOST_TEST_IS_CLOSE(b[1].lower() / meter, 1.0, 1e-9); - // BOOST_TEST_IS_CLOSE(b[2].lower() / meter, 2.0, 1e-9); - // BOOST_TEST_EQ(b[2].upper() / meter, std::numeric_limits::infinity()); + axis::regular> b(2, 0 * meter, 2 * meter); + BOOST_TEST_EQ(b[-1].lower() / meter, -std::numeric_limits::infinity()); + BOOST_TEST_IS_CLOSE(b[0].lower() / meter, 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[1].lower() / meter, 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[2].lower() / meter, 2.0, 1e-9); + BOOST_TEST_EQ(b[2].upper() / meter, std::numeric_limits::infinity()); - // BOOST_TEST_EQ(b(-1 * meter), -1); // produces NaN in conversion - // BOOST_TEST_EQ(b(0 * meter), 0); - // BOOST_TEST_EQ(b(0.99 * meter), 0); - // BOOST_TEST_EQ(b(1 * meter), 1); - // BOOST_TEST_EQ(b(1.99 * meter), 1); - // BOOST_TEST_EQ(b(2 * meter), 2); - // BOOST_TEST_EQ(b(100 * meter), 2); - // BOOST_TEST_EQ(b(std::numeric_limits::infinity() * meter), 2); + BOOST_TEST_EQ(b(-1 * meter), -1); // produces NaN in conversion + BOOST_TEST_EQ(b(0 * meter), 0); + BOOST_TEST_EQ(b(0.99 * meter), 0); + BOOST_TEST_EQ(b(1 * meter), 1); + BOOST_TEST_EQ(b(1.99 * meter), 1); + BOOST_TEST_EQ(b(2 * meter), 2); + BOOST_TEST_EQ(b(100 * meter), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity() * meter), 2); } // axis::circular From ffadd5d39030f1a7ed70fc7d3a9e97966a1db615 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 01:47:07 +0200 Subject: [PATCH 25/44] suppress pragma warning and ignore failing test on msvc --- include/boost/histogram/storage/adaptive_storage.hpp | 4 ++++ test/axis_test.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index b0128697..880ac3d1 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -15,11 +15,15 @@ #include #include #include +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" +#endif // warning-ignore required in Boost-1.66 for cpp_int.hpp:822 #include +#ifdef __clang__ #pragma clang diagnostic pop +#endif #include #include #include diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 08107f59..6dc9c1d6 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -358,8 +358,10 @@ int main() { "category(0, 1, 2, metadata=\"category\", options=overflow)"); test(axis::category({"A", "B"}, "category2"), "category(\"A\", \"B\", metadata=\"category2\", options=overflow)"); +#ifndef _MSC_VER // fails on MSVC because demagnled name for user_defined looks different test(axis::integer(-1, 1, {}, axis::option_type::none), "integer(-1, 1, metadata=main::user_defined, options=none)"); +#endif } // axis::variant support for minimal_axis From 6280b5cf94f12582680c3e7b480eb1e6e8d666e5 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 21:46:00 +0200 Subject: [PATCH 26/44] try win fix --- .appveyor.yml | 26 +++++++++++++++----------- Jamfile | 6 +++--- examples/Jamfile | 3 ++- test/Jamfile | 1 - 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 493be21e..fdaf0100 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -11,17 +11,21 @@ branches: - master - develop +install: + - cd .. + - git clone -b %APPVEYOR_REPO_BRANCH% https://github.com/boostorg/boost + - cd boost + - git submodule init libs/assert + - git submodule init libs/config + - git submodule init libs/core + - git submodule init libs/serialization + - git submodule init tools/build + - git submodule update + - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram + - cmd /c bootstrap + - b2 --with-serialization + build: off -environment: - # MSVC_DEFAULT_OPTIONS: ON - BOOST_ROOT: C:\Libraries\boost_1_67_0 - test_script: - # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) - - mkdir build - - cd build - - cmake .. - -DBOOST_ROOT="%BOOST_ROOT%" -DBoost_USE_STATIC_LIBS="ON" - - cmake --build . - - ctest -V + - b2 libs/histogram diff --git a/Jamfile b/Jamfile index a04af372..56f60ddd 100644 --- a/Jamfile +++ b/Jamfile @@ -8,9 +8,9 @@ project histogram : requirements - gcc:"-std=c++11 -pedantic -Wall -Wextra" - clang:"-std=c++11 -pedantic -Wall -Wextra" - darwin:"-std=c++11 -pedantic -Wall -Wextra" + gcc:"-std=c++14 -pedantic -Wall -Wextra" + clang:"-std=c++14 -pedantic -Wall -Wextra" + darwin:"-std=c++14 -pedantic -Wall -Wextra" ; build-project test ; diff --git a/examples/Jamfile b/examples/Jamfile index e2e7708c..ca754d54 100644 --- a/examples/Jamfile +++ b/examples/Jamfile @@ -16,7 +16,8 @@ exe getting_started_listing_02 : getting_started_listing_02.cpp ; exe guide_access_bin_counts : guide_access_bin_counts.cpp ; exe guide_axis_with_labels : guide_axis_with_labels.cpp ; exe guide_axis_with_uoflow_off : guide_axis_with_uoflow_off.cpp ; -exe guide_custom_axis : guide_custom_axis.cpp ; +exe guide_custom_minimal_axis : guide_custom_minimal_axis.cpp ; +exe guide_custom_modified_axis : guide_custom_modified_axis.cpp ; exe guide_custom_storage : guide_custom_storage.cpp ; exe guide_fill_histogram : guide_fill_histogram.cpp ; exe guide_histogram_operators : guide_histogram_operators.cpp ; diff --git a/test/Jamfile b/test/Jamfile index 2183d442..94bf85cf 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -25,7 +25,6 @@ alias run-tests : [ run histogram_test.cpp ] [ run index_mapper_test.cpp ] [ run meta_test.cpp ] - [ link speed_cpp.cpp : : speed_test ] [ run utility_test.cpp ] [ run weight_counter_test.cpp ] ; From 1cfff8bcd9071e7513b1faf4d28deea880bde110 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 22:09:56 +0200 Subject: [PATCH 27/44] try win fix --- .appveyor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index fdaf0100..b1d0c503 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,14 +15,16 @@ install: - cd .. - git clone -b %APPVEYOR_REPO_BRANCH% https://github.com/boostorg/boost - cd boost + - dir + - dir %APPVEYOR_BUILD_FOLDER% + - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram - git submodule init libs/assert - git submodule init libs/config - git submodule init libs/core - git submodule init libs/serialization - git submodule init tools/build - git submodule update - - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram - - cmd /c bootstrap + - bootstrap - b2 --with-serialization build: off From 2f92128267a50cd12073c0bac60cef18c6060927 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 22:14:15 +0200 Subject: [PATCH 28/44] try win fix --- .appveyor.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b1d0c503..badbdae3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,14 +15,13 @@ install: - cd .. - git clone -b %APPVEYOR_REPO_BRANCH% https://github.com/boostorg/boost - cd boost - - dir - - dir %APPVEYOR_BUILD_FOLDER% + - mkdir libs\histogram - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram + - git submodule init tools/build - git submodule init libs/assert - git submodule init libs/config - git submodule init libs/core - git submodule init libs/serialization - - git submodule init tools/build - git submodule update - bootstrap - b2 --with-serialization From 611a194f0371c81ef3e42b27fd3ca738c61abfec Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 22:53:35 +0200 Subject: [PATCH 29/44] factoring out test code --- Jamfile | 6 +- test/axis_regular_test.cpp | 158 +++++++++++++++++++++++++++++++++++++ test/axis_test.cpp | 135 ------------------------------- test/utility.hpp | 18 +++++ 4 files changed, 179 insertions(+), 138 deletions(-) create mode 100644 test/axis_regular_test.cpp diff --git a/Jamfile b/Jamfile index 56f60ddd..46af9f49 100644 --- a/Jamfile +++ b/Jamfile @@ -8,9 +8,9 @@ project histogram : requirements - gcc:"-std=c++14 -pedantic -Wall -Wextra" - clang:"-std=c++14 -pedantic -Wall -Wextra" - darwin:"-std=c++14 -pedantic -Wall -Wextra" + gcc:"-std=c++14 -Wall -Wextra -Wno-noexcept-type" + clang:"-std=c++14 -Wall -Wextra" + darwin:"-std=c++14 -Wall -Wextra" ; build-project test ; diff --git a/test/axis_regular_test.cpp b/test/axis_regular_test.cpp new file mode 100644 index 00000000..fee1173b --- /dev/null +++ b/test/axis_regular_test.cpp @@ -0,0 +1,158 @@ +// 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) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctors + { + BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument); + } + + // axis::regular + { + axis::regular<> a{4, -2, 2}; + BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); + axis::regular<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = b; + BOOST_TEST_EQ(a, b); + axis::regular<> c = std::move(b); + BOOST_TEST_EQ(c, a); + axis::regular<> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + BOOST_TEST_EQ(a(-10.), -1); + BOOST_TEST_EQ(a(-2.1), -1); + BOOST_TEST_EQ(a(-2.0), 0); + BOOST_TEST_EQ(a(-1.1), 0); + BOOST_TEST_EQ(a(0.0), 2); + BOOST_TEST_EQ(a(0.9), 2); + BOOST_TEST_EQ(a(1.0), 3); + BOOST_TEST_EQ(a(10.), 4); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); + } + + // regular axis with inverted range + { + axis::regular<> a{2, 1, -2}; + BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[0].lower(), 1); + BOOST_TEST_EQ(a[1].lower(), -0.5); + BOOST_TEST_EQ(a[2].lower(), -2); + BOOST_TEST_EQ(a[2].upper(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a(2), -1); + BOOST_TEST_EQ(a(1.001), -1); + BOOST_TEST_EQ(a(1), 0); + BOOST_TEST_EQ(a(0), 0); + BOOST_TEST_EQ(a(-0.499), 0); + BOOST_TEST_EQ(a(-0.5), 1); + BOOST_TEST_EQ(a(-1), 1); + BOOST_TEST_EQ(a(-2), 2); + BOOST_TEST_EQ(a(-20), 2); + } + + // axis::regular with log transform + { + axis::regular> b{2, 1e0, 1e2}; + BOOST_TEST_EQ(b[-1].lower(), 0.0); + BOOST_TEST_IS_CLOSE(b[0].lower(), 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[1].lower(), 10.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[2].lower(), 100.0, 1e-9); + BOOST_TEST_EQ(b[2].upper(), std::numeric_limits::infinity()); + + BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion + BOOST_TEST_EQ(b(0), -1); + BOOST_TEST_EQ(b(1), 0); + BOOST_TEST_EQ(b(9), 0); + BOOST_TEST_EQ(b(10), 1); + BOOST_TEST_EQ(b(90), 1); + BOOST_TEST_EQ(b(100), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); + } + + // axis::regular with sqrt transform + { + axis::regular> b{2, 0, 4}; + // this is weird: -inf * -inf = inf, thus the lower bound + BOOST_TEST_EQ(b[-1].lower(), std::numeric_limits::infinity()); + BOOST_TEST_IS_CLOSE(b[0].lower(), 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[1].lower(), 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[2].lower(), 4.0, 1e-9); + BOOST_TEST_EQ(b[2].upper(), std::numeric_limits::infinity()); + + BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion + BOOST_TEST_EQ(b(0), 0); + BOOST_TEST_EQ(b(0.99), 0); + BOOST_TEST_EQ(b(1), 1); + BOOST_TEST_EQ(b(3.99), 1); + BOOST_TEST_EQ(b(4), 2); + BOOST_TEST_EQ(b(100), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); + } + + // axis::regular with quantity + { + using namespace boost::units; + using namespace boost::units::si; + using Q = quantity; + + axis::regular> b(2, 0 * meter, 2 * meter); + BOOST_TEST_EQ(b[-1].lower() / meter, -std::numeric_limits::infinity()); + BOOST_TEST_IS_CLOSE(b[0].lower() / meter, 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[1].lower() / meter, 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[2].lower() / meter, 2.0, 1e-9); + BOOST_TEST_EQ(b[2].upper() / meter, std::numeric_limits::infinity()); + + BOOST_TEST_EQ(b(-1 * meter), -1); // produces NaN in conversion + BOOST_TEST_EQ(b(0 * meter), 0); + BOOST_TEST_EQ(b(0.99 * meter), 0); + BOOST_TEST_EQ(b(1 * meter), 1); + BOOST_TEST_EQ(b(1.99 * meter), 1); + BOOST_TEST_EQ(b(2 * meter), 2); + BOOST_TEST_EQ(b(100 * meter), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity() * meter), 2); + } + + // iterators + { + enum { A, B, C }; + test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5); + test_axis_iterator( + axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); + } + + // bin_type streamable + { + auto test = [](const auto& x, const char* ref) { + std::ostringstream os; + os << x; + BOOST_TEST_EQ(os.str(), std::string(ref)); + }; + + auto a = axis::regular<>(2, 0, 1); + test(a[0], "[0, 0.5)"); + } + + return boost::report_errors(); +} diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 6dc9c1d6..4a31f2ef 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include #include #include @@ -27,28 +25,10 @@ using namespace boost::histogram; -#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps) - -template -void test_axis_iterator(const Axis& a, int begin, int end) { - for (auto bin : a) { - BOOST_TEST_EQ(bin.idx(), begin); - BOOST_TEST_EQ(bin, a[begin]); - ++begin; - } - BOOST_TEST_EQ(begin, end); - auto rit = a.rbegin(); - for (; rit != a.rend(); ++rit) { - BOOST_TEST_EQ(rit->idx(), --begin); - BOOST_TEST_EQ(*rit, a[begin]); - } -} - int main() { // bad_ctors { auto empty = std::vector(0); - BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument); BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); BOOST_TEST_THROWS((axis::variable<>(empty)), std::invalid_argument); BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); @@ -56,117 +36,6 @@ int main() { BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument); } - // axis::regular - { - axis::regular<> a{4, -2, 2}; - BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); - BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); - axis::regular<> b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = b; - BOOST_TEST_EQ(a, b); - axis::regular<> c = std::move(b); - BOOST_TEST_EQ(c, a); - axis::regular<> d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a(-10.), -1); - BOOST_TEST_EQ(a(-2.1), -1); - BOOST_TEST_EQ(a(-2.0), 0); - BOOST_TEST_EQ(a(-1.1), 0); - BOOST_TEST_EQ(a(0.0), 2); - BOOST_TEST_EQ(a(0.9), 2); - BOOST_TEST_EQ(a(1.0), 3); - BOOST_TEST_EQ(a(10.), 4); - BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); - BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); - BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); - } - - // regular axis with inverted range - { - axis::regular<> a{2, 1, -2}; - BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits::infinity()); - BOOST_TEST_EQ(a[0].lower(), 1); - BOOST_TEST_EQ(a[1].lower(), -0.5); - BOOST_TEST_EQ(a[2].lower(), -2); - BOOST_TEST_EQ(a[2].upper(), -std::numeric_limits::infinity()); - BOOST_TEST_EQ(a(2), -1); - BOOST_TEST_EQ(a(1.001), -1); - BOOST_TEST_EQ(a(1), 0); - BOOST_TEST_EQ(a(0), 0); - BOOST_TEST_EQ(a(-0.499), 0); - BOOST_TEST_EQ(a(-0.5), 1); - BOOST_TEST_EQ(a(-1), 1); - BOOST_TEST_EQ(a(-2), 2); - BOOST_TEST_EQ(a(-20), 2); - } - - // axis::regular with log transform - { - axis::regular> b{2, 1e0, 1e2}; - BOOST_TEST_EQ(b[-1].lower(), 0.0); - BOOST_TEST_IS_CLOSE(b[0].lower(), 1.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[1].lower(), 10.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[2].lower(), 100.0, 1e-9); - BOOST_TEST_EQ(b[2].upper(), std::numeric_limits::infinity()); - - BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion - BOOST_TEST_EQ(b(0), -1); - BOOST_TEST_EQ(b(1), 0); - BOOST_TEST_EQ(b(9), 0); - BOOST_TEST_EQ(b(10), 1); - BOOST_TEST_EQ(b(90), 1); - BOOST_TEST_EQ(b(100), 2); - BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); - } - - // axis::regular with sqrt transform - { - axis::regular> b{2, 0, 4}; - // this is weird: -inf * -inf = inf, thus the lower bound - BOOST_TEST_EQ(b[-1].lower(), std::numeric_limits::infinity()); - BOOST_TEST_IS_CLOSE(b[0].lower(), 0.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[1].lower(), 1.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[2].lower(), 4.0, 1e-9); - BOOST_TEST_EQ(b[2].upper(), std::numeric_limits::infinity()); - - BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion - BOOST_TEST_EQ(b(0), 0); - BOOST_TEST_EQ(b(0.99), 0); - BOOST_TEST_EQ(b(1), 1); - BOOST_TEST_EQ(b(3.99), 1); - BOOST_TEST_EQ(b(4), 2); - BOOST_TEST_EQ(b(100), 2); - BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); - } - - // axis::regular with quantity - { - using namespace boost::units; - using namespace boost::units::si; - using Q = quantity; - - axis::regular> b(2, 0 * meter, 2 * meter); - BOOST_TEST_EQ(b[-1].lower() / meter, -std::numeric_limits::infinity()); - BOOST_TEST_IS_CLOSE(b[0].lower() / meter, 0.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[1].lower() / meter, 1.0, 1e-9); - BOOST_TEST_IS_CLOSE(b[2].lower() / meter, 2.0, 1e-9); - BOOST_TEST_EQ(b[2].upper() / meter, std::numeric_limits::infinity()); - - BOOST_TEST_EQ(b(-1 * meter), -1); // produces NaN in conversion - BOOST_TEST_EQ(b(0 * meter), 0); - BOOST_TEST_EQ(b(0.99 * meter), 0); - BOOST_TEST_EQ(b(1 * meter), 1); - BOOST_TEST_EQ(b(1.99 * meter), 1); - BOOST_TEST_EQ(b(2 * meter), 2); - BOOST_TEST_EQ(b(100 * meter), 2); - BOOST_TEST_EQ(b(std::numeric_limits::infinity() * meter), 2); - } - // axis::circular { axis::circular<> a{4, 0, 1}; @@ -284,7 +153,6 @@ int main() { // iterators { enum { A, B, C }; - test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5); test_axis_iterator( axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); @@ -389,9 +257,6 @@ int main() { BOOST_TEST_EQ(os.str(), std::string(ref)); }; - auto a = axis::regular<>(2, 0, 1); - test(a[0], "[0, 0.5)"); - auto b = axis::category<>({1, 2}); test(b[0], "1"); } diff --git a/test/utility.hpp b/test/utility.hpp index 6f8555bc..668d9e29 100644 --- a/test/utility.hpp +++ b/test/utility.hpp @@ -7,6 +7,7 @@ #ifndef BOOST_HISTOGRAM_TEST_UTILITY_HPP #define BOOST_HISTOGRAM_TEST_UTILITY_HPP +#include #include #include #include @@ -129,6 +130,23 @@ constexpr bool operator!=(const tracing_allocator& t, return !operator==(t, u); } +template +void test_axis_iterator(const Axis& a, int begin, int end) { + for (auto bin : a) { + BOOST_TEST_EQ(bin.idx(), begin); + BOOST_TEST_EQ(bin, a[begin]); + ++begin; + } + BOOST_TEST_EQ(begin, end); + auto rit = a.rbegin(); + for (; rit != a.rend(); ++rit) { + BOOST_TEST_EQ(rit->idx(), --begin); + BOOST_TEST_EQ(*rit, a[begin]); + } +} + +#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps) + } // namespace histogram } // namespace boost From cabe37778f2ab4a8e3677d092aef7664ec69810d Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Fri, 26 Oct 2018 23:01:30 +0200 Subject: [PATCH 30/44] win fix --- .appveyor.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index badbdae3..b1231240 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -22,6 +22,18 @@ install: - git submodule init libs/config - git submodule init libs/core - git submodule init libs/serialization + - git submodule init libs/io + - git submodule init libs/utility + - git submodule init libs/type_traits + - git submodule init libs/unordered_map + - git submodule init libs/variant + - git submodule init libs/smart_ptr + - git submodule init libs/spirit + - git submodule init libs/static_assert + - git submodule init libs/mpl + - git submodule init libs/mp11 + - git submodule init libs/iterator + - git submodule init libs/align - git submodule update - bootstrap - b2 --with-serialization From 49f5b42687c6b8a173274935e2c38bec630dc957 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 11:49:15 +0200 Subject: [PATCH 31/44] win fix --- .appveyor.yml | 37 ++++++++++++++++++++++++++----------- examples/Jamfile | 31 ++++++++++++++++--------------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b1231240..71e3125d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,22 +18,37 @@ install: - mkdir libs\histogram - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram - git submodule init tools/build - - git submodule init libs/assert + - git submodule init libs/array + - git submodule init libs/bind + - git submodule init libs/callable_traits + - git submodule init libs/concept_check - git submodule init libs/config + - git submodule init libs/container + - git submodule init libs/container_hash - git submodule init libs/core - - git submodule init libs/serialization + - git submodule init libs/detail + - git submodule init libs/function + - git submodule init libs/integer - git submodule init libs/io - - git submodule init libs/utility - - git submodule init libs/type_traits - - git submodule init libs/unordered_map - - git submodule init libs/variant - - git submodule init libs/smart_ptr + - git submodule init libs/iterator + - git submodule init libs/lexical_cast + - git submodule init libs/math + - git submodule init libs/move + - git submodule init libs/mp11 + - git submodule init libs/mpl + - git submodule init libs/multiprecison + - git submodule init libs/numeric + - git submodule init libs/predef + - git submodule init libs/preprocessor + - git submodule init libs/range + - git submodule init libs/rational + - git submodule init libs/serialization + - git submodule init libs/smart_ptr - git submodule init libs/spirit - git submodule init libs/static_assert - - git submodule init libs/mpl - - git submodule init libs/mp11 - - git submodule init libs/iterator - - git submodule init libs/align + - git submodule init libs/type_traits + - git submodule init libs/utility + - git submodule init libs/variant - git submodule update - bootstrap - b2 --with-serialization diff --git a/examples/Jamfile b/examples/Jamfile index ca754d54..bf516331 100644 --- a/examples/Jamfile +++ b/examples/Jamfile @@ -4,6 +4,7 @@ # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) +import testing ; project histogram-example : requirements @@ -11,18 +12,18 @@ project histogram-example . ; -exe getting_started_listing_01 : getting_started_listing_01.cpp ; -exe getting_started_listing_02 : getting_started_listing_02.cpp ; -exe guide_access_bin_counts : guide_access_bin_counts.cpp ; -exe guide_axis_with_labels : guide_axis_with_labels.cpp ; -exe guide_axis_with_uoflow_off : guide_axis_with_uoflow_off.cpp ; -exe guide_custom_minimal_axis : guide_custom_minimal_axis.cpp ; -exe guide_custom_modified_axis : guide_custom_modified_axis.cpp ; -exe guide_custom_storage : guide_custom_storage.cpp ; -exe guide_fill_histogram : guide_fill_histogram.cpp ; -exe guide_histogram_operators : guide_histogram_operators.cpp ; -exe guide_histogram_reduction : guide_histogram_reduction.cpp ; -exe guide_histogram_serialization : guide_histogram_serialization.cpp /boost/serialization//boost_serialization/static ; -exe guide_histogram_streaming : guide_histogram_streaming.cpp ; -exe guide_make_dynamic_histogram : guide_make_dynamic_histogram.cpp ; -exe guide_make_static_histogram : guide_make_static_histogram.cpp ; +run getting_started_listing_01.cpp ; +run getting_started_listing_02.cpp ; +run guide_access_bin_counts.cpp ; +run guide_axis_with_labels.cpp ; +run guide_axis_with_uoflow_off.cpp ; +run guide_custom_minimal_axis.cpp ; +run guide_custom_modified_axis.cpp ; +run guide_custom_storage.cpp ; +run guide_fill_histogram.cpp ; +run guide_histogram_operators.cpp ; +run guide_histogram_reduction.cpp ; +run guide_histogram_serialization.cpp /boost/serialization//boost_serialization/static ; +run guide_histogram_streaming.cpp ; +run guide_make_dynamic_histogram.cpp ; +run guide_make_static_histogram.cpp ; From bfad63f131b785f00c866872d103c51b7e8f62b0 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 11:51:35 +0200 Subject: [PATCH 32/44] win fix --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 71e3125d..6f715baf 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -36,7 +36,7 @@ install: - git submodule init libs/move - git submodule init libs/mp11 - git submodule init libs/mpl - - git submodule init libs/multiprecison + - git submodule init libs/multiprecision - git submodule init libs/numeric - git submodule init libs/predef - git submodule init libs/preprocessor From c67403042073561f5f9921314f561f6b5611083a Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 11:57:55 +0200 Subject: [PATCH 33/44] win fix --- .appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.appveyor.yml b/.appveyor.yml index 6f715baf..a071343a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,6 +19,7 @@ install: - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram - git submodule init tools/build - git submodule init libs/array + - git submodule init libs/assert - git submodule init libs/bind - git submodule init libs/callable_traits - git submodule init libs/concept_check From 9bf7de82bb7a3849589cd982afc7c1ce364b7255 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 12:08:47 +0200 Subject: [PATCH 34/44] win fix --- .appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index a071343a..ada797ca 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -47,6 +47,8 @@ install: - git submodule init libs/smart_ptr - git submodule init libs/spirit - git submodule init libs/static_assert + - git submodule init libs/throw_exception + - git submodule init libs/type_index - git submodule init libs/type_traits - git submodule init libs/utility - git submodule init libs/variant From c45d4b0343df1abbe90170ceb135171dd2c523e2 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 12:16:51 +0200 Subject: [PATCH 35/44] win fix --- .appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.appveyor.yml b/.appveyor.yml index ada797ca..b283f677 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -39,6 +39,7 @@ install: - git submodule init libs/mpl - git submodule init libs/multiprecision - git submodule init libs/numeric + - git submodule init libs/optional - git submodule init libs/predef - git submodule init libs/preprocessor - git submodule init libs/range From 01f0f01c81cbbebbb549c8ff189d8c7e2b623255 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 12:41:02 +0200 Subject: [PATCH 36/44] win fix --- test/Jamfile | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Jamfile b/test/Jamfile index 94bf85cf..f7ce2b69 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -45,4 +45,5 @@ alias run-speed-tests : [ run speed_root.cpp ] ; +explicit run-fail-tests ; explicit run-speed-tests ; From 3023a5345359b6afb1ad7fe14be27ba50b2bb9e7 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 13:29:33 +0200 Subject: [PATCH 37/44] more noexcept and fixed iterator_value_type --- CMakeLists.txt | 7 ++++++- histogram.sublime-project | 2 +- include/boost/histogram/axis/regular.hpp | 2 +- include/boost/histogram/axis/traits.hpp | 6 +++--- include/boost/histogram/detail/meta.hpp | 4 ++-- include/boost/histogram/histogram.hpp | 4 ++-- test/meta_test.cpp | 6 +++--- 7 files changed, 18 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed4a4a1b..bf40417e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(MIN_BOOST_VERSION 1.66) +if ("$ENV{BOOST_ROOT}" STREQUAL "") + set(BOOST_INCLUDEDIR "${PROJECT_SOURCE_DIR}/../..") +endif() + # setup build option(BUILD_BENCHMARKS "Build benchmarks" OFF) option(TEST_SERIALIZATION "Test serialization code" OFF) @@ -52,7 +56,8 @@ function(compiled_test SRC) target_compile_options(${BASENAME} PRIVATE -Wall -Wextra -g -O0) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6) # -fpermissive needed for cpp_int in gcc-6 - target_compile_options(${BASENAME} PRIVATE -fpermissive) + # -Wno-noexcept-type needed for callable_traits + target_compile_options(${BASENAME} PRIVATE -fpermissive -Wno-noexcept-type) endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options(${BASENAME} PRIVATE -Wall -Wextra -g -O0 diff --git a/histogram.sublime-project b/histogram.sublime-project index ea8acab4..efd09fe5 100644 --- a/histogram.sublime-project +++ b/histogram.sublime-project @@ -21,7 +21,7 @@ { "name": "CMake", "working_dir": "${project_path}/build", - "cmd": ["{ [ ! -e Makefile ] && cmake .. ; } && make -j4 && ctest"], + "cmd": ["cmake .. && cmake --build . && ctest"], "shell": true, "file_regex": "/([^/:]+):(\\d+):(\\d+): ", } diff --git a/include/boost/histogram/axis/regular.hpp b/include/boost/histogram/axis/regular.hpp index 1f1f364e..63800108 100644 --- a/include/boost/histogram/axis/regular.hpp +++ b/include/boost/histogram/axis/regular.hpp @@ -121,7 +121,7 @@ public: regular() = default; /// Returns instance of the transform type - const transform_type& transform() const { return *this; } + const transform_type& transform() const noexcept { return *this; } /// Returns the bin index for the passed argument. int operator()(external_type x) const noexcept { diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index a7034233..82752332 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -18,7 +18,7 @@ namespace traits { using args = detail::args_type; template - decltype(auto) metadata(T&& t) { + decltype(auto) metadata(T&& t) noexcept { return detail::static_if>( [](auto&& x) -> decltype(auto) { return x.metadata(); }, [](auto&&) -> detail::copy_qualifiers { @@ -28,7 +28,7 @@ namespace traits { } template - option_type options(const T& t) { + option_type options(const T& t) noexcept { return detail::static_if>( [](const auto& x) { return x.options(); }, [](const T&) { return axis::option_type::none; }, @@ -36,7 +36,7 @@ namespace traits { } template - unsigned extend(const T& t) { + unsigned extend(const T& t) noexcept { return t.size() + static_cast(options(t)); } } // namespace traits diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 2962b4a6..3a78e957 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -49,8 +49,8 @@ template using container_element_type = mp11::mp_first>; template -using unqualified_iterator_value_type = - rm_cvref::value_type>; +using iterator_value_type = + typename std::iterator_traits::value_type; template using return_type = typename boost::callable_traits::return_type::type; diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index f7bd21be..a6b496d1 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -305,10 +305,10 @@ auto make_histogram(T&& t) /// dynamic type factory from iterator range with custom storage type template > -histogram>, +histogram>, detail::rm_cvref> make_histogram_with(Storage&& s, Iterator begin, Iterator end) { - using T = detail::unqualified_iterator_value_type; + using T = detail::iterator_value_type; auto axes = std::vector(begin, end); return make_histogram_with(std::forward(s), std::move(axes)); } diff --git a/test/meta_test.cpp b/test/meta_test.cpp index b5b2b2d4..527a0119 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -280,12 +280,12 @@ int main() { BOOST_TEST_TRAIT_TRUE((std::is_same)); } - // unqualified_iterator_value_type + // iterator_value_type { using T1 = const char*; using T2 = std::iterator; - BOOST_TEST_TRAIT_TRUE((std::is_same, char>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, char>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); } // args_type From d010101a40405098d632f33d55b862fbabb20fa5 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 14:18:01 +0200 Subject: [PATCH 38/44] refactoring --- .travis.yml | 2 +- CMakeLists.txt | 4 +- histogram.sublime-project | 2 +- test/Jamfile | 2 - test/adaptive_storage_test.cpp | 51 +++-- test/axis_regular_test.cpp | 2 +- test/axis_test.cpp | 280 ---------------------------- test/axis_variant_test.cpp | 201 ++++++++++++++++++++ test/detail_test.cpp | 117 +++++++++++- test/histogram_dynamic_add_fail.cpp | 21 --- test/histogram_static_add_fail.cpp | 14 -- test/histogram_test.cpp | 32 ++-- 12 files changed, 376 insertions(+), 352 deletions(-) create mode 100644 test/axis_variant_test.cpp delete mode 100644 test/histogram_dynamic_add_fail.cpp delete mode 100644 test/histogram_static_add_fail.cpp diff --git a/.travis.yml b/.travis.yml index 6ee49d95..0aa396de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,7 +75,7 @@ after_success: coveralls -r .. -b . --verbose --exclude ${TRAVIS_BUILD_DIR}/deps --gcov=`which ${GCOV}` --gcov-options '\-lpbc'; fi -after_failure: +# after_failure: # - otool -L histogram.so # - printf "r\nbt" > gdb.cmd # - for x in *_test; do diff --git a/CMakeLists.txt b/CMakeLists.txt index bf40417e..437e75b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,8 +82,9 @@ endfunction() compiled_test(test/adaptive_storage_test.cpp) compiled_test(test/array_storage_test.cpp) compiled_test(test/axis_test.cpp) +compiled_test(test/axis_regular_test.cpp) +compiled_test(test/axis_variant_test.cpp) compiled_test(test/detail_test.cpp) -compiled_test(test/histogram_dynamic_add_fail.cpp) compiled_test(test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp) compiled_test(test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp) compiled_test(test/histogram_dynamic_at_tuple_wrong_dimension_fail.cpp) @@ -91,7 +92,6 @@ compiled_test(test/histogram_dynamic_at_vector_wrong_dimension_fail.cpp) compiled_test(test/histogram_dynamic_at_wrong_dimension_fail.cpp) compiled_test(test/histogram_dynamic_reduce_wrong_order_fail.cpp) # test fail to compile (test/histogram_static_fill_one_dimensional_tuple_fail.cpp) -compiled_test(test/histogram_static_add_fail.cpp) compiled_test(test/histogram_static_at_vector_wrong_dimension_fail.cpp) compiled_test(test/histogram_dynamic_test.cpp) compiled_test(test/histogram_mixed_test.cpp) diff --git a/histogram.sublime-project b/histogram.sublime-project index efd09fe5..d35d0ac4 100644 --- a/histogram.sublime-project +++ b/histogram.sublime-project @@ -21,7 +21,7 @@ { "name": "CMake", "working_dir": "${project_path}/build", - "cmd": ["cmake .. && cmake --build . && ctest"], + "cmd": ["make && ctest"], "shell": true, "file_regex": "/([^/:]+):(\\d+):(\\d+): ", } diff --git a/test/Jamfile b/test/Jamfile index f7ce2b69..0600402a 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -30,12 +30,10 @@ alias run-tests : ; alias run-fail-tests : - [ run-fail histogram_dynamic_add_fail.cpp ] [ run-fail histogram_dynamic_at_tuple_wrong_dimension_fail.cpp ] [ run-fail histogram_dynamic_at_vector_wrong_dimension_fail.cpp ] [ run-fail histogram_dynamic_at_wrong_dimension_fail.cpp ] [ run-fail histogram_dynamic_reduce_wrong_order_fail.cpp ] - [ run-fail histogram_static_add_fail.cpp ] [ run-fail histogram_static_at_vector_wrong_dimension_fail.cpp ] ; diff --git a/test/adaptive_storage_test.cpp b/test/adaptive_storage_test.cpp index f934916d..b5af774f 100644 --- a/test/adaptive_storage_test.cpp +++ b/test/adaptive_storage_test.cpp @@ -50,24 +50,17 @@ void copy_impl() { } template -void equal_impl() { +void equal_1_impl() { auto a = prepare(1); auto b = prepare(1, T(0)); BOOST_TEST_EQ(a[0], 0.0); BOOST_TEST(a == b); b.increase(0); BOOST_TEST(!(a == b)); - - array_storage c; - c.reset(1); - auto d = prepare(1, T(0)); - BOOST_TEST(c == d); - c.increase(0); - BOOST_TEST(!(c == d)); } template <> -void equal_impl() { +void equal_1_impl() { auto a = prepare(1); auto b = prepare(1, 0); auto c = prepare(2, 0); @@ -87,6 +80,16 @@ void equal_impl() { BOOST_TEST(!(d == a)); } +template +void equal_2_impl() { + auto a = prepare(1); + array_storage b; + b.reset(1); + BOOST_TEST(a == b); + b.increase(0); + BOOST_TEST(!(a == b)); +} + template void increase_and_grow_impl() { auto tmax = std::numeric_limits::max(); @@ -278,13 +281,29 @@ int main() { // equal_operator { - equal_impl(); - equal_impl(); - equal_impl(); - equal_impl(); - equal_impl(); - equal_impl(); - equal_impl(); + equal_1_impl(); + equal_1_impl(); + equal_1_impl(); + equal_1_impl(); + equal_1_impl(); + equal_1_impl(); + equal_1_impl(); + + equal_2_impl(); + equal_2_impl(); + equal_2_impl(); + equal_2_impl(); + equal_2_impl(); + equal_2_impl(); + equal_2_impl(); + + equal_2_impl(); + + auto a = prepare(1); + auto b = prepare(1); + BOOST_TEST(a == b); + a.increase(0); + BOOST_TEST_NOT(a == b); } // increase_and_grow diff --git a/test/axis_regular_test.cpp b/test/axis_regular_test.cpp index fee1173b..e2392842 100644 --- a/test/axis_regular_test.cpp +++ b/test/axis_regular_test.cpp @@ -20,6 +20,7 @@ using namespace boost::histogram; int main() { // bad_ctors { + BOOST_TEST_THROWS(axis::regular<>(1, 0, 0), std::invalid_argument); BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument); } @@ -136,7 +137,6 @@ int main() { // iterators { - enum { A, B, C }; test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5); test_axis_iterator( axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 4a31f2ef..53550c99 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -4,7 +4,6 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#include #include #include #include @@ -153,8 +152,6 @@ int main() { // iterators { enum { A, B, C }; - test_axis_iterator( - axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); @@ -165,282 +162,5 @@ int main() { // std::runtime_error); } - // axis::variant copyable - { - axis::variant> a1(axis::regular<>(2, -1, 1)); - axis::variant> a2(a1); - BOOST_TEST_EQ(a1, a2); - axis::variant> a3; - BOOST_TEST_NE(a3, a1); - a3 = a1; - BOOST_TEST_EQ(a3, a1); - axis::variant> a4(axis::regular<>(3, -2, 2)); - axis::variant, axis::integer<>> a5(a4); - BOOST_TEST_EQ(a4, a5); - axis::variant> a6; - a6 = a1; - BOOST_TEST_EQ(a6, a1); - axis::variant, axis::integer<>> a7(axis::integer<>(0, 2)); - BOOST_TEST_THROWS(axis::variant> a8(a7), std::runtime_error); - BOOST_TEST_THROWS(a4 = a7, std::runtime_error); - } - - // axis::variant movable - { - axis::variant> a(axis::regular<>(2, -1, 1)); - axis::variant> r(a); - axis::variant> b(std::move(a)); - BOOST_TEST_EQ(b, r); - axis::variant> c; - BOOST_TEST_NOT(a == c); - c = std::move(b); - BOOST_TEST(c == r); - } - - // axis::variant streamable - { - auto test = [](auto&& a, const char* ref) { - using T = detail::rm_cvref; - axis::variant axis(std::move(a)); - std::ostringstream os; - os << axis; - BOOST_TEST_EQ(os.str(), std::string(ref)); - }; - - struct user_defined {}; - - namespace tr = axis::transform; - test(axis::regular<>(2, -1, 1, "regular1"), - "regular(2, -1, 1, metadata=\"regular1\", options=underflow_and_overflow)"); - test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), - "regular_log(2, 1, 10, metadata=\"regular2\", options=none)"); - test(axis::regular>(1.5, 2, 1, 10, "regular3", axis::option_type::overflow), - "regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=1.5)"); - test(axis::regular>(-1.5, 2, 1, 10, "regular4", axis::option_type::none), - "regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-1.5)"); - test(axis::circular(4, 0.1, 1.0), - "circular(4, 0.1, 1.1, options=overflow)"); - test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none), - "variable(-1, 0, 1, metadata=\"variable\", options=none)"); - test(axis::category<>({0, 1, 2}, "category"), - "category(0, 1, 2, metadata=\"category\", options=overflow)"); - test(axis::category({"A", "B"}, "category2"), - "category(\"A\", \"B\", metadata=\"category2\", options=overflow)"); -#ifndef _MSC_VER // fails on MSVC because demagnled name for user_defined looks different - test(axis::integer(-1, 1, {}, axis::option_type::none), - "integer(-1, 1, metadata=main::user_defined, options=none)"); -#endif - } - - // axis::variant support for minimal_axis - { - struct minimal_axis { - int operator()(double) const { return 0; } - unsigned size() const { return 1; } - }; - - axis::variant axis; - BOOST_TEST_EQ(axis(0), 0); - BOOST_TEST_EQ(axis(10), 0); - BOOST_TEST_EQ(axis.size(), 1); - BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); - BOOST_TEST_THROWS(axis.value(0), std::runtime_error); - BOOST_TEST_TRAIT_TRUE( - (std::is_same)); - } - - // bin_type streamable - { - auto test = [](const auto& x, const char* ref) { - std::ostringstream os; - os << x; - BOOST_TEST_EQ(os.str(), std::string(ref)); - }; - - auto b = axis::category<>({1, 2}); - test(b[0], "1"); - } - - // axis::variant equal_comparable - { - enum { A, B, C }; - using variant = axis::variant, axis::regular>, - axis::circular<>, axis::variable<>, axis::category<>, - axis::integer<>>; - std::vector axes; - axes.push_back(axis::regular<>{2, -1, 1}); - axes.push_back(axis::regular>( - 0.5, 2, 1, 4, "", axis::option_type::underflow_and_overflow)); - axes.push_back(axis::circular<>{4}); - axes.push_back(axis::variable<>{-1, 0, 1}); - axes.push_back(axis::category<>({A, B, C})); - axes.push_back(axis::integer<>{-1, 1}); - for (const auto& a : axes) { - BOOST_TEST(!(a == variant())); - BOOST_TEST_EQ(a, variant(a)); - } - BOOST_TEST_NOT(axes == std::vector()); - BOOST_TEST(axes == std::vector(axes)); - } - - // axis::variant value_to_index_failure - { - axis::variant> x = - axis::category({"A", "B"}, "category"); - auto cx = axis::get>(x); - BOOST_TEST_EQ(cx("B"), 1); - } - - // sequence equality - { - enum { A, B, C }; - std::vector, axis::variable<>, axis::category<>, - axis::integer<>>> - std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, - axis::category<>{A, B, C}}; - - std::vector, axis::variable<>, axis::category<>>> - std_vector2 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, - axis::category<>{{A, B, C}}}; - - std::vector, axis::variable<>>> std_vector3 = { - axis::variable<>{-1, 0, 1}, axis::regular<>{2, -1, 1}}; - - std::vector, axis::regular<>>> std_vector4 = { - axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}}; - - BOOST_TEST(detail::axes_equal(std_vector1, std_vector2)); - BOOST_TEST_NOT(detail::axes_equal(std_vector2, std_vector3)); - BOOST_TEST_NOT(detail::axes_equal(std_vector3, std_vector4)); - - auto tuple1 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, - axis::category<>{{A, B, C}}); - - auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, - axis::category<>{{A, B}}); - - auto tuple3 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}); - - BOOST_TEST(detail::axes_equal(std_vector1, tuple1)); - BOOST_TEST(detail::axes_equal(tuple1, std_vector1)); - BOOST_TEST_NOT(detail::axes_equal(tuple1, tuple2)); - BOOST_TEST_NOT(detail::axes_equal(tuple2, tuple3)); - BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple3)); - } - - // sequence assign - { - enum { A, B, C, D }; - std::vector, axis::variable<>, axis::category<>, - axis::integer<>>> - std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, - axis::category<>{A, B, C}}; - - std::vector, axis::variable<>, axis::category<>>> - std_vector2 = {axis::regular<>{2, -2, 2}, axis::variable<>{-2, 0, 2}, - axis::category<>{A, B}}; - - detail::axes_assign(std_vector2, std_vector1); - BOOST_TEST(detail::axes_equal(std_vector2, std_vector1)); - - auto tuple1 = std::make_tuple(axis::regular<>{2, -3, 3}, axis::variable<>{-3, 0, 3}, - axis::category<>{A, B, C, D}); - - detail::axes_assign(tuple1, std_vector1); - BOOST_TEST(detail::axes_equal(tuple1, std_vector1)); - - decltype(std_vector1) std_vector3; - BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple1)); - detail::axes_assign(std_vector3, tuple1); - BOOST_TEST(detail::axes_equal(std_vector3, tuple1)); - - auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, - axis::category<>{A, B}); - - detail::axes_assign(tuple2, tuple1); - BOOST_TEST(detail::axes_equal(tuple2, tuple1)); - } - - // sub_axes - { - using ra = axis::regular<>; - using ia = axis::integer<>; - using ca = axis::category<>; - using T = std::tuple; - BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); - BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); - BOOST_TEST_TRAIT_TRUE( - (std::is_same, std::tuple>)); - BOOST_TEST_TRAIT_TRUE( - (std::is_same, std::tuple>)); - BOOST_TEST_TRAIT_TRUE( - (std::is_same, std::tuple>)); - BOOST_TEST_TRAIT_TRUE( - (std::is_same, std::tuple>)); - } - - // make_sub_tuple - { - using ia = axis::integer<>; - using T = std::tuple; - auto axes = T(ia(0, 1), ia(1, 2), ia(2, 3)); - BOOST_TEST_EQ(detail::make_sub_axes(axes, i1(), i2()), - (std::tuple(ia(1, 2), ia(2, 3)))); - BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1()), - (std::tuple(ia(0, 1), ia(1, 2)))); - BOOST_TEST_EQ(detail::make_sub_axes(axes, i1()), (std::tuple(ia(1, 2)))); - BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1(), i2()), axes); - } - - // vector of axes with custom allocators - { - struct null {}; - using M = std::vector>; - using T1 = axis::regular, M>; - using T2 = axis::circular; - using T3 = axis::variable, null>; - using T4 = axis::integer; - using T5 = axis::category, null>; - using axis_type = axis::variant; // no heap allocation - using axes_type = std::vector>; - - tracing_allocator_db db; - { - auto a = tracing_allocator(db); - axes_type axes(a); - axes.reserve(5); - axes.emplace_back(T1(1, 0, 1, M(3, 'c', a))); - axes.emplace_back(T2(2)); - axes.emplace_back( - T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a)); - axes.emplace_back(T4(0, 4)); - axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); - } - // 5 axis::variant objects - BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second); - BOOST_TEST_EQ(db[typeid(axis_type)].first, 5); - - // label - BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); - BOOST_TEST_EQ(db[typeid(char)].first, 3u); - - // nothing to allocate for T1 - // nothing to allocate for T2 - // T3 allocates storage for bin edges - BOOST_TEST_EQ(db[typeid(double)].first, db[typeid(double)].second); - BOOST_TEST_EQ(db[typeid(double)].first, 3u); - // nothing to allocate for T4 - // T5 allocates storage for long array - BOOST_TEST_EQ(db[typeid(long)].first, db[typeid(long)].second); - BOOST_TEST_EQ(db[typeid(long)].first, 5u); - -#if (BOOST_MSVC) - BOOST_TEST_EQ(db.size(), 5); // axis_type, char, double, long + ??? -#else - BOOST_TEST_EQ(db.size(), 4); // axis_type, char, double, long -#endif - } - return boost::report_errors(); } diff --git a/test/axis_variant_test.cpp b/test/axis_variant_test.cpp new file mode 100644 index 00000000..72459dcd --- /dev/null +++ b/test/axis_variant_test.cpp @@ -0,0 +1,201 @@ +// 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) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utility.hpp" + +using namespace boost::histogram; + +int main() { + // axis::variant copyable + { + axis::variant> a1(axis::regular<>(2, -1, 1)); + axis::variant> a2(a1); + BOOST_TEST_EQ(a1, a2); + axis::variant> a3; + BOOST_TEST_NE(a3, a1); + a3 = a1; + BOOST_TEST_EQ(a3, a1); + axis::variant> a4(axis::regular<>(3, -2, 2)); + axis::variant, axis::integer<>> a5(a4); + BOOST_TEST_EQ(a4, a5); + axis::variant> a6; + a6 = a1; + BOOST_TEST_EQ(a6, a1); + axis::variant, axis::integer<>> a7(axis::integer<>(0, 2)); + BOOST_TEST_THROWS(axis::variant> a8(a7), std::runtime_error); + BOOST_TEST_THROWS(a4 = a7, std::runtime_error); + } + + // axis::variant movable + { + axis::variant> a(axis::regular<>(2, -1, 1)); + axis::variant> r(a); + axis::variant> b(std::move(a)); + BOOST_TEST_EQ(b, r); + axis::variant> c; + BOOST_TEST_NOT(a == c); + c = std::move(b); + BOOST_TEST(c == r); + } + + // axis::variant streamable + { + auto test = [](auto&& a, const char* ref) { + using T = detail::rm_cvref; + axis::variant axis(std::move(a)); + std::ostringstream os; + os << axis; + BOOST_TEST_EQ(os.str(), std::string(ref)); + }; + + struct user_defined {}; + + namespace tr = axis::transform; + test(axis::regular<>(2, -1, 1, "regular1"), + "regular(2, -1, 1, metadata=\"regular1\", options=underflow_and_overflow)"); + test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), + "regular_log(2, 1, 10, metadata=\"regular2\", options=none)"); + test(axis::regular>(1.5, 2, 1, 10, "regular3", axis::option_type::overflow), + "regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=1.5)"); + test(axis::regular>(-1.5, 2, 1, 10, "regular4", axis::option_type::none), + "regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-1.5)"); + test(axis::circular(4, 0.1, 1.0), + "circular(4, 0.1, 1.1, options=overflow)"); + test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none), + "variable(-1, 0, 1, metadata=\"variable\", options=none)"); + test(axis::category<>({0, 1, 2}, "category"), + "category(0, 1, 2, metadata=\"category\", options=overflow)"); + test(axis::category({"A", "B"}, "category2"), + "category(\"A\", \"B\", metadata=\"category2\", options=overflow)"); +#ifndef _MSC_VER // fails on MSVC because demagnled name for user_defined looks different + test(axis::integer(-1, 1, {}, axis::option_type::none), + "integer(-1, 1, metadata=main::user_defined, options=none)"); +#endif + } + + // axis::variant support for minimal_axis + { + struct minimal_axis { + int operator()(double) const { return 0; } + unsigned size() const { return 1; } + }; + + axis::variant axis; + BOOST_TEST_EQ(axis(0), 0); + BOOST_TEST_EQ(axis(10), 0); + BOOST_TEST_EQ(axis.size(), 1); + BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); + BOOST_TEST_THROWS(axis.value(0), std::runtime_error); + BOOST_TEST_TRAIT_TRUE( + (std::is_same)); + } + + // bin_type streamable + { + auto test = [](const auto& x, const char* ref) { + std::ostringstream os; + os << x; + BOOST_TEST_EQ(os.str(), std::string(ref)); + }; + + auto b = axis::category<>({1, 2}); + test(b[0], "1"); + } + + // axis::variant equal_comparable + { + enum { A, B, C }; + using variant = axis::variant, axis::regular>, + axis::circular<>, axis::variable<>, axis::category<>, + axis::integer<>>; + std::vector axes; + axes.push_back(axis::regular<>{2, -1, 1}); + axes.push_back(axis::regular>( + 0.5, 2, 1, 4, "", axis::option_type::underflow_and_overflow)); + axes.push_back(axis::circular<>{4}); + axes.push_back(axis::variable<>{-1, 0, 1}); + axes.push_back(axis::category<>({A, B, C})); + axes.push_back(axis::integer<>{-1, 1}); + for (const auto& a : axes) { + BOOST_TEST(!(a == variant())); + BOOST_TEST_EQ(a, variant(a)); + } + BOOST_TEST_NOT(axes == std::vector()); + BOOST_TEST(axes == std::vector(axes)); + } + + // axis::variant value_to_index_failure + { + axis::variant> x = + axis::category({"A", "B"}, "category"); + auto cx = axis::get>(x); + BOOST_TEST_EQ(cx("B"), 1); + } + + // vector of axes with custom allocators + { + struct null {}; + using M = std::vector>; + using T1 = axis::regular, M>; + using T2 = axis::circular; + using T3 = axis::variable, null>; + using T4 = axis::integer; + using T5 = axis::category, null>; + using axis_type = axis::variant; // no heap allocation + using axes_type = std::vector>; + + tracing_allocator_db db; + { + auto a = tracing_allocator(db); + axes_type axes(a); + axes.reserve(5); + axes.emplace_back(T1(1, 0, 1, M(3, 'c', a))); + axes.emplace_back(T2(2)); + axes.emplace_back( + T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a)); + axes.emplace_back(T4(0, 4)); + axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); + } + // 5 axis::variant objects + BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second); + BOOST_TEST_EQ(db[typeid(axis_type)].first, 5); + + // label + BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); + BOOST_TEST_EQ(db[typeid(char)].first, 3u); + + // nothing to allocate for T1 + // nothing to allocate for T2 + // T3 allocates storage for bin edges + BOOST_TEST_EQ(db[typeid(double)].first, db[typeid(double)].second); + BOOST_TEST_EQ(db[typeid(double)].first, 3u); + // nothing to allocate for T4 + // T5 allocates storage for long array + BOOST_TEST_EQ(db[typeid(long)].first, db[typeid(long)].second); + BOOST_TEST_EQ(db[typeid(long)].first, 5u); + +#if (BOOST_MSVC) + BOOST_TEST_EQ(db.size(), 5); // axis_type, char, double, long + ??? +#else + BOOST_TEST_EQ(db.size(), 4); // axis_type, char, double, long +#endif + } + + return boost::report_errors(); +} diff --git a/test/detail_test.cpp b/test/detail_test.cpp index d22caa1a..02fa569f 100644 --- a/test/detail_test.cpp +++ b/test/detail_test.cpp @@ -5,12 +5,125 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utility.hpp" -using namespace boost::histogram::detail; +using namespace boost::histogram; int main() { - BOOST_TEST_EQ(cat("foo", 1, "bar"), "foo1bar"); + BOOST_TEST_EQ(detail::cat("foo", 1, "bar"), "foo1bar"); + + // sequence equality + { + enum { A, B, C }; + std::vector, axis::variable<>, axis::category<>, + axis::integer<>>> + std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, + axis::category<>{A, B, C}}; + + std::vector, axis::variable<>, axis::category<>>> + std_vector2 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, + axis::category<>{{A, B, C}}}; + + std::vector, axis::variable<>>> std_vector3 = { + axis::variable<>{-1, 0, 1}, axis::regular<>{2, -1, 1}}; + + std::vector, axis::regular<>>> std_vector4 = { + axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}}; + + BOOST_TEST(detail::axes_equal(std_vector1, std_vector2)); + BOOST_TEST_NOT(detail::axes_equal(std_vector2, std_vector3)); + BOOST_TEST_NOT(detail::axes_equal(std_vector3, std_vector4)); + + auto tuple1 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, + axis::category<>{{A, B, C}}); + + auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, + axis::category<>{{A, B}}); + + auto tuple3 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}); + + BOOST_TEST(detail::axes_equal(std_vector1, tuple1)); + BOOST_TEST(detail::axes_equal(tuple1, std_vector1)); + BOOST_TEST_NOT(detail::axes_equal(tuple1, tuple2)); + BOOST_TEST_NOT(detail::axes_equal(tuple2, tuple3)); + BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple3)); + } + + // sequence assign + { + enum { A, B, C, D }; + std::vector, axis::variable<>, axis::category<>, + axis::integer<>>> + std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, + axis::category<>{A, B, C}}; + + std::vector, axis::variable<>, axis::category<>>> + std_vector2 = {axis::regular<>{2, -2, 2}, axis::variable<>{-2, 0, 2}, + axis::category<>{A, B}}; + + detail::axes_assign(std_vector2, std_vector1); + BOOST_TEST(detail::axes_equal(std_vector2, std_vector1)); + + auto tuple1 = std::make_tuple(axis::regular<>{2, -3, 3}, axis::variable<>{-3, 0, 3}, + axis::category<>{A, B, C, D}); + + detail::axes_assign(tuple1, std_vector1); + BOOST_TEST(detail::axes_equal(tuple1, std_vector1)); + + decltype(std_vector1) std_vector3; + BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple1)); + detail::axes_assign(std_vector3, tuple1); + BOOST_TEST(detail::axes_equal(std_vector3, tuple1)); + + auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}, + axis::category<>{A, B}); + + detail::axes_assign(tuple2, tuple1); + BOOST_TEST(detail::axes_equal(tuple2, tuple1)); + } + + // sub_axes + { + using ra = axis::regular<>; + using ia = axis::integer<>; + using ca = axis::category<>; + using T = std::tuple; + BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_same, std::tuple>)); + } + + // make_sub_tuple + { + using ia = axis::integer<>; + using T = std::tuple; + auto axes = T(ia(0, 1), ia(1, 2), ia(2, 3)); + BOOST_TEST_EQ(detail::make_sub_axes(axes, i1(), i2()), + (std::tuple(ia(1, 2), ia(2, 3)))); + BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1()), + (std::tuple(ia(0, 1), ia(1, 2)))); + BOOST_TEST_EQ(detail::make_sub_axes(axes, i1()), (std::tuple(ia(1, 2)))); + BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1(), i2()), axes); + } return boost::report_errors(); } diff --git a/test/histogram_dynamic_add_fail.cpp b/test/histogram_dynamic_add_fail.cpp deleted file mode 100644 index d54405db..00000000 --- a/test/histogram_dynamic_add_fail.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// 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) - -#include -#include - -using namespace boost::histogram; -int main() { - auto va = std::vector>>(); - va.push_back(axis::integer<>(0, 2)); - auto a = make_histogram(va); - - auto vb = std::vector>>(); - vb.push_back(axis::integer<>(0, 3)); - auto b = make_histogram(vb); - - a += b; -} diff --git a/test/histogram_static_add_fail.cpp b/test/histogram_static_add_fail.cpp deleted file mode 100644 index 91c9b3ab..00000000 --- a/test/histogram_static_add_fail.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// 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) - -#include - -using namespace boost::histogram; -int main() { - auto a = make_histogram(axis::integer<>(0, 2)); - auto b = make_histogram(axis::integer<>(0, 3)); - a += b; -} diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 51ad13bc..7d961b0f 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -451,7 +451,20 @@ void run_tests() { BOOST_TEST_EQ(d.at(3), 0); } - // functional programming + // bad add + { + auto va = std::vector>>(); + va.push_back(axis::integer<>(0, 2)); + auto a = make_histogram(va); + + auto vb = std::vector>>(); + vb.push_back(axis::integer<>(0, 3)); + auto b = make_histogram(vb); + + BOOST_TEST_THROWS(a += b, std::invalid_argument); + } + + // STL support { auto v = std::vector{0, 1, 2}; auto h = std::for_each(v.begin(), v.end(), make(Tag(), axis::integer<>(0, 3))); @@ -459,6 +472,12 @@ void run_tests() { BOOST_TEST_EQ(h.at(1), 1); BOOST_TEST_EQ(h.at(2), 1); BOOST_TEST_EQ(sum(h), 3); + + auto a = std::vector(); + std::partial_sum(h.begin(), h.end(), std::back_inserter(a)); + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 2); + BOOST_TEST_EQ(a[2], 3); } // operators @@ -741,17 +760,6 @@ void run_tests() { BOOST_TEST_EQ(v.variance(), 6); } - // STL compatibility - { - auto h = make(Tag(), axis::integer<>(0, 3)); - 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], 1); - BOOST_TEST_EQ(a[1], 2); - BOOST_TEST_EQ(a[2], 3); - } - // using STL containers { auto h = make_s(Tag(), From 8200e3f2c55a9963dc53d634447a685bcfd3ab3f Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 17:42:35 +0200 Subject: [PATCH 39/44] generialization of axis::variant --- CMakeLists.txt | 5 +- examples/guide_custom_modified_axis.cpp | 26 +-- include/boost/histogram/axis/category.hpp | 4 +- include/boost/histogram/axis/circular.hpp | 4 +- include/boost/histogram/axis/integer.hpp | 22 ++- ...nterval_view.hpp => interval_bin_view.hpp} | 12 +- .../histogram/axis/ostream_operators.hpp | 48 +++-- .../histogram/axis/polymorphic_bin_view.hpp | 67 +++++++ include/boost/histogram/axis/regular.hpp | 8 +- .../{value_view.hpp => value_bin_view.hpp} | 14 +- include/boost/histogram/axis/variable.hpp | 4 +- include/boost/histogram/axis/variant.hpp | 47 +++-- include/boost/histogram/detail/meta.hpp | 5 +- include/boost/histogram/histogram_fwd.hpp | 2 +- test/Jamfile | 9 +- test/axis_category_test.cpp | 65 +++++++ test/axis_circular_test.cpp | 56 ++++++ test/axis_integer_test.cpp | 79 +++++++++ test/axis_test.cpp | 166 ------------------ test/axis_variable_test.cpp | 60 +++++++ test/axis_variant_test.cpp | 78 +++++--- 21 files changed, 518 insertions(+), 263 deletions(-) rename include/boost/histogram/axis/{interval_view.hpp => interval_bin_view.hpp} (70%) create mode 100644 include/boost/histogram/axis/polymorphic_bin_view.hpp rename include/boost/histogram/axis/{value_view.hpp => value_bin_view.hpp} (63%) create mode 100644 test/axis_category_test.cpp create mode 100644 test/axis_circular_test.cpp create mode 100644 test/axis_integer_test.cpp delete mode 100644 test/axis_test.cpp create mode 100644 test/axis_variable_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 437e75b5..11cd910b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,8 +81,11 @@ endfunction() compiled_test(test/adaptive_storage_test.cpp) compiled_test(test/array_storage_test.cpp) -compiled_test(test/axis_test.cpp) compiled_test(test/axis_regular_test.cpp) +compiled_test(test/axis_circular_test.cpp) +compiled_test(test/axis_variable_test.cpp) +compiled_test(test/axis_integer_test.cpp) +compiled_test(test/axis_category_test.cpp) compiled_test(test/axis_variant_test.cpp) compiled_test(test/detail_test.cpp) compiled_test(test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp) diff --git a/examples/guide_custom_modified_axis.cpp b/examples/guide_custom_modified_axis.cpp index b09c6ef1..d146e4e8 100644 --- a/examples/guide_custom_modified_axis.cpp +++ b/examples/guide_custom_modified_axis.cpp @@ -1,7 +1,8 @@ //[ guide_custom_modified_axis #include -#include +#include +#include namespace bh = boost::histogram; @@ -18,22 +19,25 @@ struct custom_axis : public bh::axis::integer<> { }; int main() { - auto h = bh::make_histogram(custom_axis(0, 3)); + auto h = bh::make_histogram(custom_axis(3, 6)); h("-10"); - h("0"); - h("1"); + h("3"); + h("4"); h("9"); + std::ostringstream os; for (auto xi : h.axis()) { - std::cout << "bin " << xi.idx() << " [" << xi.lower() << ", " - << xi.upper() << ") " << h.at(xi) << std::endl; + os << "bin " << xi.idx() + << " [" << xi.lower() << ", " << xi.upper() << ") " + << h.at(xi) << "\n"; } - /* prints: - bin 0 [0, 1) 1 - bin 1 [1, 2] 1 - bin 2 [2, 3] 0 - */ + std::cout << os.str() << std::endl; + + assert(os.str() == + "bin 0 [3, 4) 1\n" + "bin 1 [4, 5) 1\n" + "bin 2 [5, 6) 0\n"); } //] diff --git a/include/boost/histogram/axis/category.hpp b/include/boost/histogram/axis/category.hpp index f62b2f46..88c4915d 100644 --- a/include/boost/histogram/axis/category.hpp +++ b/include/boost/histogram/axis/category.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -132,7 +132,7 @@ public: return x_.first()[idx]; } - auto operator[](int idx) const noexcept { return value_view(idx, *this); } + auto operator[](int idx) const noexcept { return value_bin_view(idx, *this); } bool operator==(const category& o) const noexcept { return base_type::operator==(o) && diff --git a/include/boost/histogram/axis/circular.hpp b/include/boost/histogram/axis/circular.hpp index 434df99d..d4cd96a8 100644 --- a/include/boost/histogram/axis/circular.hpp +++ b/include/boost/histogram/axis/circular.hpp @@ -8,7 +8,7 @@ #define BOOST_HISTOGRAM_AXIS_CIRCULAR_HPP #include -#include +#include #include #include #include @@ -71,7 +71,7 @@ public: /// Returns axis value for fractional index. value_type value(value_type i) const noexcept { return phase_ + i * delta_; } - auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + auto operator[](int idx) const noexcept { return interval_bin_view(idx, *this); } bool operator==(const circular& o) const noexcept { return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_; diff --git a/include/boost/histogram/axis/integer.hpp b/include/boost/histogram/axis/integer.hpp index d2b30816..e36e396a 100644 --- a/include/boost/histogram/axis/integer.hpp +++ b/include/boost/histogram/axis/integer.hpp @@ -8,7 +8,8 @@ #define BOOST_HISTOGRAM_AXIS_INTEGER_HPP #include -#include +#include +#include #include #include #include @@ -30,7 +31,8 @@ class integer : public base, public iterator_mixin; using value_type = IntType; using metadata_type = MetaData; - using bin_type = interval_view; + using bin_view = std::conditional_t::value, + value_bin_view, interval_bin_view>; public: /** Construct over semi-open integer interval [start, stop). @@ -58,14 +60,24 @@ public: /// Returns axis value for index. value_type value(value_type i) const noexcept { - if (i < 0) { return std::numeric_limits::min(); } + if (i < 0) { + return detail::static_if>( + [](auto) { return std::numeric_limits::min(); }, + [](auto) { return -std::numeric_limits::infinity(); }, + 0 + ); + } if (i > static_cast(base_type::size())) { - return std::numeric_limits::max(); + return detail::static_if>( + [](auto) { return std::numeric_limits::max(); }, + [](auto) { return std::numeric_limits::infinity(); }, + 0 + ); } return min_ + i; } - bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } + decltype(auto) operator[](int idx) const noexcept { return bin_view(idx, *this); } bool operator==(const integer& o) const noexcept { return base_type::operator==(o) && min_ == o.min_; diff --git a/include/boost/histogram/axis/interval_view.hpp b/include/boost/histogram/axis/interval_bin_view.hpp similarity index 70% rename from include/boost/histogram/axis/interval_view.hpp rename to include/boost/histogram/axis/interval_bin_view.hpp index 1787e03b..f98bf026 100644 --- a/include/boost/histogram/axis/interval_view.hpp +++ b/include/boost/histogram/axis/interval_bin_view.hpp @@ -4,17 +4,17 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP -#define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP +#ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_BIN_VIEW_HPP +#define BOOST_HISTOGRAM_AXIS_INTERVAL_BIN_VIEW_HPP namespace boost { namespace histogram { namespace axis { template -class interval_view { +class interval_bin_view { public: - interval_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} + interval_bin_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} int idx() const noexcept { return idx_; } @@ -23,10 +23,10 @@ public: decltype(auto) center() const noexcept { return axis_.value(idx_ + 0.5); } decltype(auto) width() const noexcept { return upper() - lower(); } - bool operator==(const interval_view& rhs) const noexcept { + bool operator==(const interval_bin_view& rhs) const noexcept { return idx_ == rhs.idx_ && axis_ == rhs.axis_; } - bool operator!=(const interval_view& rhs) const noexcept { return !operator==(rhs); } + bool operator!=(const interval_bin_view& rhs) const noexcept { return !operator==(rhs); } explicit operator int() const noexcept { return idx_; } diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 6773096d..b3e19ec3 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -13,9 +13,9 @@ #include #include #include -#include +#include #include -#include +#include #include #include #include @@ -61,18 +61,7 @@ void stream_metadata(OStream& os, const T& t) { template void stream_options(OStream& os, const axis::option_type o) { - os << ", options="; - switch (o) { - case axis::option_type::none: - os << "none"; - break; - case axis::option_type::overflow: - os << "overflow"; - break; - case axis::option_type::underflow_and_overflow: - os << "underflow_and_overflow"; - break; - } + os << ", options=" << o; } template @@ -97,6 +86,23 @@ void stream_value(OStream& os, const std::basic_string& t) { namespace axis { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const axis::option_type o) { + switch (o) { + case axis::option_type::none: + os << "none"; + break; + case axis::option_type::overflow: + os << "overflow"; + break; + case axis::option_type::underflow_and_overflow: + os << "underflow_and_overflow"; + break; + } + return os; +} + template std::basic_ostream& operator<<(std::basic_ostream& os, const empty_metadata_type&) { @@ -105,18 +111,28 @@ std::basic_ostream& operator<<(std::basic_ostream& os, template std::basic_ostream& operator<<(std::basic_ostream& os, - const interval_view& i) { + const interval_bin_view& i) { os << "[" << i.lower() << ", " << i.upper() << ")"; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, - const value_view& i) { + const value_bin_view& i) { os << i.value(); return os; } +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const polymorphic_bin_view& i) { + if (i.is_continuous()) + os << "[" << i.lower() << ", " << i.upper() << ")"; + else + os << i.value(); + return os; +} + template std::basic_ostream& operator<<(std::basic_ostream& os, const regular& a) { diff --git a/include/boost/histogram/axis/polymorphic_bin_view.hpp b/include/boost/histogram/axis/polymorphic_bin_view.hpp new file mode 100644 index 00000000..29830581 --- /dev/null +++ b/include/boost/histogram/axis/polymorphic_bin_view.hpp @@ -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) + +#ifndef BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_VIEW_HPP +#define BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_VIEW_HPP + +#include + +namespace boost { +namespace histogram { +namespace axis { + +template +class polymorphic_bin_view { + using value_type = detail::return_type; +public: + polymorphic_bin_view(int idx, const Axis& axis, bool is_continuous) + : idx_(idx), axis_(axis), is_continuous_(is_continuous) {} + + int idx() const noexcept { return idx_; } + + value_type value() const { + if (is_continuous_) + throw std::runtime_error("calling value() for continuous axis is ambiguous"); + return axis_.value(idx_); + } + value_type lower() const { + if (!is_continuous_) + throw std::runtime_error("cannot call lower() for discontinuous axis"); + return axis_.value(idx_); + } + value_type upper() const { + if (!is_continuous_) + throw std::runtime_error("cannot call upper() for discontinuous axis"); + return axis_.value(idx_ + 1); + } + value_type center() const { + if (!is_continuous_) + throw std::runtime_error("cannot call center() for discontinuous axis"); + return axis_.value(idx_ + 0.5); + } + template () - std::declval())> + value_type width() const { return upper() - lower(); } + + bool operator==(const polymorphic_bin_view& rhs) const noexcept { + return idx_ == rhs.idx_ && axis_ == rhs.axis_; + } + bool operator!=(const polymorphic_bin_view& rhs) const noexcept { return !operator==(rhs); } + + explicit operator int() const noexcept { return idx_; } + + bool is_continuous() const noexcept { return is_continuous_; } + +private: + const int idx_; + const Axis& axis_; + const bool is_continuous_; +}; + +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/regular.hpp b/include/boost/histogram/axis/regular.hpp index 63800108..53c38f9e 100644 --- a/include/boost/histogram/axis/regular.hpp +++ b/include/boost/histogram/axis/regular.hpp @@ -8,7 +8,7 @@ #define BOOST_HISTOGRAM_AXIS_REGULAR_HPP #include -#include +#include #include #include #include @@ -103,7 +103,9 @@ public: , min_(this->forward(start)) , delta_((this->forward(stop) - this->forward(start)) / n) { if (!std::isfinite(min_) || !std::isfinite(delta_)) - throw std::invalid_argument("forward transform of lower or upper invalid"); + throw std::invalid_argument("forward transform of start or stop invalid"); + if (delta_ == 0) + throw std::invalid_argument("range of forward transformed axis is zero"); } /** Construct n bins over real range [begin, end). @@ -155,7 +157,7 @@ public: } /// Access bin at index - auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + auto operator[](int idx) const noexcept { return interval_bin_view(idx, *this); } bool operator==(const regular& o) const noexcept { return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ && diff --git a/include/boost/histogram/axis/value_view.hpp b/include/boost/histogram/axis/value_bin_view.hpp similarity index 63% rename from include/boost/histogram/axis/value_view.hpp rename to include/boost/histogram/axis/value_bin_view.hpp index b24ce606..ed3e8dff 100644 --- a/include/boost/histogram/axis/value_view.hpp +++ b/include/boost/histogram/axis/value_bin_view.hpp @@ -4,26 +4,28 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP -#define BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP +#ifndef BOOST_HISTOGRAM_AXIS_VALUE_BIN_VIEW_HPP +#define BOOST_HISTOGRAM_AXIS_VALUE_BIN_VIEW_HPP + +#include namespace boost { namespace histogram { namespace axis { template -class value_view { +class value_bin_view { public: - value_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} + value_bin_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} int idx() const noexcept { return idx_; } decltype(auto) value() const { return axis_.value(idx_); } - bool operator==(const value_view& rhs) const noexcept { + bool operator==(const value_bin_view& rhs) const noexcept { return idx_ == rhs.idx_ && axis_ == rhs.axis_; } - bool operator!=(const value_view& rhs) const noexcept { return !operator==(rhs); } + bool operator!=(const value_bin_view& rhs) const noexcept { return !operator==(rhs); } explicit operator int() const noexcept { return idx_; } diff --git a/include/boost/histogram/axis/variable.hpp b/include/boost/histogram/axis/variable.hpp index e358b19b..787a65d4 100644 --- a/include/boost/histogram/axis/variable.hpp +++ b/include/boost/histogram/axis/variable.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -158,7 +158,7 @@ public: return (1.0 - z) * x_.first()[k] + z * x_.first()[k + 1]; } - auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + auto operator[](int idx) const noexcept { return interval_bin_view(idx, *this); } bool operator==(const variable& o) const noexcept { return base_type::operator==(o) && diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 5b35432e..9a70274e 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -21,6 +21,7 @@ #include #include #include +#include namespace boost { namespace histogram { @@ -44,7 +45,6 @@ namespace axis { template class variant : private boost::variant, public iterator_mixin> { using base_type = boost::variant; - using bin_type = interval_view; using first_bounded_type = mp11::mp_first; using metadata_type = detail::rm_cvref()))>; @@ -141,22 +141,24 @@ public: *this); } - // Only works for axes with compatible call signature - // and will throw a invalid_argument exception otherwise - int operator()(double x) const { + // Will throw invalid_argument exception if axis has incompatible call signature + template + int operator()(Us... x) const { + auto&& args = std::forward_as_tuple(std::forward(x)...); return visit( - [x](const auto& a) { - using T = detail::rm_cvref; - using args = axis::traits::args; - return detail::static_if_c<( - mp11::mp_size::value == 1 && - std::is_convertible>::value)>( - [x](const auto& a) -> int { return a(x); }, - [](const auto& a) -> int { + [&args](const auto& a) { + using A = detail::rm_cvref; + using args_t = std::tuple; + using expected_args_t = axis::traits::args; + return detail::static_if>( + [&args](const auto& a) -> int { return mp11::tuple_apply(a, args); }, + [](const auto&) -> int { throw std::invalid_argument(detail::cat( - "cannot convert double to ", - boost::core::demangled_name(BOOST_CORE_TYPEID(args)), " for ", - boost::core::demangled_name(BOOST_CORE_TYPEID(decltype(a))), + "cannot convert ", + boost::core::demangled_name(BOOST_CORE_TYPEID(args_t)), + " to ", + boost::core::demangled_name(BOOST_CORE_TYPEID(expected_args_t)), " for ", + boost::core::demangled_name(BOOST_CORE_TYPEID(A)), "; use boost::histogram::axis::get to obtain a reference " "of this axis type")); }, @@ -202,9 +204,16 @@ public: *this); } - // this only works for axes with compatible bin type - // and will throw a runtime_error otherwise - bin_type operator[](const int idx) const { return bin_type(idx, *this); } + decltype(auto) operator[](const int idx) const { + const bool is_continuous = visit( + [](const auto& a) { + using A = detail::rm_cvref; + using T = detail::arg_type; + return !std::is_integral::value; + }, + *this); + return polymorphic_bin_view(idx, *this, is_continuous); + } bool operator==(const variant& rhs) const { return base_type::operator==(static_cast(rhs)); diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 3a78e957..efec949c 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -133,9 +133,12 @@ struct is_axis_variant_impl> : std::true_type {}; template using is_axis_variant = typename is_axis_variant_impl::type; +template +using is_axis_or_axis_variant = mp11::mp_or, is_axis_variant>; + template using is_axis_vector = mp11::mp_all, - is_axis>>>; + is_axis_or_axis_variant>>>; struct static_container_tag {}; struct iterable_container_tag {}; diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index f9988790..eee3070c 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -43,7 +43,7 @@ template class variable; -template +template class integer; template , diff --git a/test/Jamfile b/test/Jamfile index 0600402a..9b105292 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -15,13 +15,18 @@ project histogram-test alias run-tests : [ run adaptive_storage_serialization_test.cpp /boost/serialization//boost_serialization/static ] - [ run histogram_serialization_test.cpp /boost/serialization//boost_serialization/static ] [ run adaptive_storage_test.cpp ] [ run array_storage_test.cpp ] - [ run axis_test.cpp ] + [ run axis_regular_test.cpp ] + [ run axis_circular_test.cpp ] + [ run axis_variable_test.cpp ] + [ run axis_integer_test.cpp ] + [ run axis_category_test.cpp ] + [ run axis_variant_test.cpp ] [ run detail_test.cpp ] [ run histogram_dynamic_test.cpp ] [ run histogram_mixed_test.cpp ] + [ run histogram_serialization_test.cpp /boost/serialization//boost_serialization/static ] [ run histogram_test.cpp ] [ run index_mapper_test.cpp ] [ run meta_test.cpp ] diff --git a/test/axis_category_test.cpp b/test/axis_category_test.cpp new file mode 100644 index 00000000..6a3c15b5 --- /dev/null +++ b/test/axis_category_test.cpp @@ -0,0 +1,65 @@ +// 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) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctors + { + auto empty = std::vector(0); + BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument); + } + + // axis::category + { + std::string A("A"), B("B"), C("C"), other; + axis::category a({A, B, C}); + axis::category b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = axis::category{{B, A, C}}; + BOOST_TEST_NE(a, b); + b = a; + b = b; + BOOST_TEST_EQ(a, b); + axis::category c = std::move(b); + BOOST_TEST_EQ(c, a); + BOOST_TEST_NE(b, a); + axis::category d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(a(A), 0); + BOOST_TEST_EQ(a(B), 1); + BOOST_TEST_EQ(a(C), 2); + BOOST_TEST_EQ(a(other), 3); + BOOST_TEST_EQ(a.value(0), A); + BOOST_TEST_EQ(a.value(1), B); + BOOST_TEST_EQ(a.value(2), C); + BOOST_TEST_THROWS(a.value(3), std::out_of_range); + } + + // iterators + { + test_axis_iterator(axis::category<>({3, 1, 2}, ""), 0, 3); + test_axis_iterator(axis::category({"A", "B"}, ""), 0, 2); + } + + return boost::report_errors(); +} diff --git a/test/axis_circular_test.cpp b/test/axis_circular_test.cpp new file mode 100644 index 00000000..97a39086 --- /dev/null +++ b/test/axis_circular_test.cpp @@ -0,0 +1,56 @@ +// 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) + +#include +#include +#include +#include +#include +#include "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctor + { + BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); + BOOST_TEST_THROWS(axis::circular<>(2, 0, -1), std::invalid_argument); + } + + // axis::circular + { + axis::circular<> a{4, 0, 1}; + BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1); + axis::circular<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = b; + BOOST_TEST_EQ(a, b); + axis::circular<> c = std::move(b); + BOOST_TEST_EQ(c, a); + axis::circular<> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + BOOST_TEST_EQ(a(-1.0 * 3), 0); + BOOST_TEST_EQ(a(0.0), 0); + BOOST_TEST_EQ(a(0.25), 1); + BOOST_TEST_EQ(a(0.5), 2); + BOOST_TEST_EQ(a(0.75), 3); + BOOST_TEST_EQ(a(1.0), 0); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); + } + + // iterators + { + test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); + } + + return boost::report_errors(); +} diff --git a/test/axis_integer_test.cpp b/test/axis_integer_test.cpp new file mode 100644 index 00000000..b45f73d7 --- /dev/null +++ b/test/axis_integer_test.cpp @@ -0,0 +1,79 @@ +// 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) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctor + { + BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument); + BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); + } + + // axis::integer + { + axis::integer<> a{-1, 2}; + BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); + axis::integer<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = b; + BOOST_TEST_EQ(a, b); + axis::integer<> c = std::move(b); + BOOST_TEST_EQ(c, a); + axis::integer<> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-2), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(2), 3); + BOOST_TEST_EQ(a(10), 3); + } + + // axis::integer with int type + { + axis::integer a{-1, 2}; + BOOST_TEST_EQ(a[-2].value(), std::numeric_limits::min()); + BOOST_TEST_EQ(a[4].value(), std::numeric_limits::max()); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-2), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(2), 3); + BOOST_TEST_EQ(a(10), 3); + } + + // iterators + { + test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); + } + + return boost::report_errors(); +} diff --git a/test/axis_test.cpp b/test/axis_test.cpp deleted file mode 100644 index 53550c99..00000000 --- a/test/axis_test.cpp +++ /dev/null @@ -1,166 +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) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "utility.hpp" - -using namespace boost::histogram; - -int main() { - // bad_ctors - { - auto empty = std::vector(0); - BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); - BOOST_TEST_THROWS((axis::variable<>(empty)), std::invalid_argument); - BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); - BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); - BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument); - } - - // axis::circular - { - axis::circular<> a{4, 0, 1}; - BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1); - axis::circular<> b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = b; - BOOST_TEST_EQ(a, b); - axis::circular<> c = std::move(b); - BOOST_TEST_EQ(c, a); - axis::circular<> d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a(-1.0 * 3), 0); - BOOST_TEST_EQ(a(0.0), 0); - BOOST_TEST_EQ(a(0.25), 1); - BOOST_TEST_EQ(a(0.5), 2); - BOOST_TEST_EQ(a(0.75), 3); - BOOST_TEST_EQ(a(1.0), 0); - BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); - BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), 4); - BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); - } - - // axis::variable - { - axis::variable<> a{-1, 0, 1}; - BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); - BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); - axis::variable<> b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = b; - BOOST_TEST_EQ(a, b); - axis::variable<> c = std::move(b); - BOOST_TEST_EQ(c, a); - BOOST_TEST_NE(b, a); - axis::variable<> d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - axis::variable<> e{-2, 0, 2}; - BOOST_TEST_NE(a, e); - BOOST_TEST_EQ(a(-10), -1); - BOOST_TEST_EQ(a(-1), 0); - BOOST_TEST_EQ(a(0), 1); - BOOST_TEST_EQ(a(1), 2); - BOOST_TEST_EQ(a(10), 2); - BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); - BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 2); - BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 2); - } - - // axis::integer - { - axis::integer<> a{-1, 2}; - BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits::min()); - BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::max()); - axis::integer<> b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = b; - BOOST_TEST_EQ(a, b); - axis::integer<> c = std::move(b); - BOOST_TEST_EQ(c, a); - axis::integer<> d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a(-10), -1); - BOOST_TEST_EQ(a(-2), -1); - BOOST_TEST_EQ(a(-1), 0); - BOOST_TEST_EQ(a(0), 1); - BOOST_TEST_EQ(a(1), 2); - BOOST_TEST_EQ(a(2), 3); - BOOST_TEST_EQ(a(10), 3); - } - - // axis::category - { - std::string A("A"), B("B"), C("C"), other; - axis::category a({A, B, C}); - axis::category b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = axis::category{{B, A, C}}; - BOOST_TEST_NE(a, b); - b = a; - b = b; - BOOST_TEST_EQ(a, b); - axis::category c = std::move(b); - BOOST_TEST_EQ(c, a); - BOOST_TEST_NE(b, a); - axis::category d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a.size(), 3); - BOOST_TEST_EQ(a(A), 0); - BOOST_TEST_EQ(a(B), 1); - BOOST_TEST_EQ(a(C), 2); - BOOST_TEST_EQ(a(other), 3); - BOOST_TEST_EQ(a.value(0), A); - BOOST_TEST_EQ(a.value(1), B); - BOOST_TEST_EQ(a.value(2), C); - BOOST_TEST_THROWS(a.value(3), std::out_of_range); - } - - // iterators - { - enum { A, B, C }; - test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); - test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); - test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); - test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3); - test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); - // BOOST_TEST_THROWS(axis::variant>(axis::category<>({A, B, - // C}))[0].lower(), - // std::runtime_error); - } - - return boost::report_errors(); -} diff --git a/test/axis_variable_test.cpp b/test/axis_variable_test.cpp new file mode 100644 index 00000000..719ef1f6 --- /dev/null +++ b/test/axis_variable_test.cpp @@ -0,0 +1,60 @@ +// 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) + +#include +#include +#include +#include +#include +#include "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctors + { + auto empty = std::vector(0); + BOOST_TEST_THROWS((axis::variable<>(empty)), std::invalid_argument); + BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); + } + + // axis::variable + { + axis::variable<> a{-1, 0, 1}; + BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); + axis::variable<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = b; + BOOST_TEST_EQ(a, b); + axis::variable<> c = std::move(b); + BOOST_TEST_EQ(c, a); + BOOST_TEST_NE(b, a); + axis::variable<> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + axis::variable<> e{-2, 0, 2}; + BOOST_TEST_NE(a, e); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(10), 2); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 2); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 2); + } + + // iterators + { + test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); + } + + return boost::report_errors(); +} diff --git a/test/axis_variant_test.cpp b/test/axis_variant_test.cpp index 72459dcd..701484ff 100644 --- a/test/axis_variant_test.cpp +++ b/test/axis_variant_test.cpp @@ -17,11 +17,57 @@ #include #include #include +#include #include "utility.hpp" using namespace boost::histogram; int main() { + { + BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument); + } + + { + axis::variant< + axis::integer<>, + axis::category + > a{axis::integer<>(0, 2, "int")}; + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-1), -1); + BOOST_TEST_EQ(a(0), 0); + BOOST_TEST_EQ(a(0.5), 0); + BOOST_TEST_EQ(a(1), 1); + BOOST_TEST_EQ(a(2), 2); + BOOST_TEST_EQ(a(10), 2); + BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); + BOOST_TEST_EQ(a.metadata(), std::string("int")); + BOOST_TEST_EQ(a.options(), axis::option_type::underflow_and_overflow); + + a = axis::category({"A", "B"}, "cat"); + BOOST_TEST_EQ(a("A"), 0); + BOOST_TEST_EQ(a("B"), 1); + // BOOST_TEST_EQ(a.metadata(), std::string("cat")); + // BOOST_TEST_EQ(a.options(), axis::option_type::overflow); + } + + // axis::variant support for minimal_axis + { + struct minimal_axis { + int operator()(double) const { return 0; } + unsigned size() const { return 1; } + }; + + axis::variant axis; + BOOST_TEST_EQ(axis(0), 0); + BOOST_TEST_EQ(axis(10), 0); + BOOST_TEST_EQ(axis.size(), 1); + BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); + BOOST_TEST_THROWS(axis.value(0), std::runtime_error); + BOOST_TEST_TRAIT_TRUE( + (std::is_same)); + } + // axis::variant copyable { axis::variant> a1(axis::regular<>(2, -1, 1)); @@ -89,23 +135,6 @@ int main() { #endif } - // axis::variant support for minimal_axis - { - struct minimal_axis { - int operator()(double) const { return 0; } - unsigned size() const { return 1; } - }; - - axis::variant axis; - BOOST_TEST_EQ(axis(0), 0); - BOOST_TEST_EQ(axis(10), 0); - BOOST_TEST_EQ(axis.size(), 1); - BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); - BOOST_TEST_THROWS(axis.value(0), std::runtime_error); - BOOST_TEST_TRAIT_TRUE( - (std::is_same)); - } - // bin_type streamable { auto test = [](const auto& x, const char* ref) { @@ -140,12 +169,16 @@ int main() { BOOST_TEST(axes == std::vector(axes)); } - // axis::variant value_to_index_failure + // axis::variant with unusual args { axis::variant> x = axis::category({"A", "B"}, "category"); - auto cx = axis::get>(x); - BOOST_TEST_EQ(cx("B"), 1); + BOOST_TEST_EQ(x("B"), 1); + } + + { + auto a = axis::variant>(axis::category<>({2, 1, 3})); + BOOST_TEST_THROWS(a[0].lower(), std::runtime_error); } // vector of axes with custom allocators @@ -197,5 +230,10 @@ int main() { #endif } + // iterators + { + test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); + } + return boost::report_errors(); } From 49f26d075e8c3c121bb2684a7c5e693ebc9469b3 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 17:43:26 +0200 Subject: [PATCH 40/44] more testing --- test/axis_variant_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/axis_variant_test.cpp b/test/axis_variant_test.cpp index 701484ff..043f2758 100644 --- a/test/axis_variant_test.cpp +++ b/test/axis_variant_test.cpp @@ -47,8 +47,8 @@ int main() { a = axis::category({"A", "B"}, "cat"); BOOST_TEST_EQ(a("A"), 0); BOOST_TEST_EQ(a("B"), 1); - // BOOST_TEST_EQ(a.metadata(), std::string("cat")); - // BOOST_TEST_EQ(a.options(), axis::option_type::overflow); + BOOST_TEST_EQ(a.metadata(), std::string("cat")); + BOOST_TEST_EQ(a.options(), axis::option_type::overflow); } // axis::variant support for minimal_axis From 9a6c7755ab10a500861b6f59ceedcb82468bd0dc Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 17:51:27 +0200 Subject: [PATCH 41/44] fix win --- .appveyor.yml | 1 + .travis.yml | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index b283f677..363bac66 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -52,6 +52,7 @@ install: - git submodule init libs/type_index - git submodule init libs/type_traits - git submodule init libs/utility + - git submodule init libs/units - git submodule init libs/variant - git submodule update - bootstrap diff --git a/.travis.yml b/.travis.yml index 0aa396de..f59c0e67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - g++-5 + - libstd++6 matrix: include: @@ -34,6 +34,12 @@ matrix: - os: osx env: SERIAL=OFF - os: linux # coverage build + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 env: - MATRIX_EVAL="GCOV=gcov-5 && CC=gcc-5 && CXX=g++-5" From 7e9c94a2b9b4da284495913728f41641a162d212 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 18:59:58 +0200 Subject: [PATCH 42/44] fix --- .appveyor.yml | 5 +++-- .travis.yml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 363bac66..1ac073c4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,7 +17,6 @@ install: - cd boost - mkdir libs\histogram - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram - - git submodule init tools/build - git submodule init libs/array - git submodule init libs/assert - git submodule init libs/bind @@ -51,9 +50,11 @@ install: - git submodule init libs/throw_exception - git submodule init libs/type_index - git submodule init libs/type_traits - - git submodule init libs/utility + - git submodule init libs/typeof - git submodule init libs/units + - git submodule init libs/utility - git submodule init libs/variant + - git submodule init tools/build - git submodule update - bootstrap - b2 --with-serialization diff --git a/.travis.yml b/.travis.yml index f59c0e67..da8cba5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - libstd++6 + - libstdc++6 matrix: include: From e84683ad1bd30573b7f234ccf681ee0dbf4a75e9 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 19:24:37 +0200 Subject: [PATCH 43/44] fix --- .travis.yml | 2 +- include/boost/histogram/axis/variant.hpp | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index da8cba5b..f9fd31fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - libstdc++6 + - libstdc++5-dev libgcc-5-dev libstdc++6 matrix: include: diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 9a70274e..16687c6f 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -27,6 +27,14 @@ namespace boost { namespace histogram { namespace detail { +struct is_continuous : public boost::static_visitor { + template + bool operator()(const A&) const { + using T = detail::arg_type; + return !std::is_integral::value; + } +}; + template struct functor_wrapper : public boost::static_visitor { F& fcn; @@ -205,13 +213,9 @@ public: } decltype(auto) operator[](const int idx) const { - const bool is_continuous = visit( - [](const auto& a) { - using A = detail::rm_cvref; - using T = detail::arg_type; - return !std::is_integral::value; - }, - *this); + // using visit here causes internal error in MSVC 2017 + const bool is_continuous = boost::apply_visitor(detail::is_continuous(), + static_cast(*this)); return polymorphic_bin_view(idx, *this, is_continuous); } From feeec8423e1d74bc5aa539c9e2a9b476102441f6 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 19:31:42 +0200 Subject: [PATCH 44/44] fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f9fd31fb..94fc9323 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - libstdc++5-dev libgcc-5-dev libstdc++6 + - libstdc++-5-dev libgcc-5-dev libstdc++6 libasan2 libtsan0 libubsan0 matrix: include: