variant.hpp 11 KB

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