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