]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
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 | |
44 | public: | |
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 | ||
201 | private: | |
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 | |
209 | template <> | |
210 | class variant<> {}; | |
211 | ||
212 | /// Apply visitor to variant (reference). | |
213 | template <class Visitor, class... Us> | |
214 | decltype(auto) visit(Visitor&& vis, variant<Us...>& var) { | |
215 | return detail::variant_access::visit(vis, var); | |
216 | } | |
217 | ||
218 | /// Apply visitor to variant (movable reference). | |
219 | template <class Visitor, class... Us> | |
220 | decltype(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). | |
225 | template <class Visitor, class... Us> | |
226 | decltype(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. | |
231 | template <class T, class... Us> | |
232 | auto 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. | |
237 | template <class T, class... Us> | |
238 | auto 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. | |
243 | template <class T, class... Us> | |
244 | decltype(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. | |
251 | template <class T, class... Us> | |
252 | decltype(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. | |
259 | template <class T, class... Us> | |
260 | decltype(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 | |
267 | template <class Visitor, class T> | |
268 | decltype(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 | |
273 | template <class T, class U> | |
274 | decltype(auto) get(U&& u) { | |
275 | return std::forward<U>(u); | |
276 | } | |
277 | ||
278 | // pass-through version of get_if for generic programming | |
279 | template <class T, class U> | |
280 | auto 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 | |
285 | template <class T, class U> | |
286 | auto 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 | */ | |
296 | template <class... Us, class... Vs> | |
297 | bool 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 | */ | |
306 | template <class... Us, class T> | |
307 | bool 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 | ||
320 | template <class T, class... Us> | |
321 | bool operator==(const T& t, const variant<Us...>& u) noexcept { | |
322 | return u == t; | |
323 | } | |
324 | ||
325 | /// The negation of operator==. | |
326 | template <class... Us, class... Ts> | |
327 | bool operator!=(const variant<Us...>& u, const variant<Ts...>& t) noexcept { | |
328 | return !(u == t); | |
329 | } | |
330 | ||
331 | /// The negation of operator==. | |
332 | template <class... Us, class T> | |
333 | bool operator!=(const variant<Us...>& u, const T& t) noexcept { | |
334 | return !(u == t); | |
335 | } | |
336 | ||
337 | /// The negation of operator==. | |
338 | template <class T, class... Us> | |
339 | bool 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 |