update axis::traits (#217)

* added traits:::is_continuous, traits::value_type
* use of static_options on qualified types is now invalid
This commit is contained in:
Hans Dembinski
2019-09-24 23:05:03 +01:00
committed by GitHub
parent c5c251fdbf
commit 6b767cc2aa
5 changed files with 106 additions and 70 deletions

View File

@@ -40,7 +40,7 @@ namespace axis {
template <class Value, class MetaData, class Options>
class integer : public iterator_mixin<integer<Value, MetaData, Options>> {
static_assert(std::is_integral<Value>::value || std::is_floating_point<Value>::value,
"integer axis requires type floating point or integral type");
"integer axis requires floating point or integral type");
using value_type = Value;
using local_index_type = std::conditional_t<std::is_integral<value_type>::value,

View File

@@ -28,7 +28,17 @@ namespace histogram {
namespace detail {
template <class T>
using static_options_impl = axis::option::bitset<T::options()>;
using get_options_from_method = axis::option::bitset<T::options()>;
template <class Axis>
struct static_options_impl {
static_assert(std::is_same<std::decay_t<Axis>, Axis>::value,
"support of static_options for qualified types was removed, please use "
"static_options<std::decay_t<...>>");
using type = mp11::mp_eval_or<
mp11::mp_if<has_method_update<Axis>, axis::option::growth_t, axis::option::none_t>,
get_options_from_method, Axis>;
};
template <class I, class D, class A>
double value_method_switch_impl1(std::false_type, I&&, D&&, const A&) {
@@ -97,29 +107,51 @@ struct variant_access {
namespace axis {
namespace traits {
/** Returns reference to metadata of an axis.
/** Get value type for axis type.
If the expression x.metadata() for an axis instance `x` (maybe const) is valid, return
the result. Otherwise, return a reference to a static instance of
boost::histogram::axis::null_type.
@param axis any axis instance
Doxygen does not render this well. This is a meta-function (alias template), it accepts
an axis type and returns the value type.
*/
template <class Axis>
decltype(auto) metadata(Axis&& axis) noexcept {
return detail::static_if<detail::has_method_metadata<std::decay_t<Axis>>>(
[](auto&& a) -> decltype(auto) { return a.metadata(); },
[](auto &&) -> mp11::mp_if<std::is_const<std::remove_reference_t<Axis>>,
axis::null_type const&, axis::null_type&> {
return detail::null_value;
},
std::forward<Axis>(axis));
}
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
using value_type = std::decay_t<decltype(std::declval<Axis>().value(0))>;
#else
struct value_type;
#endif
/** Whether axis is continuous or discrete.
Doxygen does not render this well. This is a meta-function (alias template), it accepts
an axis type and returns a compile-time boolean. If the boolean is true, the axis is
continuous. Otherwise it is discrete.
*/
template <class Axis>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
using is_continuous = typename std::is_floating_point<traits::value_type<Axis>>::type;
#else
struct is_continuous;
#endif
/** Meta-function to detect whether an axis is reducible.
Doxygen does not render this well. This is a meta-function (alias template), it accepts
an axis type and represents compile-time boolean which is true or false, depending on
whether the axis can be reduced with boost::histogram::algorithm::reduce().
@tparam Axis axis type.
*/
template <class Axis>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
using is_reducible = std::is_constructible<Axis, const Axis&, axis::index_type,
axis::index_type, unsigned>;
#else
struct is_reducible;
#endif
/** Get static axis options for axis type.
Doxygen does not render this well. This is a meta-function, it accepts an axis
type and represents its boost::histogram::axis::option::bitset.
Doxygen does not render this well. This is a meta-function (alias template), it accepts
an axis type and returns the boost::histogram::axis::option::bitset.
If Axis::options() is valid and constexpr, static_options is the corresponding
option type. Otherwise, it is boost::histogram::axis::option::growth_t, if the
@@ -129,10 +161,7 @@ decltype(auto) metadata(Axis&& axis) noexcept {
*/
template <class Axis>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
using static_options =
mp11::mp_eval_or<mp11::mp_if<detail::has_method_update<std::decay_t<Axis>>,
option::growth_t, option::none_t>,
detail::static_options_impl, std::decay_t<Axis>>;
using static_options = typename detail::static_options_impl<Axis>::type;
#else
struct static_options;
#endif
@@ -163,6 +192,25 @@ constexpr index_type extent(const Axis& axis) noexcept {
(opt & option::overflow ? 1 : 0);
}
/** Returns reference to metadata of an axis.
If the expression x.metadata() for an axis instance `x` (maybe const) is valid, return
the result. Otherwise, return a reference to a static instance of
boost::histogram::axis::null_type.
@param axis any axis instance
*/
template <class Axis>
decltype(auto) metadata(Axis&& axis) noexcept {
return detail::static_if<detail::has_method_metadata<std::decay_t<Axis>>>(
[](auto&& a) -> decltype(auto) { return a.metadata(); },
[](auto &&) -> mp11::mp_if<std::is_const<std::remove_reference_t<Axis>>,
axis::null_type const&, axis::null_type&> {
return detail::null_value;
},
std::forward<Axis>(axis));
}
/** Returns axis value for index.
If the axis has no `value` method, throw std::runtime_error. If the method exists and
@@ -298,22 +346,6 @@ Result width_as(const Axis& axis, index_type index) {
axis);
}
/** Meta-function to detect whether an axis is reducible.
Doxygen does not render this well. This is a meta-function, it accepts an axis
type and represents std::true_type or std::false_type, depending on whether the axis can
be reduced with boost::histogram::algorithm::reduce().
@tparam Axis axis type.
*/
template <class Axis>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
using is_reducible = std::is_constructible<Axis, const Axis&, axis::index_type,
axis::index_type, unsigned>;
#else
struct is_reducible;
#endif
} // namespace traits
} // namespace axis
} // namespace histogram

View File

@@ -89,7 +89,7 @@ struct storage_grower {
auto sit = shifts;
auto dit = data_;
for_each_axis(axes_, [&](const auto& a) {
using opt = axis::traits::static_options<decltype(a)>;
using opt = axis::traits::static_options<std::decay_t<decltype(a)>>;
if (opt::test(axis::option::underflow)) {
if (dit->idx == 0) {
// axis has underflow and we are in the underflow bin:

View File

@@ -294,7 +294,7 @@ public:
const auto clast = ca + begin_.indices_.hist_->rank() - 1;
begin_.indices_.hist_->for_each_axis(
[ca, clast, cov, &stride, this](const auto& a) mutable {
using opt = axis::traits::static_options<decltype(a)>;
using opt = axis::traits::static_options<std::decay_t<decltype(a)>>;
constexpr int under = opt::test(axis::option::underflow);
constexpr int over = opt::test(axis::option::overflow);
const auto size = a.size();

View File

@@ -16,6 +16,38 @@
using namespace boost::histogram::axis;
int main() {
// value_type
{
BOOST_TEST_TRAIT_SAME(traits::value_type<integer<int>>, int);
BOOST_TEST_TRAIT_SAME(traits::value_type<category<int>>, int);
BOOST_TEST_TRAIT_SAME(traits::value_type<regular<double>>, double);
}
// is_continuous
{
BOOST_TEST_TRAIT_TRUE((traits::is_continuous<regular<>>));
BOOST_TEST_TRAIT_FALSE((traits::is_continuous<integer<int>>));
BOOST_TEST_TRAIT_FALSE((traits::is_continuous<category<int>>));
BOOST_TEST_TRAIT_TRUE((traits::is_continuous<integer<double>>));
}
// is_reducible
{
struct not_reducible {};
struct reducible {
reducible(const reducible&, index_type, index_type, unsigned);
};
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<reducible>));
BOOST_TEST_TRAIT_FALSE((traits::is_reducible<not_reducible>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<regular<>>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<variable<>>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<circular<>>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<integer<>>));
BOOST_TEST_TRAIT_FALSE((traits::is_reducible<category<>>));
}
// index, rank, value, width
{
auto a = integer<>(1, 3);
@@ -30,10 +62,7 @@ int main() {
BOOST_TEST_EQ(traits::rank(b), 1);
BOOST_TEST_EQ(traits::value(b, 0), 1);
BOOST_TEST_EQ(traits::width(b, 0), 1);
auto& b1 = b;
BOOST_TEST(traits::static_options<decltype(b1)>::test(option::underflow));
const auto& b2 = b;
BOOST_TEST(traits::static_options<decltype(b2)>::test(option::underflow));
BOOST_TEST(traits::static_options<decltype(b)>::test(option::underflow));
auto c = category<std::string>{"red", "blue"};
BOOST_TEST_EQ(traits::index(c, "blue"), 1);
@@ -61,8 +90,6 @@ int main() {
{
using A = integer<>;
BOOST_TEST_EQ(traits::static_options<A>::test(option::growth), false);
BOOST_TEST_EQ(traits::static_options<A&>::test(option::growth), false);
BOOST_TEST_EQ(traits::static_options<const A&>::test(option::growth), false);
auto expected = option::underflow | option::overflow;
auto a = A{};
BOOST_TEST_EQ(traits::options(a), expected);
@@ -72,8 +99,6 @@ int main() {
using B = integer<int, null_type, option::growth_t>;
BOOST_TEST_EQ(traits::static_options<B>::test(option::growth), true);
BOOST_TEST_EQ(traits::static_options<B&>::test(option::growth), true);
BOOST_TEST_EQ(traits::static_options<const B&>::test(option::growth), true);
BOOST_TEST_EQ(traits::options(B{}), option::growth);
struct growing {
@@ -81,8 +106,6 @@ int main() {
};
using C = growing;
BOOST_TEST_EQ(traits::static_options<C>::test(option::growth), true);
BOOST_TEST_EQ(traits::static_options<C&>::test(option::growth), true);
BOOST_TEST_EQ(traits::static_options<const C&>::test(option::growth), true);
auto c = C{};
BOOST_TEST_EQ(traits::options(c), option::growth);
BOOST_TEST_EQ(traits::options(static_cast<C&>(c)), option::growth);
@@ -94,8 +117,6 @@ int main() {
};
using D = notgrowing;
BOOST_TEST_EQ(traits::static_options<D>::test(option::growth), false);
BOOST_TEST_EQ(traits::static_options<D&>::test(option::growth), false);
BOOST_TEST_EQ(traits::static_options<const D&>::test(option::growth), false);
auto d = D{};
BOOST_TEST_EQ(traits::options(d), option::none);
BOOST_TEST_EQ(traits::options(static_cast<D&>(d)), option::none);
@@ -146,22 +167,5 @@ int main() {
BOOST_TEST_EQ(traits::metadata(static_cast<const Both&>(b)), 0);
}
// is_reducible
{
struct not_reducible {};
struct reducible {
reducible(const reducible&, index_type, index_type, unsigned);
};
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<reducible>));
BOOST_TEST_TRAIT_FALSE((traits::is_reducible<not_reducible>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<regular<>>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<variable<>>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<circular<>>));
BOOST_TEST_TRAIT_TRUE((traits::is_reducible<integer<>>));
BOOST_TEST_TRAIT_FALSE((traits::is_reducible<category<>>));
}
return boost::report_errors();
}