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<