diff --git a/doc/concepts/Axis.qbk b/doc/concepts/Axis.qbk index 44fc7ca9..9fc866df 100644 --- a/doc/concepts/Axis.qbk +++ b/doc/concepts/Axis.qbk @@ -51,7 +51,7 @@ An [*Axis] maps input values to indices. It holds state specific to that axis, l * `a` and `b` are values of type `A` * `i` and `j` are indices of type [headerref boost/histogram/fwd.hpp `boost::histogram::axis::index_type`] * `n` is a value of type `unsigned` -* `M` is a metadata type that is [@https://en.cppreference.com/w/cpp/named_req/CopyConstructible CopyConstructible] and [@https://en.cppreference.com/w/cpp/named_req/CopyAssignable CopyAssignable] and *nothrow* [@https://en.cppreference.com/w/cpp/named_req/MoveAssignable MoveAssignable]. +* `M` is a metadata type that is [@https://en.cppreference.com/w/cpp/named_req/DefaultConstructible DefaultConstructible], [@https://en.cppreference.com/w/cpp/named_req/CopyConstructible CopyConstructible] and [@https://en.cppreference.com/w/cpp/named_req/CopyAssignable CopyAssignable]. It it supports moves, it must be *nothrow* [@https://en.cppreference.com/w/cpp/named_req/MoveAssignable MoveAssignable]. * `ar` is a value of an archive with Boost.Serialization semantics [table Valid expressions diff --git a/include/boost/histogram/axis/boolean.hpp b/include/boost/histogram/axis/boolean.hpp index 0549b70b..5fd0879a 100644 --- a/include/boost/histogram/axis/boolean.hpp +++ b/include/boost/histogram/axis/boolean.hpp @@ -39,10 +39,14 @@ class boolean : public iterator_mixin>, public: /** Construct a boolean axis. - * - * @param meta description of the axis. + + @param meta description of the axis. + + The constructor is nothrow if meta is nothrow move constructible. */ - explicit boolean(metadata_type meta = {}) : metadata_base(std::move(meta)) {} + explicit boolean(metadata_type meta = {}) noexcept( + std::is_nothrow_move_constructible::value) + : metadata_base(std::move(meta)) {} /// Return index for value argument. index_type index(value_type x) const noexcept { return static_cast(x); } diff --git a/include/boost/histogram/axis/category.hpp b/include/boost/histogram/axis/category.hpp index 0c705afb..9802c95d 100644 --- a/include/boost/histogram/axis/category.hpp +++ b/include/boost/histogram/axis/category.hpp @@ -28,18 +28,18 @@ namespace axis { /** Maps at a set of unique values to bin indices. - The axis maps a set of values to bins, following the order of arguments in the - constructor. The optional overflow bin for this axis counts input values that - are not part of the set. Binning has O(N) complexity, but with a very small - factor. For small N (the typical use case) it beats other kinds of lookup. - - @tparam Value input value type, must be equal-comparable. - @tparam MetaData type to store meta data. - @tparam Options see boost::histogram::axis::option. - @tparam Allocator allocator to use for dynamic memory management. - - The options `underflow` and `circular` are not allowed. The options `growth` - and `overflow` are mutually exclusive. + The axis maps a set of values to bins, following the order of arguments in the + constructor. The optional overflow bin for this axis counts input values that + are not part of the set. Binning has O(N) complexity, but with a very small + factor. For small N (the typical use case) it beats other kinds of lookup. + + @tparam Value input value type, must be equal-comparable. + @tparam MetaData type to store meta data. + @tparam Options see boost::histogram::axis::option. + @tparam Allocator allocator to use for dynamic memory management. + + The options `underflow` and `circular` are not allowed. The options `growth` + and `overflow` are mutually exclusive. */ template class category : public iterator_mixin>, @@ -56,13 +56,20 @@ class category : public iterator_mixin> category(It begin, It end, metadata_type meta = {}, options_type options = {}, diff --git a/include/boost/histogram/axis/integer.hpp b/include/boost/histogram/axis/integer.hpp index d61dfdd8..a40ac5ff 100644 --- a/include/boost/histogram/axis/integer.hpp +++ b/include/boost/histogram/axis/integer.hpp @@ -31,11 +31,13 @@ namespace axis { /** Axis for an interval of integer values with unit steps. - Binning is a O(1) operation. This axis bins faster than a regular axis. + Binning is a O(1) operation. This axis bins even faster than a regular axis. - @tparam Value input value type. Must be integer or floating point. - @tparam MetaData type to store meta data. - @tparam Options see boost::histogram::axis::option. + The options `growth` and `circular` are mutually exclusive. + + @tparam Value input value type. Must be integer or floating point. + @tparam MetaData type to store meta data. + @tparam Options see boost::histogram::axis::option. */ template class integer : public iterator_mixin>, @@ -47,23 +49,6 @@ class integer : public iterator_mixin>, using options_type = detail::replace_default; - static_assert(std::is_integral::value || - std::is_floating_point::value, - "integer axis requires floating point or integral type"); - - static_assert(!options_type::test(option::circular | option::growth) || - (options_type::test(option::circular) ^ - options_type::test(option::growth)), - "circular and growth options are mutually exclusive"); - - static_assert(std::is_floating_point::value || - (!options_type::test(option::circular) && - !options_type::test(option::growth)) || - (!options_type::test(option::overflow) && - !options_type::test(option::underflow)), - "circular or growing integer axis with integral type " - "cannot have entries in underflow or overflow bins"); - using local_index_type = std::conditional_t::value, index_type, real_index_type>; @@ -72,18 +57,37 @@ class integer : public iterator_mixin>, /** 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 meta description of the axis (optional). - @param options see boost::histogram::axis::option (optional). + @param start first integer of covered range. + @param stop one past last integer of covered range. + @param meta description of the axis (optional). + @param options see boost::histogram::axis::option (optional). + + The constructor throws `std::invalid_argument` if start is not less than stop. + + The arguments meta and alloc are passed by value. If you move either of them into the + axis and the constructor throws, their values are lost. Do not move if you cannot + guarantee that the bin description is not valid. */ integer(value_type start, value_type stop, metadata_type meta = {}, options_type options = {}) : metadata_base(std::move(meta)) , size_(static_cast(stop - start)) , min_(start) { - (void)options; - if (!(stop >= start)) + static_assert( + std::is_integral::value || std::is_floating_point::value, + "integer axis requires floating point or integral type"); + + static_assert(!(options.test(option::circular) && options.test(option::growth)), + "circular and growth options are mutually exclusive"); + + static_assert( + std::is_floating_point::value || + !((options.test(option::growth) || options.test(option::circular)) && + (options.test(option::overflow) || options.test(option::underflow))), + "circular or growing integer axis with integral type " + "cannot have entries in underflow or overflow bins"); + + if (!(stop >= start)) // double negation so it works with NaN BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required")); } diff --git a/include/boost/histogram/axis/metadata_base.hpp b/include/boost/histogram/axis/metadata_base.hpp index 520d82c5..94da12d2 100644 --- a/include/boost/histogram/axis/metadata_base.hpp +++ b/include/boost/histogram/axis/metadata_base.hpp @@ -27,6 +27,15 @@ class metadata_base { protected: using metadata_type = Metadata; + static_assert(std::is_default_constructible::value, + "metadata must be default constructible"); + + static_assert(std::is_copy_constructible::value, + "metadata must be copy constructible"); + + static_assert(std::is_copy_assignable::value, + "metadata must be copy assignable"); + // std::string explicitly guarantees nothrow only in C++17 static_assert(std::is_same::value || std::is_nothrow_move_constructible::value, @@ -45,15 +54,15 @@ class metadata_base { return *this; } -private: - mutable metadata_type data_; - public: /// Returns reference to metadata. metadata_type& metadata() noexcept { return data_; } /// Returns reference to mutable metadata from const axis. metadata_type& metadata() const noexcept { return data_; } + +private: + mutable metadata_type data_; }; #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED diff --git a/include/boost/histogram/axis/regular.hpp b/include/boost/histogram/axis/regular.hpp index 2fb5dee0..a5c7c124 100644 --- a/include/boost/histogram/axis/regular.hpp +++ b/include/boost/histogram/axis/regular.hpp @@ -167,20 +167,20 @@ step_type step(T t) { /** Axis for equidistant intervals on the real line. - The most common binning strategy. Very fast. Binning is a O(1) operation. + The most common binning strategy. Very fast. Binning is a O(1) operation. - If the axis has an overflow bin (the default), a value on the upper edge of the last - bin is put in the overflow bin. The axis range represents a semi-open interval. + If the axis has an overflow bin (the default), a value on the upper edge of the last + bin is put in the overflow bin. The axis range represents a semi-open interval. - If the overflow bin is deactivated, then a value on the upper edge of the last bin is - still counted towards the last bin. The axis range represents a closed interval. This - is the desired behavior for random numbers drawn from a bounded interval, which is - usually closed. + If the overflow bin is deactivated, then a value on the upper edge of the last bin is + still counted towards the last bin. The axis range represents a closed interval. - @tparam Value input value type, must be floating point. - @tparam Transform builtin or user-defined transform type. - @tparam MetaData type to store meta data. - @tparam Options see boost::histogram::axis::option. + The options `growth` and `circular` are mutually exclusive. + + @tparam Value input value type, must be floating point. + @tparam Transform builtin or user-defined transform type. + @tparam MetaData type to store meta data. + @tparam Options see boost::histogram::axis::option. */ template class regular : public iterator_mixin>, @@ -202,12 +202,19 @@ class regular : public iterator_mixin::value, "regular axis requires floating point type"); - static_assert((!options.test(option::circular) && !options.test(option::growth)) || - (options.test(option::circular) ^ options.test(option::growth)), + static_assert(!(options.test(option::circular) && options.test(option::growth)), "circular and growth options are mutually exclusive"); - if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required")); + if (size() <= 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required")); if (!std::isfinite(min_) || !std::isfinite(delta_)) BOOST_THROW_EXCEPTION( std::invalid_argument("forward transform of start or stop invalid")); diff --git a/include/boost/histogram/axis/variable.hpp b/include/boost/histogram/axis/variable.hpp index f866e098..6be785e7 100644 --- a/include/boost/histogram/axis/variable.hpp +++ b/include/boost/histogram/axis/variable.hpp @@ -68,13 +68,20 @@ class variable : public iterator_mixin> variable(It begin, It end, metadata_type meta = {}, options_type options = {}, @@ -89,16 +96,21 @@ class variable : public iterator_mixin 0 required")); + const auto n = std::distance(begin, end); + if (n < 0) + BOOST_THROW_EXCEPTION( + std::invalid_argument("end must be reachable by incrementing begin")); - vec_.reserve(std::distance(begin, end)); + if (n < 2) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 1 required")); + + vec_.reserve(n); vec_.emplace_back(*begin++); bool strictly_ascending = true; for (; begin != end; ++begin) { strictly_ascending &= vec_.back() < *begin; vec_.emplace_back(*begin); } + if (!strictly_ascending) BOOST_THROW_EXCEPTION( std::invalid_argument("input sequence must be strictly ascending"));