]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/histogram/axis/variant.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / histogram / axis / variant.hpp
CommitLineData
92f5a8d4
TL
1// Copyright 2015-2019 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_VARIANT_HPP
8#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP
9
10#include <boost/core/nvp.hpp>
11#include <boost/histogram/axis/iterator.hpp>
12#include <boost/histogram/axis/polymorphic_bin.hpp>
13#include <boost/histogram/axis/traits.hpp>
14#include <boost/histogram/detail/relaxed_equal.hpp>
15#include <boost/histogram/detail/static_if.hpp>
16#include <boost/histogram/detail/type_name.hpp>
17#include <boost/histogram/detail/variant_proxy.hpp>
18#include <boost/mp11/algorithm.hpp> // mp_contains
19#include <boost/mp11/list.hpp> // mp_first
20#include <boost/throw_exception.hpp>
20effc67 21#include <boost/variant2/variant.hpp>
92f5a8d4
TL
22#include <stdexcept>
23#include <type_traits>
24#include <utility>
25
26namespace boost {
27namespace histogram {
28namespace axis {
29
30/// Polymorphic axis type
31template <class... Ts>
32class variant : public iterator_mixin<variant<Ts...>> {
33 using impl_type = boost::variant2::variant<Ts...>;
34
35 template <class T>
36 using is_bounded_type = mp11::mp_contains<variant, std::decay_t<T>>;
37
f67539c2 38 template <class T>
92f5a8d4
TL
39 using requires_bounded_type = std::enable_if_t<is_bounded_type<T>::value>;
40
20effc67
TL
41 using metadata_type = std::remove_const_t<std::remove_reference_t<decltype(
42 traits::metadata(std::declval<std::remove_pointer_t<mp11::mp_first<variant>>>()))>>;
92f5a8d4
TL
43
44public:
45 // cannot import ctors with using directive, it breaks gcc and msvc
46 variant() = default;
47 variant(const variant&) = default;
48 variant& operator=(const variant&) = default;
49 variant(variant&&) = default;
50 variant& operator=(variant&&) = default;
51
52 template <class T, class = requires_bounded_type<T>>
53 variant(T&& t) : impl(std::forward<T>(t)) {}
54
55 template <class T, class = requires_bounded_type<T>>
56 variant& operator=(T&& t) {
57 impl = std::forward<T>(t);
58 return *this;
59 }
60
61 template <class... Us>
62 variant(const variant<Us...>& u) {
63 this->operator=(u);
64 }
65
66 template <class... Us>
67 variant& operator=(const variant<Us...>& u) {
68 visit(
69 [this](const auto& u) {
70 using U = std::decay_t<decltype(u)>;
71 detail::static_if<is_bounded_type<U>>(
72 [this](const auto& u) { this->operator=(u); },
73 [](const auto&) {
74 BOOST_THROW_EXCEPTION(std::runtime_error(
75 detail::type_name<U>() + " is not convertible to a bounded type of " +
76 detail::type_name<variant>()));
77 },
78 u);
79 },
80 u);
81 return *this;
82 }
83
84 /// Return size of axis.
85 index_type size() const {
f67539c2 86 return visit([](const auto& a) -> index_type { return a.size(); }, *this);
92f5a8d4
TL
87 }
88
89 /// Return options of axis or option::none_t if axis has no options.
90 unsigned options() const {
f67539c2 91 return visit([](const auto& a) { return traits::options(a); }, *this);
92f5a8d4
TL
92 }
93
94 /// Returns true if the axis is inclusive or false.
95 bool inclusive() const {
f67539c2
TL
96 return visit([](const auto& a) { return traits::inclusive(a); }, *this);
97 }
98
99 /// Returns true if the axis is ordered or false.
100 bool ordered() const {
101 return visit([](const auto& a) { return traits::ordered(a); }, *this);
92f5a8d4
TL
102 }
103
20effc67
TL
104 /// Returns true if the axis is continuous or false.
105 bool continuous() const {
106 return visit([](const auto& a) { return traits::continuous(a); }, *this);
107 }
108
92f5a8d4
TL
109 /// Return reference to const metadata or instance of null_type if axis has no
110 /// metadata.
20effc67 111 metadata_type& metadata() const {
92f5a8d4 112 return visit(
20effc67 113 [](const auto& a) -> metadata_type& {
92f5a8d4 114 using M = decltype(traits::metadata(a));
20effc67
TL
115 return detail::static_if<std::is_same<M, metadata_type&>>(
116 [](const auto& a) -> metadata_type& { return traits::metadata(a); },
117 [](const auto&) -> metadata_type& {
92f5a8d4
TL
118 BOOST_THROW_EXCEPTION(std::runtime_error(
119 "cannot return metadata of type " + detail::type_name<M>() +
120 " through axis::variant interface which uses type " +
121 detail::type_name<metadata_type>() +
122 "; use boost::histogram::axis::get to obtain a reference "
123 "of this axis type"));
124 },
125 a);
126 },
127 *this);
128 }
129
130 /// Return reference to metadata or instance of null_type if axis has no
131 /// metadata.
132 metadata_type& metadata() {
133 return visit(
134 [](auto& a) -> metadata_type& {
135 using M = decltype(traits::metadata(a));
136 return detail::static_if<std::is_same<M, metadata_type&>>(
137 [](auto& a) -> metadata_type& { return traits::metadata(a); },
138 [](auto&) -> metadata_type& {
139 BOOST_THROW_EXCEPTION(std::runtime_error(
140 "cannot return metadata of type " + detail::type_name<M>() +
141 " through axis::variant interface which uses type " +
142 detail::type_name<metadata_type>() +
143 "; use boost::histogram::axis::get to obtain a reference "
144 "of this axis type"));
145 },
146 a);
147 },
148 *this);
149 }
150
151 /** Return index for value argument.
152
153 Throws std::invalid_argument if axis has incompatible call signature.
154 */
155 template <class U>
156 index_type index(const U& u) const {
157 return visit([&u](const auto& a) { return traits::index(a, u); }, *this);
158 }
159
160 /** Return value for index argument.
161
162 Only works for axes with value method that returns something convertible
163 to double and will throw a runtime_error otherwise, see
164 axis::traits::value().
165 */
166 double value(real_index_type idx) const {
167 return visit([idx](const auto& a) { return traits::value_as<double>(a, idx); },
168 *this);
169 }
170
171 /** Return bin for index argument.
172
173 Only works for axes with value method that returns something convertible
174 to double and will throw a runtime_error otherwise, see
175 axis::traits::value().
176 */
177 auto bin(index_type idx) const {
178 return visit(
179 [idx](const auto& a) {
180 return detail::value_method_switch(
181 [idx](const auto& a) { // axis is discrete
182 const double x = traits::value_as<double>(a, idx);
183 return polymorphic_bin<double>(x, x);
184 },
185 [idx](const auto& a) { // axis is continuous
186 const double x1 = traits::value_as<double>(a, idx);
187 const double x2 = traits::value_as<double>(a, idx + 1);
188 return polymorphic_bin<double>(x1, x2);
189 },
f67539c2 190 a, detail::priority<1>{});
92f5a8d4
TL
191 },
192 *this);
193 }
194
92f5a8d4
TL
195 template <class Archive>
196 void serialize(Archive& ar, unsigned /* version */) {
197 detail::variant_proxy<variant> p{*this};
198 ar& make_nvp("variant", p);
199 }
200
201private:
202 impl_type impl;
203
204 friend struct detail::variant_access;
205 friend struct boost::histogram::unsafe_access;
206};
207
208// specialization for empty argument list, useful for meta-programming
209template <>
210class variant<> {};
211
212/// Apply visitor to variant (reference).
213template <class Visitor, class... Us>
214decltype(auto) visit(Visitor&& vis, variant<Us...>& var) {
215 return detail::variant_access::visit(vis, var);
216}
217
218/// Apply visitor to variant (movable reference).
219template <class Visitor, class... Us>
220decltype(auto) visit(Visitor&& vis, variant<Us...>&& var) {
221 return detail::variant_access::visit(vis, std::move(var));
222}
223
224/// Apply visitor to variant (const reference).
225template <class Visitor, class... Us>
226decltype(auto) visit(Visitor&& vis, const variant<Us...>& var) {
227 return detail::variant_access::visit(vis, var);
228}
229
230/// Returns pointer to T in variant or null pointer if type does not match.
231template <class T, class... Us>
232auto get_if(variant<Us...>* v) {
233 return detail::variant_access::template get_if<T>(v);
234}
235
236/// Returns pointer to const T in variant or null pointer if type does not match.
237template <class T, class... Us>
238auto get_if(const variant<Us...>* v) {
239 return detail::variant_access::template get_if<T>(v);
240}
241
242/// Return reference to T, throws std::runtime_error if type does not match.
243template <class T, class... Us>
244decltype(auto) get(variant<Us...>& v) {
245 auto tp = get_if<T>(&v);
246 if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
247 return *tp;
248}
249
250/// Return movable reference to T, throws unspecified exception if type does not match.
251template <class T, class... Us>
252decltype(auto) get(variant<Us...>&& v) {
253 auto tp = get_if<T>(&v);
254 if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
255 return std::move(*tp);
256}
257
258/// Return const reference to T, throws unspecified exception if type does not match.
259template <class T, class... Us>
260decltype(auto) get(const variant<Us...>& v) {
261 auto tp = get_if<T>(&v);
262 if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
263 return *tp;
264}
265
266// pass-through version of visit for generic programming
267template <class Visitor, class T>
268decltype(auto) visit(Visitor&& vis, T&& var) {
269 return std::forward<Visitor>(vis)(std::forward<T>(var));
270}
271
272// pass-through version of get for generic programming
273template <class T, class U>
274decltype(auto) get(U&& u) {
275 return std::forward<U>(u);
276}
277
278// pass-through version of get_if for generic programming
279template <class T, class U>
280auto get_if(U* u) {
281 return reinterpret_cast<T*>(std::is_same<T, std::decay_t<U>>::value ? u : nullptr);
282}
283
284// pass-through version of get_if for generic programming
285template <class T, class U>
286auto get_if(const U* u) {
287 return reinterpret_cast<const T*>(std::is_same<T, std::decay_t<U>>::value ? u
288 : nullptr);
289}
290
20effc67
TL
291/** Compare two variants.
292
293 Return true if the variants point to the same concrete axis type and the types compare
294 equal. Otherwise return false.
295*/
296template <class... Us, class... Vs>
297bool operator==(const variant<Us...>& u, const variant<Vs...>& v) noexcept {
298 return visit([&](const auto& vi) { return u == vi; }, v);
299}
300
301/** Compare variant with a concrete axis type.
302
303 Return true if the variant point to the same concrete axis type and the types compare
304 equal. Otherwise return false.
305*/
306template <class... Us, class T>
307bool operator==(const variant<Us...>& u, const T& t) noexcept {
308 using V = variant<Us...>;
309 return detail::static_if_c<(mp11::mp_contains<V, T>::value ||
310 mp11::mp_contains<V, T*>::value ||
311 mp11::mp_contains<V, const T*>::value)>(
312 [&](const auto& t) {
313 using U = std::decay_t<decltype(t)>;
314 const U* tp = detail::variant_access::template get_if<U>(&u);
315 return tp && detail::relaxed_equal{}(*tp, t);
316 },
317 [&](const auto&) { return false; }, t);
318}
319
320template <class T, class... Us>
321bool operator==(const T& t, const variant<Us...>& u) noexcept {
322 return u == t;
323}
324
325/// The negation of operator==.
326template <class... Us, class... Ts>
327bool operator!=(const variant<Us...>& u, const variant<Ts...>& t) noexcept {
328 return !(u == t);
329}
330
331/// The negation of operator==.
332template <class... Us, class T>
333bool operator!=(const variant<Us...>& u, const T& t) noexcept {
334 return !(u == t);
335}
336
337/// The negation of operator==.
338template <class T, class... Us>
339bool operator!=(const T& t, const variant<Us...>& u) noexcept {
340 return u != t;
341}
342
92f5a8d4
TL
343} // namespace axis
344} // namespace histogram
345} // namespace boost
346
347#endif