Better error messages when fill arguments do not match accumulator requirements

This commit is contained in:
Hans Dembinski 2019-11-03 20:40:31 +01:00 committed by Hans Dembinski
parent 3b495bb1d0
commit b7fc900e3b
20 changed files with 535 additions and 140 deletions

View File

@ -7,7 +7,6 @@
#ifndef BOOST_HISTOGRAM_AXIS_OPTION_HPP
#define BOOST_HISTOGRAM_AXIS_OPTION_HPP
#include <boost/mp11.hpp>
#include <type_traits>
/**

View File

@ -0,0 +1,64 @@
// Copyright 2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_DETAIL_ACCUMULATOR_TRAITS_HPP
#define BOOST_HISTOGRAM_DETAIL_ACCUMULATOR_TRAITS_HPP
#include <boost/histogram/fwd.hpp>
#include <tuple>
#include <type_traits>
namespace boost {
namespace accumulators {
template <class, class, class>
struct accumulator_set;
}
namespace histogram {
namespace detail {
template <bool WeightSupport, class... Ts>
struct accumulator_traits_holder {
using wsupport = std::integral_constant<bool, WeightSupport>;
using args = std::tuple<Ts...>;
};
template <class R, class T, class U, class... Ts>
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_2(
R (T::*)(boost::histogram::weight_type<U>, Ts...));
template <class R, class T, class U, class... Ts>
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_2(
R (T::*)(boost::histogram::weight_type<U>&&, Ts...));
template <class R, class T, class U, class... Ts>
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_2(
R (T::*)(const boost::histogram::weight_type<U>&, Ts...));
template <class R, class T, class... Ts>
accumulator_traits_holder<false, Ts...> accumulator_traits_impl_2(R (T::*)(Ts...));
template <class T>
auto accumulator_traits_impl(T&)
-> decltype(std::declval<T&>() += 0, accumulator_traits_holder<true>{});
template <class T>
auto accumulator_traits_impl(T&) -> decltype(accumulator_traits_impl_2(&T::operator()));
// for boost.accumulators compatibility
template <class S, class F, class W>
accumulator_traits_holder<false, S> accumulator_traits_impl(
boost::accumulators::accumulator_set<S, F, W>&);
template <class T>
using accumulator_traits = decltype(accumulator_traits_impl(std::declval<T&>()));
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@ -0,0 +1,83 @@
// Copyright 2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_DETAIL_ARGUMENT_TRAITS_HPP
#define BOOST_HISTOGRAM_DETAIL_ARGUMENT_TRAITS_HPP
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/list.hpp>
#include <tuple>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
struct is_weight_impl : mp11::mp_false {};
template <class T>
struct is_weight_impl<weight_type<T>> : mp11::mp_true {};
template <class T>
using is_weight = is_weight_impl<T>;
template <class T>
struct is_sample_impl : mp11::mp_false {};
template <class T>
struct is_sample_impl<sample_type<T>> : mp11::mp_true {};
template <class T>
using is_sample = is_sample_impl<T>;
template <int Idx, class L>
struct sample_args_impl {
using type = mp11::mp_first<std::decay_t<mp11::mp_at_c<L, (Idx >= 0 ? Idx : 0)>>>;
};
template <class L>
struct sample_args_impl<-1, L> {
using type = std::tuple<>;
};
template <std::size_t NArgs, std::size_t Start, int WeightPos, int SamplePos,
class SampleArgs>
struct argument_traits_holder {
using nargs = mp11::mp_size_t<NArgs>;
using start = mp11::mp_size_t<Start>;
using wpos = mp11::mp_int<WeightPos>;
using spos = mp11::mp_int<SamplePos>;
using sargs = SampleArgs;
};
template <class... Ts>
struct argument_traits_impl {
using list_ = mp11::mp_list<Ts...>;
static constexpr std::size_t size_ = sizeof...(Ts);
static constexpr std::size_t weight_ = mp11::mp_find_if<list_, is_weight>::value;
static constexpr std::size_t sample_ = mp11::mp_find_if<list_, is_sample>::value;
static constexpr int spos_ = (sample_ < size_ ? static_cast<int>(sample_) : -1);
static constexpr int wpos_ = (weight_ < size_ ? static_cast<int>(weight_) : -1);
using type =
argument_traits_holder<(size_ - (weight_ < size_) - (sample_ < size_)),
(weight_ < size_ && sample_ < size_ &&
(weight_ + sample_ < 2)
? 2
: ((weight_ == 0 || sample_ == 0) ? 1 : 0)),
wpos_, spos_, typename sample_args_impl<spos_, list_>::type>;
};
template <class... Ts>
using argument_traits = typename argument_traits_impl<Ts...>::type;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@ -12,7 +12,7 @@
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/linearize.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11.hpp>
#include <boost/mp11/algorithm.hpp>
#include <tuple>
namespace boost {

View File

@ -114,24 +114,6 @@ BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(
BOOST_HISTOGRAM_DETAIL_DETECT(has_threading_support, (T::has_threading_support));
template <class T>
struct is_weight_impl : mp11::mp_false {};
template <class T>
struct is_weight_impl<weight_type<T>> : mp11::mp_true {};
template <class T>
using is_weight = is_weight_impl<T>;
template <class T>
struct is_sample_impl : mp11::mp_false {};
template <class T>
struct is_sample_impl<sample_type<T>> : mp11::mp_true {};
template <class T>
using is_sample = is_sample_impl<T>;
template <class T>
using is_storage = mp11::mp_and<is_indexable_container<T>, has_method_reset<T>,
has_threading_support<T>>;

View File

@ -12,13 +12,18 @@
#include <boost/config/workaround.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/accumulator_traits.hpp>
#include <boost/histogram/detail/argument_traits.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/linearize.hpp>
#include <boost/histogram/detail/make_default.hpp>
#include <boost/histogram/detail/optional_index.hpp>
#include <boost/histogram/detail/tuple_slice.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/mp11/utility.hpp>
#include <mutex>
#include <tuple>
#include <type_traits>
@ -27,6 +32,23 @@ namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
struct sample_args_passed_vs_expected;
template <class... Passed, class... Expected>
struct sample_args_passed_vs_expected<std::tuple<Passed...>, std::tuple<Expected...>> {
static_assert(!(sizeof...(Expected) > 0 && sizeof...(Passed) == 0),
"error: accumulator requires samples, but sample argument is missing");
static_assert(
!(sizeof...(Passed) > 0 && sizeof...(Expected) == 0),
"error: accumulator does not accept samples, but sample argument is passed");
static_assert(sizeof...(Passed) == sizeof...(Expected),
"error: numbers of passed and expected sample arguments differ");
static_assert(
std::is_convertible<std::tuple<Passed...>, std::tuple<Expected...>>::value,
"error: sample argument(s) not convertible to accumulator argument(s)");
};
template <class A>
struct storage_grower {
const A& axes_;
@ -168,21 +190,6 @@ auto fill_storage(IW, IS, Storage& s, const Index idx, const Args& a) noexcept {
return s.end();
}
template <class L>
struct args_indices {
static constexpr int _size = static_cast<int>(mp11::mp_size<L>::value);
static constexpr int _weight = static_cast<int>(mp11::mp_find_if<L, is_weight>::value);
static constexpr int _sample = static_cast<int>(mp11::mp_find_if<L, is_sample>::value);
static constexpr unsigned nargs = _size - (_weight < _size) - (_sample < _size);
static constexpr int start =
_weight < _size && _sample < _size && (_weight + _sample < 2)
? 2
: ((_weight == 0 || _sample == 0) ? 1 : 0);
using weight = mp11::mp_int<(_weight < _size ? _weight : -1)>;
using sample = mp11::mp_int<(_sample < _size ? _sample : -1)>;
};
template <int S, int N>
struct linearize_args {
template <class Index, class A, class Args>
@ -216,54 +223,56 @@ constexpr unsigned min(const unsigned n) noexcept {
}
// not growing
template <class Storage, class Axes, class Args>
auto fill_2(mp11::mp_false, const std::size_t offset, Storage& st, const Axes& axes,
const Args& args) {
using pos = args_indices<mp11::mp_transform<std::decay_t, Args>>;
template <class ArgTraits, class Storage, class Axes, class Args>
auto fill_2(ArgTraits, mp11::mp_false, const std::size_t offset, Storage& st,
const Axes& axes, const Args& args) {
mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{offset};
linearize_args<pos::start, min<Axes>(pos::nargs)>::apply(idx, axes, args);
return fill_storage(typename pos::weight{}, typename pos::sample{}, st, idx, args);
linearize_args<ArgTraits::start::value, min<Axes>(ArgTraits::nargs::value)>::apply(
idx, axes, args);
return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
args);
}
// at least one axis is growing
template <class Storage, class A, class Args>
auto fill_2(mp11::mp_true, const std::size_t, Storage& st, A& axes, const Args& args) {
using pos = args_indices<mp11::mp_transform<std::decay_t, Args>>;
std::array<axis::index_type, pos::nargs> shifts;
template <class ArgTraits, class Storage, class Axes, class Args>
auto fill_2(ArgTraits, mp11::mp_true, const std::size_t, Storage& st, Axes& axes,
const Args& args) {
std::array<axis::index_type, ArgTraits::nargs::value> shifts;
// offset must be zero for linearize_growth
mp11::mp_if<has_non_inclusive_axis<A>, optional_index, std::size_t> idx{0};
mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{0};
std::size_t stride = 1;
bool update_needed = false;
mp11::mp_for_each<mp11::mp_iota_c<min<A>(pos::nargs)>>([&](auto i) {
mp11::mp_for_each<mp11::mp_iota_c<min<Axes>(ArgTraits::nargs::value)>>([&](auto i) {
auto& ax = axis_get<i>(axes);
const auto extent =
linearize_growth(idx, shifts[i], stride, ax, std::get<(pos::start + i)>(args));
const auto extent = linearize_growth(idx, shifts[i], stride, ax,
std::get<(ArgTraits::start::value + i)>(args));
update_needed |= shifts[i] != 0;
stride *= extent;
});
if (update_needed) {
storage_grower<A> g(axes);
storage_grower<Axes> g(axes);
g.from_shifts(shifts.data());
g.apply(st, shifts.data());
}
return fill_storage(typename pos::weight{}, typename pos::sample{}, st, idx, args);
return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
args);
}
// pack original args tuple into another tuple (which is unpacked later)
template <int Start, int Size, class IW, class IS, class Args>
decltype(auto) pack_args(IW, IS, const Args& args) noexcept {
return std::make_tuple(std::get<IW::value>(args), std::get<IS::value>(args),
tuple_slice<Start, Size>(args));
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args),
std::get<IS::value>(args));
}
template <int Start, int Size, class IW, class Args>
decltype(auto) pack_args(IW, mp11::mp_int<-1>, const Args& args) noexcept {
return std::make_tuple(std::get<IW::value>(args), tuple_slice<Start, Size>(args));
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args));
}
template <int Start, int Size, class IS, class Args>
decltype(auto) pack_args(mp11::mp_int<-1>, IS, const Args& args) noexcept {
return std::make_tuple(std::get<IS::value>(args), tuple_slice<Start, Size>(args));
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IS::value>(args));
}
template <int Start, int Size, class Args>
@ -275,9 +284,9 @@ decltype(auto) pack_args(mp11::mp_int<-1>, mp11::mp_int<-1>, const Args& args) n
#pragma warning(disable : 4702) // fixing warning would reduce code readability a lot
#endif
template <class S, class A, class Args>
auto fill(const std::size_t offset, S& storage, A& axes, const Args& args) {
using pos = args_indices<mp11::mp_transform<std::decay_t, Args>>;
template <class ArgTraits, class S, class A, class Args>
auto fill(std::true_type, ArgTraits, const std::size_t offset, S& storage, A& axes,
const Args& args) {
using growing = has_growing_axis<A>;
// Sometimes we need to pack the tuple into another tuple:
@ -292,12 +301,18 @@ auto fill(const std::size_t offset, S& storage, A& axes, const Args& args) {
// interface), so we throw at runtime if incompatible argument is passed (e.g.
// 3d tuple)
if (axes_rank(axes) == pos::nargs)
return fill_2(growing{}, offset, storage, axes, args);
else if (axes_rank(axes) == 1 && axis::traits::rank(axis_get<0>(axes)) == pos::nargs)
return fill_2(growing{}, offset, storage, axes,
pack_args<pos::start, pos::nargs>(typename pos::weight{},
typename pos::sample{}, args));
if (axes_rank(axes) == ArgTraits::nargs::value)
return fill_2(ArgTraits{}, growing{}, offset, storage, axes, args);
else if (axes_rank(axes) == 1 &&
axis::traits::rank(axis_get<0>(axes)) == ArgTraits::nargs::value)
return fill_2(
argument_traits_holder<
1, 0, (ArgTraits::wpos::value >= 0 ? 1 : -1),
(ArgTraits::spos::value >= 0 ? (ArgTraits::wpos::value >= 0 ? 2 : 1) : -1),
typename ArgTraits::sargs>{},
growing{}, offset, storage, axes,
pack_args<ArgTraits::start::value, ArgTraits::nargs::value>(
typename ArgTraits::wpos{}, typename ArgTraits::spos{}, args));
return (BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments != histogram rank")),
storage.end());
@ -307,6 +322,12 @@ auto fill(const std::size_t offset, S& storage, A& axes, const Args& args) {
#pragma warning(default : 4702)
#endif
// empty implementation for bad arguments to stop compiler from showing internals
template <class ArgTraits, class S, class A, class Args>
auto fill(std::false_type, ArgTraits, const std::size_t, S& storage, A&, const Args&) {
return storage.end();
}
} // namespace detail
} // namespace histogram
} // namespace boost

View File

@ -20,7 +20,9 @@
#include <boost/histogram/detail/span.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/throw_exception.hpp>
#include <boost/variant2/variant.hpp>
#include <stdexcept>
@ -278,7 +280,7 @@ void fill_n_check_extra_args(std::size_t n, weight_type<T>&& w, Ts&&... ts) {
inline void fill_n_check_extra_args(std::size_t) noexcept {}
template <class S, class A, class T, std::size_t N, class... Us>
void fill_n(const std::size_t offset, S& storage, A& axes,
void fill_n(std::true_type, const std::size_t offset, S& storage, A& axes,
const dtl::span<const T, N> values, Us&&... us) {
static_assert(!std::is_pointer<T>::value,
"passing iterable of pointers not allowed (cannot determine lengths); "
@ -303,6 +305,10 @@ void fill_n(const std::size_t offset, S& storage, A& axes,
values, std::forward<Us>(us)...);
}
// empty implementation for bad arguments to stop compiler from showing internals
template <class... Ts>
void fill_n(std::false_type, Ts...) {}
} // namespace detail
} // namespace histogram
} // namespace boost

View File

@ -7,19 +7,24 @@
#ifndef BOOST_HISTOGRAM_HISTOGRAM_HPP
#define BOOST_HISTOGRAM_HISTOGRAM_HPP
#include <boost/histogram/detail/accumulator_traits.hpp>
#include <boost/histogram/detail/argument_traits.hpp>
#include <boost/histogram/detail/at.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/common_type.hpp>
#include <boost/histogram/detail/fill.hpp>
#include <boost/histogram/detail/fill_n.hpp>
#include <boost/histogram/detail/mutex_base.hpp>
#include <boost/histogram/detail/non_member_container_access.hpp>
#include <boost/histogram/detail/span.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/sample.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/histogram/weight.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/throw_exception.hpp>
#include <mutex>
#include <stdexcept>
@ -51,12 +56,9 @@ namespace histogram {
*/
template <class Axes, class Storage>
class histogram : detail::mutex_base<Axes, Storage> {
using mutex_base_t = typename detail::mutex_base<Axes, Storage>;
public:
static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
static_assert(std::is_same<std::decay_t<Storage>, Storage>::value,
"Storage may not be a reference or const or volatile");
static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
public:
using axes_type = Axes;
@ -66,6 +68,10 @@ public:
using iterator = typename storage_type::iterator;
using const_iterator = typename storage_type::const_iterator;
private:
using mutex_base = typename detail::mutex_base<axes_type, storage_type>;
public:
histogram() = default;
template <class A, class S>
@ -174,16 +180,28 @@ public:
`std::make_tuple(1.2, 2.3)`. If the histogram contains only this axis and no other,
the arguments can be passed directly.
*/
template <class... Args>
iterator operator()(const Args&... args) {
return operator()(std::forward_as_tuple(args...));
template <class Arg0, class... Args>
std::enable_if_t<(detail::is_tuple<Arg0>::value == false || sizeof...(Args) > 0),
iterator>
operator()(const Arg0& arg0, const Args&... args) {
return operator()(std::forward_as_tuple(arg0, args...));
}
/// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`.
template <class... Ts>
iterator operator()(const std::tuple<Ts...>& args) {
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
return detail::fill(offset_, storage_, axes_, args);
using arg_traits = detail::argument_traits<std::decay_t<Ts>...>;
using acc_traits = detail::accumulator_traits<value_type>;
constexpr bool weight_valid =
arg_traits::wpos::value == -1 || acc_traits::wsupport::value;
static_assert(weight_valid, "error: accumulator does not support weights");
detail::sample_args_passed_vs_expected<typename arg_traits::sargs,
typename acc_traits::args>();
constexpr bool sample_valid =
std::is_convertible<typename arg_traits::sargs, typename acc_traits::args>::value;
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
return detail::fill(mp11::mp_bool<(weight_valid && sample_valid)>{}, arg_traits{},
offset_, storage_, axes_, args);
}
/** Fill histogram with several values at once.
@ -203,8 +221,14 @@ public:
*/
template <class Iterable, class = detail::requires_iterable<Iterable>>
void fill(const Iterable& args) {
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
detail::fill_n(offset_, storage_, axes_, detail::make_span(args));
using acc_traits = detail::accumulator_traits<value_type>;
constexpr unsigned n_sample_args_expected =
std::tuple_size<typename acc_traits::args>::value;
static_assert(n_sample_args_expected == 0,
"sample argument is missing but required by accumulator");
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
detail::fill_n(mp11::mp_bool<(n_sample_args_expected == 0)>{}, offset_, storage_,
axes_, detail::make_span(args));
}
/** Fill histogram with several values and weights at once.
@ -214,8 +238,15 @@ public:
*/
template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
void fill(const Iterable& args, const weight_type<T>& weights) {
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
detail::fill_n(offset_, storage_, axes_, detail::make_span(args),
using acc_traits = detail::accumulator_traits<value_type>;
constexpr bool weight_valid = acc_traits::wsupport::value;
static_assert(weight_valid, "error: accumulator does not support weights");
detail::sample_args_passed_vs_expected<std::tuple<>, typename acc_traits::args>();
constexpr bool sample_valid =
std::is_convertible<std::tuple<>, typename acc_traits::args>::value;
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_, storage_,
axes_, detail::make_span(args),
weight(detail::to_ptr_size(weights.value)));
}
@ -234,13 +265,20 @@ public:
@param args iterable of values.
@param samples single sample or an iterable of samples.
*/
template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
void fill(const Iterable& args, const sample_type<T>& samples) {
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
template <class Iterable, class... Ts, class = detail::requires_iterable<Iterable>,
class = mp11::mp_list<detail::requires_iterable<Ts>...>>
void fill(const Iterable& args, const sample_type<std::tuple<Ts...>>& samples) {
using acc_traits = detail::accumulator_traits<value_type>;
using sample_args_passed = std::tuple<decltype(*detail::data(std::declval<Ts>()))...>;
detail::sample_args_passed_vs_expected<sample_args_passed,
typename acc_traits::args>();
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
mp11::tuple_apply(
[&](const auto&... sargs) {
detail::fill_n(offset_, storage_, axes_, detail::make_span(args),
detail::to_ptr_size(sargs)...);
constexpr bool sample_valid =
std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
detail::fill_n(mp11::mp_bool<(sample_valid)>{}, offset_, storage_, axes_,
detail::make_span(args), detail::to_ptr_size(sargs)...);
},
samples.value);
}
@ -255,13 +293,23 @@ public:
fill(args, samples);
}
template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
template <class Iterable, class T, class... Ts,
class = detail::requires_iterable<Iterable>,
class = mp11::mp_list<detail::requires_iterable<Ts>...>>
void fill(const Iterable& args, const weight_type<T>& weights,
const sample_type<U>& samples) {
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
const sample_type<std::tuple<Ts...>>& samples) {
using acc_traits = detail::accumulator_traits<value_type>;
using sample_args = std::tuple<decltype(*detail::data(std::declval<Ts>()))...>;
detail::sample_args_passed_vs_expected<sample_args, typename acc_traits::args>();
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
mp11::tuple_apply(
[&](const auto&... sargs) {
detail::fill_n(offset_, storage_, axes_, detail::make_span(args),
constexpr bool weight_valid = acc_traits::wsupport::value;
static_assert(weight_valid, "error: accumulator does not support weights");
constexpr bool sample_valid =
std::is_convertible<sample_args, typename acc_traits::args>::value;
detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_,
storage_, axes_, detail::make_span(args),
weight(detail::to_ptr_size(weights.value)),
detail::to_ptr_size(sargs)...);
},

View File

@ -54,16 +54,19 @@ boost_test(TYPE compile-fail SOURCES make_histogram_fail0.cpp
boost_test(TYPE compile-fail SOURCES make_histogram_fail1.cpp
LIBRARIES Boost::histogram
)
boost_test(TYPE compile-fail SOURCES profile_fail0.cpp
boost_test(TYPE compile-fail SOURCES histogram_fail0.cpp
LIBRARIES Boost::histogram
)
boost_test(TYPE compile-fail SOURCES profile_fail1.cpp
boost_test(TYPE compile-fail SOURCES histogram_fail1.cpp
LIBRARIES Boost::histogram
)
boost_test(TYPE compile-fail SOURCES profile_fail2.cpp
boost_test(TYPE compile-fail SOURCES histogram_fail2.cpp
LIBRARIES Boost::histogram
)
boost_test(TYPE compile-fail SOURCES profile_fail3.cpp
boost_test(TYPE compile-fail SOURCES histogram_fail3.cpp
LIBRARIES Boost::histogram
)
boost_test(TYPE compile-fail SOURCES histogram_fail4.cpp
LIBRARIES Boost::histogram
)
boost_test(TYPE run SOURCES accumulators_test.cpp
@ -92,6 +95,10 @@ boost_test(TYPE run SOURCES axis_variable_test.cpp
LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES axis_variant_test.cpp
LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES detail_accumulator_traits_test.cpp
LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES detail_argument_traits_test.cpp
LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES detail_args_type_test.cpp
LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES detail_axes_test.cpp

View File

@ -51,6 +51,8 @@ alias cxx14 :
[ run axis_traits_test.cpp ]
[ run axis_variable_test.cpp ]
[ run axis_variant_test.cpp ]
[ run detail_accumulator_traits_test.cpp ]
[ run detail_argument_traits_test.cpp ]
[ run detail_args_type_test.cpp ]
[ run detail_axes_test.cpp ]
[ run detail_convert_integer_test.cpp ]
@ -101,10 +103,11 @@ alias failure :
[ compile-fail axis_variable_fail1.cpp ]
[ compile-fail make_histogram_fail0.cpp ]
[ compile-fail make_histogram_fail1.cpp ]
[ compile-fail profile_fail0.cpp ]
[ compile-fail profile_fail1.cpp ]
[ compile-fail profile_fail2.cpp ]
[ compile-fail profile_fail3.cpp ]
[ compile-fail histogram_fail0.cpp ]
[ compile-fail histogram_fail1.cpp ]
[ compile-fail histogram_fail2.cpp ]
[ compile-fail histogram_fail3.cpp ]
[ compile-fail histogram_fail4.cpp ]
;
alias threading :

View File

@ -8,7 +8,6 @@
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/histogram/axis.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/mp11.hpp>
#include "std_ostream.hpp"
#include "throw_exception.hpp"
#include "utility_axis.hpp"

View File

@ -0,0 +1,82 @@
// Copyright 2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/core/lightweight_test.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/histogram/detail/accumulator_traits.hpp>
#include <boost/histogram/weight.hpp>
#include <tuple>
namespace dtl = boost::histogram::detail;
int main() {
using boost::histogram::weight_type;
struct A1 {
void operator()(){};
};
BOOST_TEST_NOT(dtl::accumulator_traits<A1>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A1>::args, std::tuple<>);
struct A2 {
void operator()(int, double) {}
};
BOOST_TEST_NOT(dtl::accumulator_traits<A2>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A2>::args,
std::tuple<int, double>);
struct A3 {
void operator()() {}
void operator()(weight_type<int>) {}
};
BOOST_TEST(dtl::accumulator_traits<A3>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A3>::args, std::tuple<>);
struct A4 {
void operator()(int, double, char) {}
void operator()(weight_type<int>, int, double, char) {}
};
BOOST_TEST(dtl::accumulator_traits<A4>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A4>::args,
std::tuple<int, double, char>);
struct A5 {
void operator()(const weight_type<int>, int) {}
};
BOOST_TEST(dtl::accumulator_traits<A5>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A5>::args, std::tuple<int>);
struct A6 {
void operator()(const weight_type<int>&, const int) {}
};
BOOST_TEST(dtl::accumulator_traits<A6>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A6>::args, std::tuple<int>);
struct A7 {
void operator()(weight_type<int>&&, int&&) {}
};
BOOST_TEST(dtl::accumulator_traits<A7>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A7>::args, std::tuple<int&&>);
struct B {
int operator+=(int) { return 0; }
};
BOOST_TEST(dtl::accumulator_traits<B>::wsupport::value);
BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<B>::args, std::tuple<>);
BOOST_TEST(dtl::accumulator_traits<int>::wsupport::value);
BOOST_TEST_TRAIT_SAME(dtl::accumulator_traits<int>::args, std::tuple<>);
return boost::report_errors();
}

View File

@ -0,0 +1,61 @@
// Copyright 2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/core/lightweight_test.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/histogram/detail/argument_traits.hpp>
#include <boost/histogram/sample.hpp>
#include <boost/histogram/weight.hpp>
namespace dtl = boost::histogram::detail;
int main() {
using boost::histogram::sample;
using boost::histogram::weight;
// is_weight
{
struct A {};
using B = int;
BOOST_TEST_NOT(dtl::is_weight<A>::value);
BOOST_TEST_NOT(dtl::is_weight<B>::value);
BOOST_TEST_NOT(dtl::is_weight<decltype(sample(0))>::value);
BOOST_TEST(dtl::is_weight<decltype(weight(0))>::value);
}
// is_sample
{
struct A {};
using B = int;
BOOST_TEST_NOT(dtl::is_sample<A>::value);
BOOST_TEST_NOT(dtl::is_sample<B>::value);
BOOST_TEST_NOT(dtl::is_sample<decltype(weight(0))>::value);
BOOST_TEST(dtl::is_sample<decltype(sample(0))>::value);
BOOST_TEST(dtl::is_sample<decltype(sample(0, A{}))>::value);
}
using T1 = dtl::argument_traits<int>;
using E1 = dtl::argument_traits_holder<1, 0, -1, -1, std::tuple<>>;
BOOST_TEST_TRAIT_SAME(T1, E1);
using T2 = dtl::argument_traits<int, int>;
using E2 = dtl::argument_traits_holder<2, 0, -1, -1, std::tuple<>>;
BOOST_TEST_TRAIT_SAME(T2, E2);
using T3 = dtl::argument_traits<decltype(weight(0)), int, int>;
using E3 = dtl::argument_traits_holder<2, 1, 0, -1, std::tuple<>>;
BOOST_TEST_TRAIT_SAME(T3, E3);
using T4 = dtl::argument_traits<decltype(weight(0)), int, int, decltype(sample(0))>;
using E4 = dtl::argument_traits_holder<2, 1, 0, 3, std::tuple<int>>;
BOOST_TEST_TRAIT_SAME(T4, E4);
using T5 = dtl::argument_traits<int, decltype(sample(0, 0.0))>;
using E5 = dtl::argument_traits_holder<1, 0, -1, 1, std::tuple<int, double>>;
BOOST_TEST_TRAIT_SAME(T5, E5);
return boost::report_errors();
}

View File

@ -269,26 +269,6 @@ int main() {
BOOST_TEST_TRAIT_TRUE((is_sequence_of_any_axis<decltype(v)>));
}
// is_weight
{
struct A {};
using B = int;
using C = weight_type<int>;
BOOST_TEST_TRAIT_FALSE((is_weight<A>));
BOOST_TEST_TRAIT_FALSE((is_weight<B>));
BOOST_TEST_TRAIT_TRUE((is_weight<C>));
}
// is_sample
{
struct A {};
using B = int;
using C = sample_type<int>;
BOOST_TEST_TRAIT_FALSE((is_sample<A>));
BOOST_TEST_TRAIT_FALSE((is_sample<B>));
BOOST_TEST_TRAIT_TRUE((is_sample<C>));
}
// has_operator_equal
{
struct A {};

View File

@ -5,10 +5,16 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/make_profile.hpp>
#include <boost/histogram/make_histogram.hpp>
int main() {
using namespace boost::histogram;
auto h = make_profile(axis::integer<>(0, 5));
h(0, weight(1)); // profile requires a sample
auto h = make_histogram(axis::integer<>(0, 5));
// invalid sample argument
h(0, sample(1));
auto values = {0, 1};
h.fill(values, sample(values)); // invalid sample argument
}

View File

@ -5,10 +5,20 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/make_profile.hpp>
#include <boost/histogram/make_histogram.hpp>
int main() {
using namespace boost::histogram;
auto h = make_weighted_profile(axis::integer<>(0, 5));
h(0); // weighted profile requires a sample
struct accumulator {
void operator()() {}
};
auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5));
// invalid weight argument
h(0, weight(1));
auto values = {0, 1};
h.fill(values, weight(1));
}

View File

@ -9,6 +9,17 @@
int main() {
using namespace boost::histogram;
auto h = make_profile(axis::integer<>(0, 5));
h(0, sample(1, 2)); // profile requires one sample
struct accumulator {
void operator()(double) {}
void operator()(weight_type<double>, double) {}
};
auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5));
// accumulator requires sample
h(0, weight(1));
auto values = {1, 2};
h.fill(values, weight(1));
}

23
test/histogram_fail3.cpp Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/make_histogram.hpp>
int main() {
struct accumulator {
void operator()(double) {}
};
using namespace boost::histogram;
auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5));
// wrong number of sample arguments
h(0, sample(1, 2));
auto values = {0, 1};
h.fill(values, sample(values, values));
}

24
test/histogram_fail4.cpp Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/make_histogram.hpp>
int main() {
using namespace boost::histogram;
struct accumulator {
void operator()(std::string) {}
};
auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5));
// invalid weight argument
h(0, sample(1));
auto values = {1, 2};
h.fill(values, sample(values));
}

View File

@ -1,14 +0,0 @@
// Copyright 2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/make_profile.hpp>
int main() {
using namespace boost::histogram;
auto h = make_profile(axis::integer<>(0, 5));
h(0, sample(1, 2)); // weighted profile requires one sample
}