File indexing completed on 2025-01-18 09:38:10
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_HISTOGRAM_AXIS_TRAITS_HPP
0008 #define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
0009
0010 #include <boost/histogram/axis/option.hpp>
0011 #include <boost/histogram/detail/args_type.hpp>
0012 #include <boost/histogram/detail/detect.hpp>
0013 #include <boost/histogram/detail/priority.hpp>
0014 #include <boost/histogram/detail/static_if.hpp>
0015 #include <boost/histogram/detail/try_cast.hpp>
0016 #include <boost/histogram/detail/type_name.hpp>
0017 #include <boost/histogram/fwd.hpp>
0018 #include <boost/mp11/algorithm.hpp>
0019 #include <boost/mp11/list.hpp>
0020 #include <boost/mp11/utility.hpp>
0021 #include <boost/throw_exception.hpp>
0022 #include <boost/variant2/variant.hpp>
0023 #include <stdexcept>
0024 #include <string>
0025 #include <utility>
0026
0027 namespace boost {
0028 namespace histogram {
0029 namespace detail {
0030
0031 template <class Axis>
0032 struct value_type_deducer {
0033 using type =
0034 std::remove_cv_t<std::remove_reference_t<detail::arg_type<decltype(&Axis::index)>>>;
0035 };
0036
0037 template <class Axis>
0038 auto traits_options(priority<2>) -> axis::option::bitset<Axis::options()>;
0039
0040 template <class Axis>
0041 auto traits_options(priority<1>) -> decltype(&Axis::update, axis::option::growth_t{});
0042
0043 template <class Axis>
0044 auto traits_options(priority<0>) -> axis::option::none_t;
0045
0046 template <class Axis>
0047 auto traits_is_inclusive(priority<1>) -> std::integral_constant<bool, Axis::inclusive()>;
0048
0049 template <class Axis>
0050 auto traits_is_inclusive(priority<0>)
0051 -> decltype(traits_options<Axis>(priority<2>{})
0052 .test(axis::option::underflow | axis::option::overflow));
0053
0054 template <class Axis>
0055 auto traits_is_ordered(priority<1>) -> std::integral_constant<bool, Axis::ordered()>;
0056
0057 template <class Axis, class ValueType = typename value_type_deducer<Axis>::type>
0058 auto traits_is_ordered(priority<0>) -> typename std::is_arithmetic<ValueType>::type;
0059
0060 template <class I, class D, class A,
0061 class J = std::decay_t<arg_type<decltype(&A::value)>>>
0062 decltype(auto) value_method_switch(I&& i, D&& d, const A& a, priority<1>) {
0063 return static_if<std::is_same<J, axis::index_type>>(std::forward<I>(i),
0064 std::forward<D>(d), a);
0065 }
0066
0067 template <class I, class D, class A>
0068 double value_method_switch(I&&, D&&, const A&, priority<0>) {
0069
0070
0071 return BOOST_THROW_EXCEPTION(
0072 std::runtime_error(type_name<A>() + " has no value method")),
0073 double{};
0074 }
0075
0076 struct variant_access {
0077 template <class T, class Variant>
0078 static auto get_if(Variant* v) noexcept {
0079 using T0 = mp11::mp_first<std::decay_t<Variant>>;
0080 return static_if<std::is_pointer<T0>>(
0081 [](auto* vptr) {
0082 using TP = mp11::mp_if<std::is_const<std::remove_pointer_t<T0>>, const T*, T*>;
0083 auto ptp = variant2::get_if<TP>(vptr);
0084 return ptp ? *ptp : nullptr;
0085 },
0086 [](auto* vptr) { return variant2::get_if<T>(vptr); }, &(v->impl));
0087 }
0088
0089 template <class T0, class Visitor, class Variant>
0090 static decltype(auto) visit_impl(mp11::mp_identity<T0>, Visitor&& vis, Variant&& v) {
0091 return variant2::visit(std::forward<Visitor>(vis), v.impl);
0092 }
0093
0094 template <class T0, class Visitor, class Variant>
0095 static decltype(auto) visit_impl(mp11::mp_identity<T0*>, Visitor&& vis, Variant&& v) {
0096 return variant2::visit(
0097 [&vis](auto&& x) -> decltype(auto) { return std::forward<Visitor>(vis)(*x); },
0098 v.impl);
0099 }
0100
0101 template <class Visitor, class Variant>
0102 static decltype(auto) visit(Visitor&& vis, Variant&& v) {
0103 using T0 = mp11::mp_first<std::decay_t<Variant>>;
0104 return visit_impl(mp11::mp_identity<T0>{}, std::forward<Visitor>(vis),
0105 std::forward<Variant>(v));
0106 }
0107 };
0108
0109 template <class A>
0110 decltype(auto) metadata_impl(A&& a, decltype(a.metadata(), 0)) {
0111 return std::forward<A>(a).metadata();
0112 }
0113
0114 template <class A>
0115 axis::null_type& metadata_impl(A&&, float) {
0116 static axis::null_type null_value;
0117 return null_value;
0118 }
0119
0120 }
0121
0122 namespace axis {
0123 namespace traits {
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140 template <class Axis>
0141 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0142 using value_type = typename detail::value_type_deducer<Axis>::type;
0143 #else
0144 struct value_type;
0145 #endif
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155 template <class Axis>
0156 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0157 using is_continuous = typename std::is_floating_point<traits::value_type<Axis>>::type;
0158 #else
0159 struct is_continuous;
0160 #endif
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173 template <class Axis>
0174 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0175 using is_reducible = std::is_constructible<Axis, const Axis&, axis::index_type,
0176 axis::index_type, unsigned>;
0177 #else
0178 struct is_reducible;
0179 #endif
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192 template <class Axis>
0193 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0194 using get_options = decltype(detail::traits_options<Axis>(detail::priority<2>{}));
0195 #else
0196 struct get_options;
0197 #endif
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217 template <class Axis>
0218 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0219 using is_inclusive = decltype(detail::traits_is_inclusive<Axis>(detail::priority<1>{}));
0220 #else
0221 struct is_inclusive;
0222 #endif
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242 template <class Axis>
0243 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0244 using is_ordered = decltype(detail::traits_is_ordered<Axis>(detail::priority<1>{}));
0245 #else
0246 struct is_ordered;
0247 #endif
0248
0249
0250
0251
0252
0253
0254
0255 template <class Axis>
0256 constexpr unsigned options(const Axis& axis) noexcept {
0257 (void)axis;
0258 return get_options<Axis>::value;
0259 }
0260
0261
0262 template <class... Ts>
0263 unsigned options(const variant<Ts...>& axis) noexcept {
0264 return axis.options();
0265 }
0266
0267
0268
0269
0270
0271
0272
0273 template <class Axis>
0274 constexpr bool inclusive(const Axis& axis) noexcept {
0275 (void)axis;
0276 return is_inclusive<Axis>::value;
0277 }
0278
0279
0280 template <class... Ts>
0281 bool inclusive(const variant<Ts...>& axis) noexcept {
0282 return axis.inclusive();
0283 }
0284
0285
0286
0287
0288
0289
0290
0291 template <class Axis>
0292 constexpr bool ordered(const Axis& axis) noexcept {
0293 (void)axis;
0294 return is_ordered<Axis>::value;
0295 }
0296
0297
0298 template <class... Ts>
0299 bool ordered(const variant<Ts...>& axis) noexcept {
0300 return axis.ordered();
0301 }
0302
0303
0304
0305
0306
0307
0308
0309 template <class Axis>
0310 constexpr bool continuous(const Axis& axis) noexcept {
0311 (void)axis;
0312 return is_continuous<Axis>::value;
0313 }
0314
0315
0316 template <class... Ts>
0317 bool continuous(const variant<Ts...>& axis) noexcept {
0318 return axis.continuous();
0319 }
0320
0321
0322
0323
0324
0325 template <class Axis>
0326 index_type extent(const Axis& axis) noexcept {
0327 const auto opt = options(axis);
0328 return axis.size() + (opt & option::underflow ? 1 : 0) +
0329 (opt & option::overflow ? 1 : 0);
0330 }
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340 template <class Axis>
0341 decltype(auto) metadata(Axis&& axis) noexcept {
0342 return detail::metadata_impl(std::forward<Axis>(axis), 0);
0343 }
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355 template <class Axis>
0356 decltype(auto) value(const Axis& axis, real_index_type index) {
0357 return detail::value_method_switch(
0358 [index](const auto& a) { return a.value(static_cast<index_type>(index)); },
0359 [index](const auto& a) { return a.value(index); }, axis, detail::priority<1>{});
0360 }
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372 template <class Result, class Axis>
0373 Result value_as(const Axis& axis, real_index_type index) {
0374 return detail::try_cast<Result, std::runtime_error>(
0375 axis::traits::value(axis, index));
0376 }
0377
0378
0379
0380
0381
0382
0383
0384
0385 template <class Axis, class U>
0386 axis::index_type index(const Axis& axis, const U& value) noexcept(
0387 std::is_convertible<U, value_type<Axis>>::value) {
0388 return axis.index(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
0389 }
0390
0391
0392 template <class... Ts, class U>
0393 axis::index_type index(const variant<Ts...>& axis, const U& value) {
0394 return axis.index(value);
0395 }
0396
0397
0398
0399
0400
0401
0402 template <class Axis>
0403 constexpr unsigned int rank(const Axis& axis) {
0404 (void)axis;
0405 using T = value_type<Axis>;
0406
0407 return mp11::mp_eval_if_not<detail::is_tuple<T>, mp11::mp_size_t<1>, mp11::mp_size,
0408 T>::value;
0409 }
0410
0411
0412
0413 template <class... Ts>
0414 unsigned int rank(const axis::variant<Ts...>& axis) {
0415 return detail::variant_access::visit(
0416 [](const auto& a) { return axis::traits::rank(a); }, axis);
0417 }
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428
0429
0430 template <class Axis, class U>
0431 std::pair<index_type, index_type> update(Axis& axis, const U& value) noexcept(
0432 std::is_convertible<U, value_type<Axis>>::value) {
0433 return detail::static_if_c<get_options<Axis>::test(option::growth)>(
0434 [&value](auto& a) {
0435 return a.update(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
0436 },
0437 [&value](auto& a) -> std::pair<index_type, index_type> {
0438 return {axis::traits::index(a, value), 0};
0439 },
0440 axis);
0441 }
0442
0443
0444 template <class... Ts, class U>
0445 std::pair<index_type, index_type> update(variant<Ts...>& axis, const U& value) {
0446 return visit([&value](auto& a) { return a.update(value); }, axis);
0447 }
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458 template <class Axis>
0459 decltype(auto) width(const Axis& axis, index_type index) {
0460 return detail::value_method_switch(
0461 [](const auto&) { return 0; },
0462 [index](const auto& a) { return a.value(index + 1) - a.value(index); }, axis,
0463 detail::priority<1>{});
0464 }
0465
0466
0467
0468
0469
0470
0471
0472
0473
0474 template <class Result, class Axis>
0475 Result width_as(const Axis& axis, index_type index) {
0476 return detail::value_method_switch(
0477 [](const auto&) { return Result{}; },
0478 [index](const auto& a) {
0479 return detail::try_cast<Result, std::runtime_error>(a.value(index + 1) -
0480 a.value(index));
0481 },
0482 axis, detail::priority<1>{});
0483 }
0484
0485 }
0486 }
0487 }
0488 }
0489
0490 #endif