improve docstrings for axes, explain move issue (#382)

This commit is contained in:
Hans Dembinski 2022-12-26 14:41:18 +01:00 committed by GitHub
parent 652c7f8ebb
commit 04946089f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 79 deletions

View File

@ -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` * `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`] * `i` and `j` are indices of type [headerref boost/histogram/fwd.hpp `boost::histogram::axis::index_type`]
* `n` is a value of type `unsigned` * `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 * `ar` is a value of an archive with Boost.Serialization semantics
[table Valid expressions [table Valid expressions

View File

@ -39,10 +39,14 @@ class boolean : public iterator_mixin<boolean<MetaData>>,
public: public:
/** Construct a boolean axis. /** 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<metadata_type>::value)
: metadata_base(std::move(meta)) {}
/// Return index for value argument. /// Return index for value argument.
index_type index(value_type x) const noexcept { return static_cast<index_type>(x); } index_type index(value_type x) const noexcept { return static_cast<index_type>(x); }

View File

@ -28,18 +28,18 @@ namespace axis {
/** Maps at a set of unique values to bin indices. /** 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 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 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 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. factor. For small N (the typical use case) it beats other kinds of lookup.
@tparam Value input value type, must be equal-comparable. @tparam Value input value type, must be equal-comparable.
@tparam MetaData type to store meta data. @tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option. @tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management. @tparam Allocator allocator to use for dynamic memory management.
The options `underflow` and `circular` are not allowed. The options `growth` The options `underflow` and `circular` are not allowed. The options `growth`
and `overflow` are mutually exclusive. and `overflow` are mutually exclusive.
*/ */
template <class Value, class MetaData, class Options, class Allocator> template <class Value, class MetaData, class Options, class Allocator>
class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>, class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>,
@ -56,13 +56,20 @@ public:
constexpr category() = default; constexpr category() = default;
explicit category(allocator_type alloc) : vec_(alloc) {} explicit category(allocator_type alloc) : vec_(alloc) {}
/** Construct from iterator range of unique values. /** Construct from forward iterator range of unique values.
@param begin begin of category range of unique values. @param begin begin of category range of unique values.
@param end end of category range of unique values. @param end end of category range of unique values.
@param meta description of the axis (optional). @param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
@param alloc allocator instance to use (optional). @param alloc allocator instance to use (optional).
The constructor throws `std::invalid_argument` if iterator range is invalid. If the
range contains duplicated values, the behavior of the axis is undefined.
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.
*/ */
template <class It, class = detail::requires_iterator<It>> template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta = {}, options_type options = {}, category(It begin, It end, metadata_type meta = {}, options_type options = {},

View File

@ -31,11 +31,13 @@ namespace axis {
/** Axis for an interval of integer values with unit steps. /** 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. The options `growth` and `circular` are mutually exclusive.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option. @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 Value, class MetaData, class Options> template <class Value, class MetaData, class Options>
class integer : public iterator_mixin<integer<Value, MetaData, Options>>, class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
@ -47,23 +49,6 @@ class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
using options_type = using options_type =
detail::replace_default<Options, decltype(option::underflow | option::overflow)>; detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
static_assert(std::is_integral<value_type>::value ||
std::is_floating_point<value_type>::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_type>::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<std::is_integral<value_type>::value, using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
index_type, real_index_type>; index_type, real_index_type>;
@ -72,18 +57,37 @@ public:
/** Construct over semi-open integer interval [start, stop). /** Construct over semi-open integer interval [start, stop).
@param start first integer of covered range. @param start first integer of covered range.
@param stop one past last integer of covered range. @param stop one past last integer of covered range.
@param meta description of the axis (optional). @param meta description of the axis (optional).
@param options see boost::histogram::axis::option (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 = {}, integer(value_type start, value_type stop, metadata_type meta = {},
options_type options = {}) options_type options = {})
: metadata_base(std::move(meta)) : metadata_base(std::move(meta))
, size_(static_cast<index_type>(stop - start)) , size_(static_cast<index_type>(stop - start))
, min_(start) { , min_(start) {
(void)options; static_assert(
if (!(stop >= start)) std::is_integral<value_type>::value || std::is_floating_point<value_type>::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_type>::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")); BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required"));
} }

View File

@ -27,6 +27,15 @@ class metadata_base {
protected: protected:
using metadata_type = Metadata; using metadata_type = Metadata;
static_assert(std::is_default_constructible<metadata_type>::value,
"metadata must be default constructible");
static_assert(std::is_copy_constructible<metadata_type>::value,
"metadata must be copy constructible");
static_assert(std::is_copy_assignable<metadata_type>::value,
"metadata must be copy assignable");
// std::string explicitly guarantees nothrow only in C++17 // std::string explicitly guarantees nothrow only in C++17
static_assert(std::is_same<metadata_type, std::string>::value || static_assert(std::is_same<metadata_type, std::string>::value ||
std::is_nothrow_move_constructible<metadata_type>::value, std::is_nothrow_move_constructible<metadata_type>::value,
@ -45,15 +54,15 @@ protected:
return *this; return *this;
} }
private:
mutable metadata_type data_;
public: public:
/// Returns reference to metadata. /// Returns reference to metadata.
metadata_type& metadata() noexcept { return data_; } metadata_type& metadata() noexcept { return data_; }
/// Returns reference to mutable metadata from const axis. /// Returns reference to mutable metadata from const axis.
metadata_type& metadata() const noexcept { return data_; } metadata_type& metadata() const noexcept { return data_; }
private:
mutable metadata_type data_;
}; };
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED

View File

@ -167,20 +167,20 @@ step_type<T> step(T t) {
/** Axis for equidistant intervals on the real line. /** 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 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. 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 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 still counted towards the last bin. The axis range represents a closed interval.
is the desired behavior for random numbers drawn from a bounded interval, which is
usually closed.
@tparam Value input value type, must be floating point. The options `growth` and `circular` are mutually exclusive.
@tparam Transform builtin or user-defined transform type.
@tparam MetaData type to store meta data. @tparam Value input value type, must be floating point.
@tparam Options see boost::histogram::axis::option. @tparam Transform builtin or user-defined transform type.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
*/ */
template <class Value, class Transform, class MetaData, class Options> template <class Value, class Transform, class MetaData, class Options>
class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>, class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
@ -202,12 +202,19 @@ public:
/** Construct n bins over real transformed range [start, stop). /** Construct n bins over real transformed range [start, stop).
@param trans transform instance to use. @param trans transform instance to use.
@param n number of bins. @param n number of bins.
@param start low edge of first bin. @param start low edge of first bin.
@param stop high edge of last bin. @param stop high edge of last bin.
@param meta description of the axis (optional). @param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
The constructor throws `std::invalid_argument` if n is zero, or if start and stop
produce an invalid range after transformation.
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.
*/ */
regular(transform_type trans, unsigned n, value_type start, value_type stop, regular(transform_type trans, unsigned n, value_type start, value_type stop,
metadata_type meta = {}, options_type options = {}) metadata_type meta = {}, options_type options = {})
@ -223,10 +230,9 @@ public:
"transform must be no-throw move assignable"); "transform must be no-throw move assignable");
static_assert(std::is_floating_point<internal_value_type>::value, static_assert(std::is_floating_point<internal_value_type>::value,
"regular axis requires floating point type"); "regular axis requires floating point type");
static_assert((!options.test(option::circular) && !options.test(option::growth)) || static_assert(!(options.test(option::circular) && options.test(option::growth)),
(options.test(option::circular) ^ options.test(option::growth)),
"circular and growth options are mutually exclusive"); "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_)) if (!std::isfinite(min_) || !std::isfinite(delta_))
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
std::invalid_argument("forward transform of start or stop invalid")); std::invalid_argument("forward transform of start or stop invalid"));

View File

@ -68,13 +68,20 @@ public:
constexpr variable() = default; constexpr variable() = default;
explicit variable(allocator_type alloc) : vec_(alloc) {} explicit variable(allocator_type alloc) : vec_(alloc) {}
/** Construct from iterator range of bin edges. /** Construct from forward iterator range of bin edges.
@param begin begin of edge sequence. @param begin begin of edge sequence.
@param end end of edge sequence. @param end end of edge sequence.
@param meta description of the axis (optional). @param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
@param alloc allocator instance to use (optional). @param alloc allocator instance to use (optional).
The constructor throws `std::invalid_argument` if iterator range is invalid, if less
than two edges are provided or if bin edges are not in ascending order.
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.
*/ */
template <class It, class = detail::requires_iterator<It>> template <class It, class = detail::requires_iterator<It>>
variable(It begin, It end, metadata_type meta = {}, options_type options = {}, variable(It begin, It end, metadata_type meta = {}, options_type options = {},
@ -89,16 +96,21 @@ public:
(options.test(option::circular) ^ options.test(option::growth)), (options.test(option::circular) ^ options.test(option::growth)),
"circular and growth options are mutually exclusive"); "circular and growth options are mutually exclusive");
if (std::distance(begin, end) < 2) const auto n = std::distance(begin, end);
BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required")); 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++); vec_.emplace_back(*begin++);
bool strictly_ascending = true; bool strictly_ascending = true;
for (; begin != end; ++begin) { for (; begin != end; ++begin) {
strictly_ascending &= vec_.back() < *begin; strictly_ascending &= vec_.back() < *begin;
vec_.emplace_back(*begin); vec_.emplace_back(*begin);
} }
if (!strictly_ascending) if (!strictly_ascending)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
std::invalid_argument("input sequence must be strictly ascending")); std::invalid_argument("input sequence must be strictly ascending"));