]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/histogram/axis/traits.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / histogram / axis / traits.hpp
1 // Copyright 2018 Hans Dembinski
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7 #ifndef BOOST_HISTOGRAM_AXIS_TRAITS_HPP
8 #define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
9
10 #include <boost/core/ignore_unused.hpp>
11 #include <boost/histogram/axis/option.hpp>
12 #include <boost/histogram/detail/args_type.hpp>
13 #include <boost/histogram/detail/detect.hpp>
14 #include <boost/histogram/detail/static_if.hpp>
15 #include <boost/histogram/detail/try_cast.hpp>
16 #include <boost/histogram/detail/type_name.hpp>
17 #include <boost/histogram/fwd.hpp>
18 #include <boost/mp11/algorithm.hpp>
19 #include <boost/mp11/list.hpp>
20 #include <boost/mp11/utility.hpp>
21 #include <boost/throw_exception.hpp>
22 #include <boost/variant2/variant.hpp>
23 #include <stdexcept>
24 #include <string>
25 #include <utility>
26
27 namespace boost {
28 namespace histogram {
29 namespace detail {
30
31 template <class T>
32 using get_options_from_method = axis::option::bitset<T::options()>;
33
34 template <class Axis>
35 struct static_options_impl {
36 static_assert(std::is_same<std::decay_t<Axis>, Axis>::value,
37 "support of static_options for qualified types was removed, please use "
38 "static_options<std::decay_t<...>>");
39 using type = mp11::mp_eval_or<
40 mp11::mp_if<has_method_update<Axis>, axis::option::growth_t, axis::option::none_t>,
41 get_options_from_method, Axis>;
42 };
43
44 template <class T>
45 using get_inclusive_from_method = std::integral_constant<bool, T::inclusive()>;
46
47 template <class Axis>
48 struct static_is_inclusive_impl {
49 using type = mp11::mp_eval_or<decltype(static_options_impl<Axis>::type::test(
50 axis::option::underflow | axis::option::overflow)),
51 get_inclusive_from_method, Axis>;
52 };
53
54 template <class I, class D, class A>
55 double value_method_switch_impl1(std::false_type, I&&, D&&, const A&) {
56 // comma trick to make all compilers happy; some would complain about
57 // unreachable code after the throw, others about a missing return
58 return BOOST_THROW_EXCEPTION(
59 std::runtime_error(type_name<A>() + " has no value method")),
60 double{};
61 }
62
63 template <class I, class D, class A>
64 decltype(auto) value_method_switch_impl1(std::true_type, I&& i, D&& d, const A& a) {
65 using T = arg_type<decltype(&A::value)>;
66 return static_if<std::is_same<T, axis::index_type>>(std::forward<I>(i),
67 std::forward<D>(d), a);
68 }
69
70 template <class I, class D, class A>
71 decltype(auto) value_method_switch(I&& i, D&& d, const A& a) {
72 return value_method_switch_impl1(has_method_value<A>{}, std::forward<I>(i),
73 std::forward<D>(d), a);
74 }
75
76 static axis::null_type null_value;
77
78 struct variant_access {
79 template <class T, class Variant>
80 static auto get_if(Variant* v) noexcept {
81 using T0 = mp11::mp_first<std::decay_t<Variant>>;
82 return static_if<std::is_pointer<T0>>(
83 [](auto* vptr) {
84 using TP = mp11::mp_if<std::is_const<std::remove_pointer_t<T0>>, const T*, T*>;
85 auto ptp = variant2::get_if<TP>(vptr);
86 return ptp ? *ptp : nullptr;
87 },
88 [](auto* vptr) { return variant2::get_if<T>(vptr); }, &(v->impl));
89 }
90
91 template <class T0, class Visitor, class Variant>
92 static decltype(auto) visit_impl(mp11::mp_identity<T0>, Visitor&& vis, Variant&& v) {
93 return variant2::visit(std::forward<Visitor>(vis), v.impl);
94 }
95
96 template <class T0, class Visitor, class Variant>
97 static decltype(auto) visit_impl(mp11::mp_identity<T0*>, Visitor&& vis, Variant&& v) {
98 return variant2::visit(
99 [&vis](auto&& x) -> decltype(auto) { return std::forward<Visitor>(vis)(*x); },
100 v.impl);
101 }
102
103 template <class Visitor, class Variant>
104 static decltype(auto) visit(Visitor&& vis, Variant&& v) {
105 using T0 = mp11::mp_first<std::decay_t<Variant>>;
106 return visit_impl(mp11::mp_identity<T0>{}, std::forward<Visitor>(vis),
107 std::forward<Variant>(v));
108 }
109 };
110
111 } // namespace detail
112
113 namespace axis {
114 namespace traits {
115
116 /** Get value type for axis type.
117
118 Doxygen does not render this well. This is a meta-function (template alias), it accepts
119 an axis type and returns the value type.
120 */
121 template <class Axis>
122 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
123 using value_type =
124 std::remove_cv_t<std::remove_reference_t<detail::arg_type<decltype(&Axis::index)>>>;
125 #else
126 struct value_type;
127 #endif
128
129 /** Whether axis is continuous or discrete.
130
131 Doxygen does not render this well. This is a meta-function (template alias), it accepts
132 an axis type and returns a compile-time boolean. If the boolean is true, the axis is
133 continuous. Otherwise it is discrete.
134 */
135 template <class Axis>
136 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
137 using is_continuous = typename std::is_floating_point<traits::value_type<Axis>>::type;
138 #else
139 struct is_continuous;
140 #endif
141
142 /** Meta-function to detect whether an axis is reducible.
143
144 Doxygen does not render this well. This is a meta-function (template alias), it accepts
145 an axis type and represents compile-time boolean which is true or false, depending on
146 whether the axis can be reduced with boost::histogram::algorithm::reduce().
147
148 @tparam Axis axis type.
149 */
150 template <class Axis>
151 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
152 using is_reducible = std::is_constructible<Axis, const Axis&, axis::index_type,
153 axis::index_type, unsigned>;
154 #else
155 struct is_reducible;
156 #endif
157
158 /** Get static axis options for axis type.
159
160 Doxygen does not render this well. This is a meta-function (template alias), it accepts
161 an axis type and returns the boost::histogram::axis::option::bitset.
162
163 If Axis::options() is valid and constexpr, static_options is the corresponding
164 option type. Otherwise, it is boost::histogram::axis::option::growth_t, if the
165 axis has a method `update`, else boost::histogram::axis::option::none_t.
166
167 @tparam Axis axis type
168 */
169 template <class Axis>
170 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
171 using static_options = typename detail::static_options_impl<Axis>::type;
172 #else
173 struct static_options;
174 #endif
175
176 /** Meta-function to detect whether an axis is inclusive.
177
178 Doxygen does not render this well. This is a meta-function (template alias), it accepts
179 an axis type and represents compile-time boolean which is true or false, depending on
180 whether the axis is inclusive or not.
181
182 An inclusive axis has a bin for every possible input value. A histogram which consists
183 only of inclusive axes can be filled more efficiently, since input values always
184 end up in a valid cell and there is no need to keep track of input tuples that need to
185 be discarded.
186
187 An axis with underflow and overflow bins is always inclusive, but an axis may be
188 inclusive under other conditions. The meta-function checks for the method `constexpr
189 static bool inclusive()`, and uses the result. If this method is not present, it uses
190 static_options<Axis> and checks whether the underflow and overflow bits are present.
191
192 @tparam axis type
193 */
194 template <class Axis>
195 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
196 using static_is_inclusive = typename detail::static_is_inclusive_impl<Axis>::type;
197 #else
198 struct static_is_inclusive;
199 #endif
200
201 /** Returns axis options as unsigned integer.
202
203 If axis.options() is a valid expression, return the result. Otherwise, return
204 static_options<Axis>::value.
205
206 @param axis any axis instance
207 */
208 template <class Axis>
209 constexpr unsigned options(const Axis& axis) noexcept {
210 boost::ignore_unused(axis);
211 return static_options<Axis>::value;
212 }
213
214 // specialization for variant
215 template <class... Ts>
216 unsigned options(const variant<Ts...>& axis) noexcept {
217 return axis.options();
218 }
219
220 /** Returns true if axis is inclusive or false.
221
222 See static_is_inclusive for details.
223
224 @param axis any axis instance
225 */
226 template <class Axis>
227 constexpr bool inclusive(const Axis& axis) noexcept {
228 boost::ignore_unused(axis);
229 return static_is_inclusive<Axis>::value;
230 }
231
232 // specialization for variant
233 template <class... Ts>
234 bool inclusive(const variant<Ts...>& axis) noexcept {
235 return axis.inclusive();
236 }
237
238 /** Returns axis size plus any extra bins for under- and overflow.
239
240 @param axis any axis instance
241 */
242 template <class Axis>
243 index_type extent(const Axis& axis) noexcept {
244 const auto opt = options(axis);
245 return axis.size() + (opt & option::underflow ? 1 : 0) +
246 (opt & option::overflow ? 1 : 0);
247 }
248
249 /** Returns reference to metadata of an axis.
250
251 If the expression x.metadata() for an axis instance `x` (maybe const) is valid, return
252 the result. Otherwise, return a reference to a static instance of
253 boost::histogram::axis::null_type.
254
255 @param axis any axis instance
256 */
257 template <class Axis>
258 decltype(auto) metadata(Axis&& axis) noexcept {
259 return detail::static_if<detail::has_method_metadata<std::decay_t<Axis>>>(
260 [](auto&& a) -> decltype(auto) { return a.metadata(); },
261 [](auto &&) -> mp11::mp_if<std::is_const<std::remove_reference_t<Axis>>,
262 axis::null_type const&, axis::null_type&> {
263 return detail::null_value;
264 },
265 std::forward<Axis>(axis));
266 }
267
268 /** Returns axis value for index.
269
270 If the axis has no `value` method, throw std::runtime_error. If the method exists and
271 accepts a floating point index, pass the index and return the result. If the method
272 exists but accepts only integer indices, cast the floating point index to int, pass this
273 index and return the result.
274
275 @param axis any axis instance
276 @param index floating point axis index
277 */
278 template <class Axis>
279 decltype(auto) value(const Axis& axis, real_index_type index) {
280 return detail::value_method_switch(
281 [index](const auto& a) { return a.value(static_cast<index_type>(index)); },
282 [index](const auto& a) { return a.value(index); }, axis);
283 }
284
285 /** Returns axis value for index if it is convertible to target type or throws.
286
287 Like boost::histogram::axis::traits::value, but converts the result into the requested
288 return type. If the conversion is not possible, throws std::runtime_error.
289
290 @tparam Result requested return type
291 @tparam Axis axis type
292 @param axis any axis instance
293 @param index floating point axis index
294 */
295 template <class Result, class Axis>
296 Result value_as(const Axis& axis, real_index_type index) {
297 return detail::try_cast<Result, std::runtime_error>(
298 value(axis, index)); // avoid conversion warning
299 }
300
301 /** Returns axis index for value.
302
303 Throws std::invalid_argument if the value argument is not implicitly convertible.
304
305 @param axis any axis instance
306 @param value argument to be passed to `index` method
307 */
308 template <class Axis, class U>
309 axis::index_type index(const Axis& axis, const U& value) noexcept(
310 std::is_convertible<U, value_type<Axis>>::value) {
311 return axis.index(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
312 }
313
314 // specialization for variant
315 template <class... Ts, class U>
316 axis::index_type index(const variant<Ts...>& axis, const U& value) {
317 return axis.index(value);
318 }
319
320 /** Return axis rank (how many arguments it processes).
321
322 @param axis any axis instance
323 */
324 template <class Axis>
325 constexpr unsigned rank(const Axis& axis) {
326 boost::ignore_unused(axis);
327 using T = value_type<Axis>;
328 // cannot use mp_eval_or since T could be a fixed-sized sequence
329 return mp11::mp_eval_if_not<detail::is_tuple<T>, mp11::mp_size_t<1>, mp11::mp_size,
330 T>::value;
331 }
332
333 // specialization for variant
334 template <class... Ts>
335 unsigned rank(const axis::variant<Ts...>& axis) {
336 return detail::variant_access::visit([](const auto& a) { return rank(a); }, axis);
337 }
338
339 /** Returns pair of axis index and shift for the value argument.
340
341 Throws `std::invalid_argument` if the value argument is not implicitly convertible to
342 the argument expected by the `index` method. If the result of
343 boost::histogram::axis::traits::static_options<decltype(axis)> has the growth flag set,
344 call `update` method with the argument and return the result. Otherwise, call `index`
345 and return the pair of the result and a zero shift.
346
347 @param axis any axis instance
348 @param value argument to be passed to `update` or `index` method
349 */
350 template <class Axis, class U>
351 std::pair<index_type, index_type> update(Axis& axis, const U& value) noexcept(
352 std::is_convertible<U, value_type<Axis>>::value) {
353 return detail::static_if_c<static_options<Axis>::test(option::growth)>(
354 [&value](auto& a) {
355 return a.update(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
356 },
357 [&value](auto& a) { return std::make_pair(index(a, value), index_type{0}); }, axis);
358 }
359
360 // specialization for variant
361 template <class... Ts, class U>
362 std::pair<index_type, index_type> update(variant<Ts...>& axis, const U& value) {
363 return visit([&value](auto& a) { return a.update(value); }, axis);
364 }
365
366 /** Returns bin width at axis index.
367
368 If the axis has no `value` method, throw std::runtime_error. If the method exists and
369 accepts a floating point index, return the result of `axis.value(index + 1) -
370 axis.value(index)`. If the method exists but accepts only integer indices, return 0.
371
372 @param axis any axis instance
373 @param index bin index
374 */
375 template <class Axis>
376 decltype(auto) width(const Axis& axis, index_type index) {
377 return detail::value_method_switch(
378 [](const auto&) { return 0; },
379 [index](const auto& a) { return a.value(index + 1) - a.value(index); }, axis);
380 }
381
382 /** Returns bin width at axis index.
383
384 Like boost::histogram::axis::traits::width, but converts the result into the requested
385 return type. If the conversion is not possible, throw std::runtime_error.
386
387 @param axis any axis instance
388 @param index bin index
389 */
390 template <class Result, class Axis>
391 Result width_as(const Axis& axis, index_type index) {
392 return detail::value_method_switch(
393 [](const auto&) { return Result{}; },
394 [index](const auto& a) {
395 return detail::try_cast<Result, std::runtime_error>(a.value(index + 1) -
396 a.value(index));
397 },
398 axis);
399 }
400
401 } // namespace traits
402 } // namespace axis
403 } // namespace histogram
404 } // namespace boost
405
406 #endif