cpp17: set axis compile-time options via deduction guide (#340)

This commit is contained in:
Hans Dembinski 2021-09-30 13:49:22 +02:00 committed by Hans Dembinski
parent 8f3a3c07e4
commit b482598597
6 changed files with 180 additions and 83 deletions

View File

@ -40,7 +40,7 @@ class boolean : public iterator_mixin<boolean<MetaData>>,
public:
/** Construct a boolean axis.
*
* \param meta description of the axis.
* @param meta description of the axis.
*/
explicit boolean(metadata_type meta = {}) : metadata_base(std::move(meta)) {}

View File

@ -67,14 +67,17 @@ public:
/** Construct from iterator range of unique values.
*
* \param begin begin of category range of unique values.
* \param end end of category range of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
* @param begin begin of category range of unique values.
* @param end end of category range of unique values.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional).
*/
template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
category(It begin, It end, metadata_type meta = {}, options_type options = {},
allocator_type alloc = {})
: metadata_base(std::move(meta)), vec_(alloc) {
(void)options;
if (std::distance(begin, end) < 0)
BOOST_THROW_EXCEPTION(
std::invalid_argument("end must be reachable by incrementing begin"));
@ -82,32 +85,51 @@ public:
while (begin != end) vec_.emplace_back(*begin++);
}
// kept for backward compatibility
template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta, allocator_type alloc)
: category(begin, end, std::move(meta), {}, std::move(alloc)) {}
/** Construct axis from iterable sequence of unique values.
*
* \param iterable sequence of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
* @param iterable sequence of unique values.
* @param meta description of the axis.
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use.
*/
template <class C, class = detail::requires_iterable<C>>
category(const C& iterable, metadata_type meta = {}, allocator_type alloc = {})
: category(std::begin(iterable), std::end(iterable), std::move(meta),
category(const C& iterable, metadata_type meta = {}, options_type options = {},
allocator_type alloc = {})
: category(std::begin(iterable), std::end(iterable), std::move(meta), options,
std::move(alloc)) {}
// kept for backward compatibility
template <class C, class = detail::requires_iterable<C>>
category(const C& iterable, metadata_type meta, allocator_type alloc)
: category(std::begin(iterable), std::end(iterable), std::move(meta), {},
std::move(alloc)) {}
/** Construct axis from an initializer list of unique values.
*
* \param list `std::initializer_list` of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
* @param list `std::initializer_list` of unique values.
* @param meta description of the axis.
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use.
*/
template <class U>
category(std::initializer_list<U> list, metadata_type meta = {},
allocator_type alloc = {})
: category(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
options_type options = {}, allocator_type alloc = {})
: category(list.begin(), list.end(), std::move(meta), options, std::move(alloc)) {}
// kept for backward compatibility
template <class U>
category(std::initializer_list<U> list, metadata_type meta, allocator_type alloc)
: category(list.begin(), list.end(), std::move(meta), {}, std::move(alloc)) {}
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
category(const category& src, index_type begin, index_type end, unsigned merge)
// LCOV_EXCL_START: gcc-8 is missing the delegated ctor for no reason
: category(src.vec_.begin() + begin, src.vec_.begin() + end, src.metadata(),
: category(src.vec_.begin() + begin, src.vec_.begin() + end, src.metadata(), {},
src.get_allocator())
// LCOV_EXCL_STOP
{
@ -189,12 +211,17 @@ private:
template <class T>
category(std::initializer_list<T>)
->category<detail::replace_cstring<std::decay_t<T>>, null_type>;
-> category<detail::replace_cstring<std::decay_t<T>>, null_type>;
template <class T, class M>
category(std::initializer_list<T>, M)
->category<detail::replace_cstring<std::decay_t<T>>,
detail::replace_cstring<std::decay_t<M>>>;
-> category<detail::replace_cstring<std::decay_t<T>>,
detail::replace_cstring<std::decay_t<M>>>;
template <class T, class M, unsigned B>
category(std::initializer_list<T>, M, const option::bitset<B>&)
-> category<detail::replace_cstring<std::decay_t<T>>,
detail::replace_cstring<std::decay_t<M>>, option::bitset<B>>;
#endif

View File

@ -34,9 +34,9 @@ namespace axis {
Binning is a O(1) operation. This axis bins 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 (all values allowed).
@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>
class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
@ -73,14 +73,17 @@ public:
/** Construct over semi-open integer interval [start, stop).
*
* \param start first integer of covered range.
* \param stop one past last integer of covered range.
* \param meta description of the axis.
* @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).
*/
integer(value_type start, value_type stop, metadata_type meta = {})
integer(value_type start, value_type stop, metadata_type meta = {},
options_type options = {})
: metadata_base(std::move(meta))
, size_(static_cast<index_type>(stop - start))
, min_(start) {
(void)options;
if (!(stop >= start))
BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required"));
}
@ -217,6 +220,12 @@ integer(T, T, M)
-> integer<detail::convert_integer<T, index_type>,
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
template <class T, class M, unsigned B>
integer(T, T, M, const option::bitset<B>&)
-> integer<detail::convert_integer<T, index_type>,
detail::replace_type<std::decay_t<M>, const char*, std::string>,
option::bitset<B>>;
#endif
} // namespace axis

View File

@ -173,7 +173,7 @@ step_type<T> step(T t) {
@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 (all values allowed).
@tparam Options see boost::histogram::axis::option.
*/
template <class Value, class Transform, class MetaData, class Options>
class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
@ -213,14 +213,16 @@ public:
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
*/
regular(transform_type trans, unsigned n, value_type start, value_type stop,
metadata_type meta = {})
metadata_type meta = {}, options_type options = {})
: transform_type(std::move(trans))
, metadata_base(std::move(meta))
, size_(static_cast<index_type>(n))
, min_(this->forward(detail::get_scale(start)))
, delta_(this->forward(detail::get_scale(stop)) - min_) {
(void)options;
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
if (!std::isfinite(min_) || !std::isfinite(delta_))
BOOST_THROW_EXCEPTION(
@ -235,18 +237,21 @@ public:
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
*/
regular(unsigned n, value_type start, value_type stop, metadata_type meta = {})
: regular({}, n, start, stop, std::move(meta)) {}
regular(unsigned n, value_type start, value_type stop, metadata_type meta = {},
options_type options = {})
: regular({}, n, start, stop, std::move(meta), options) {}
/** Construct bins with the given step size over real transformed range
* [start, stop).
*
* @param trans transform instance to use.
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
* @param trans transform instance to use.
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
@ -254,27 +259,29 @@ public:
*/
template <class T>
regular(transform_type trans, step_type<T> step, value_type start, value_type stop,
metadata_type meta = {})
metadata_type meta = {}, options_type options = {})
: regular(trans, static_cast<index_type>(std::abs(stop - start) / step.value),
start,
start + static_cast<index_type>(std::abs(stop - start) / step.value) *
step.value,
std::move(meta)) {}
std::move(meta), options) {}
/** Construct bins with the given step size over real range [start, stop).
*
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
* (start + n * step).
*/
template <class T>
regular(step_type<T> step, value_type start, value_type stop, metadata_type meta = {})
: regular({}, step, start, stop, std::move(meta)) {}
regular(step_type<T> step, value_type start, value_type stop, metadata_type meta = {},
options_type options = {})
: regular({}, step, start, stop, std::move(meta), options) {}
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
regular(const regular& src, index_type begin, index_type end, unsigned merge)
@ -397,20 +404,28 @@ private:
template <class T>
regular(unsigned, T, T)
->regular<detail::convert_integer<T, double>, transform::id, null_type>;
-> regular<detail::convert_integer<T, double>, transform::id, null_type>;
template <class T, class M>
regular(unsigned, T, T, M)
->regular<detail::convert_integer<T, double>, transform::id,
detail::replace_cstring<std::decay_t<M>>>;
regular(unsigned, T, T, M) -> regular<detail::convert_integer<T, double>, transform::id,
detail::replace_cstring<std::decay_t<M>>>;
template <class T, class M, unsigned B>
regular(unsigned, T, T, M, const option::bitset<B>&)
-> regular<detail::convert_integer<T, double>, transform::id,
detail::replace_cstring<std::decay_t<M>>, option::bitset<B>>;
template <class Tr, class T, class = detail::requires_transform<Tr, T>>
regular(Tr, unsigned, T, T)->regular<detail::convert_integer<T, double>, Tr, null_type>;
regular(Tr, unsigned, T, T) -> regular<detail::convert_integer<T, double>, Tr, null_type>;
template <class Tr, class T, class M>
regular(Tr, unsigned, T, T, M)
->regular<detail::convert_integer<T, double>, Tr,
detail::replace_cstring<std::decay_t<M>>>;
regular(Tr, unsigned, T, T, M) -> regular<detail::convert_integer<T, double>, Tr,
detail::replace_cstring<std::decay_t<M>>>;
template <class Tr, class T, class M, unsigned B>
regular(Tr, unsigned, T, T, M, const option::bitset<B>&)
-> regular<detail::convert_integer<T, double>, Tr,
detail::replace_cstring<std::decay_t<M>>, option::bitset<B>>;
#endif

View File

@ -40,9 +40,9 @@ namespace axis {
Binning is a O(log(N)) operation. If speed matters and the problem domain
allows it, prefer a regular axis, possibly with a transform.
@tparam Value input value type, must be floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option (all values allowed).
@tparam Value input value type, must be floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management.
*/
template <class Value, class MetaData, class Options, class Allocator>
@ -73,14 +73,18 @@ public:
/** Construct from iterator range of bin edges.
*
* \param begin begin of edge sequence.
* \param end end of edge sequence.
* \param meta description of the axis.
* \param alloc allocator instance to use.
* @param begin begin of edge sequence.
* @param end end of edge sequence.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional).
*/
template <class It, class = detail::requires_iterator<It>>
variable(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
variable(It begin, It end, metadata_type meta = {}, options_type options = {},
allocator_type alloc = {})
: metadata_base(std::move(meta)), vec_(std::move(alloc)) {
(void)options;
if (std::distance(begin, end) < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
@ -96,27 +100,46 @@ public:
std::invalid_argument("input sequence must be strictly ascending"));
}
// kept for backward compatibility
template <class It, class = detail::requires_iterator<It>>
variable(It begin, It end, metadata_type meta, allocator_type alloc)
: variable(begin, end, std::move(meta), {}, std::move(alloc)) {}
/** Construct variable axis from iterable range of bin edges.
*
* \param iterable iterable range of bin edges.
* \param meta description of the axis.
* \param alloc allocator instance to use.
* @param iterable iterable range of bin edges.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional).
*/
template <class U, class = detail::requires_iterable<U>>
variable(const U& iterable, metadata_type meta = {}, allocator_type alloc = {})
: variable(std::begin(iterable), std::end(iterable), std::move(meta),
variable(const U& iterable, metadata_type meta = {}, options_type options = {},
allocator_type alloc = {})
: variable(std::begin(iterable), std::end(iterable), std::move(meta), options,
std::move(alloc)) {}
// kept for backward compatibility
template <class U, class = detail::requires_iterable<U>>
variable(const U& iterable, metadata_type meta, allocator_type alloc)
: variable(std::begin(iterable), std::end(iterable), std::move(meta), {},
std::move(alloc)) {}
/** Construct variable axis from initializer list of bin edges.
*
* @param list `std::initializer_list` of bin edges.
* @param meta description of the axis.
* @param alloc allocator instance to use.
* @param list `std::initializer_list` of bin edges.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional).
*/
template <class U>
variable(std::initializer_list<U> list, metadata_type meta = {},
allocator_type alloc = {})
: variable(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
options_type options = {}, allocator_type alloc = {})
: variable(list.begin(), list.end(), std::move(meta), options, std::move(alloc)) {}
// kept for backward compatibility
template <class U>
variable(std::initializer_list<U> list, metadata_type meta, allocator_type alloc)
: variable(list.begin(), list.end(), std::move(meta), {}, std::move(alloc)) {}
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
variable(const variable& src, index_type begin, index_type end, unsigned merge)
@ -221,26 +244,36 @@ private:
template <class T>
variable(std::initializer_list<T>)
->variable<detail::convert_integer<T, double>, null_type>;
-> variable<detail::convert_integer<T, double>, null_type>;
template <class T, class M>
variable(std::initializer_list<T>, M)
->variable<detail::convert_integer<T, double>,
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
-> variable<detail::convert_integer<T, double>,
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
template <class T, class M, unsigned B>
variable(std::initializer_list<T>, M, const option::bitset<B>&)
-> variable<detail::convert_integer<T, double>,
detail::replace_type<std::decay_t<M>, const char*, std::string>,
option::bitset<B>>;
template <class Iterable, class = detail::requires_iterable<Iterable>>
variable(Iterable)
->variable<
detail::convert_integer<
std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
null_type>;
variable(Iterable) -> variable<
detail::convert_integer<
std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
null_type>;
template <class Iterable, class M>
variable(Iterable, M)
->variable<
detail::convert_integer<
std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
variable(Iterable, M) -> variable<
detail::convert_integer<
std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
template <class Iterable, class M, unsigned B>
variable(Iterable, M, const option::bitset<B>&) -> variable<
detail::convert_integer<
std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
detail::replace_type<std::decay_t<M>, const char*, std::string>, option::bitset<B>>;
#endif

View File

@ -21,6 +21,7 @@
using namespace boost::histogram;
namespace tr = axis::transform;
namespace op = axis::option;
// tests requires a C++17 compatible compiler
@ -43,6 +44,10 @@ int main() {
regular<float, tr::sqrt, std::string>);
BOOST_TEST_TRAIT_SAME(decltype(regular(tr::sqrt(), 1, 0, 1, 0)),
regular<double, tr::sqrt, int>);
BOOST_TEST_TRAIT_SAME(decltype(regular(1, 0, 1, "x", op::growth)),
regular<double, tr::id, std::string, op::growth_t>);
BOOST_TEST_TRAIT_SAME(decltype(regular(tr::sqrt(), 1, 0, 1, "x", op::growth)),
regular<double, tr::sqrt, std::string, op::growth_t>);
}
{
@ -53,6 +58,8 @@ int main() {
BOOST_TEST_TRAIT_SAME(decltype(integer(1.0f, 2.0f)), integer<float, null_type>);
BOOST_TEST_TRAIT_SAME(decltype(integer(1, 2, "foo")), integer<int, std::string>);
BOOST_TEST_TRAIT_SAME(decltype(integer(1, 2, 0)), integer<int, int>);
BOOST_TEST_TRAIT_SAME(decltype(integer(1, 2, "foo", op::growth)),
integer<int, std::string, op::growth_t>);
}
{
@ -63,6 +70,8 @@ int main() {
BOOST_TEST_TRAIT_SAME(decltype(variable({-1, 0, 1}, "foo")),
variable<double, std::string>);
BOOST_TEST_TRAIT_SAME(decltype(variable({-1, 1}, 0)), variable<double, int>);
BOOST_TEST_TRAIT_SAME(decltype(variable({-1, 1}, "foo", op::underflow)),
variable<double, std::string, op::underflow_t>);
BOOST_TEST_TRAIT_SAME(decltype(variable(std::vector<int>{{-1, 1}})),
variable<double, null_type>);
@ -72,6 +81,8 @@ int main() {
variable<double, std::string>);
BOOST_TEST_TRAIT_SAME(decltype(variable(std::vector<int>{{-1, 1}}, 0)),
variable<double, int>);
BOOST_TEST_TRAIT_SAME(decltype(variable({-1, 1}, "foo", op::growth)),
variable<double, std::string, op::growth_t>);
}
{
@ -80,6 +91,8 @@ int main() {
BOOST_TEST_TRAIT_SAME(decltype(category{"x", "y"}), category<std::string, null_type>);
BOOST_TEST_TRAIT_SAME(decltype(category({1, 2}, "foo")), category<int, std::string>);
BOOST_TEST_TRAIT_SAME(decltype(category({1, 2}, 0)), category<int, int>);
BOOST_TEST_TRAIT_SAME(decltype(category({1, 2}, "foo", op::growth)),
category<int, std::string, op::growth_t>);
}
{