variant.hpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // Copyright 2015-2017 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/typeinfo.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/cat.hpp>
  13. #include <boost/histogram/detail/meta.hpp>
  14. #include <boost/histogram/fwd.hpp>
  15. #include <boost/mp11/bind.hpp>
  16. #include <boost/mp11/function.hpp>
  17. #include <boost/mp11/list.hpp>
  18. #include <boost/throw_exception.hpp>
  19. #include <boost/variant/apply_visitor.hpp>
  20. #include <boost/variant/get.hpp>
  21. #include <boost/variant/static_visitor.hpp>
  22. #include <boost/variant/variant.hpp>
  23. #include <ostream>
  24. #include <stdexcept>
  25. #include <tuple>
  26. #include <type_traits>
  27. #include <utility>
  28. namespace boost {
  29. namespace histogram {
  30. namespace axis {
  31. template <class T, class... Us>
  32. T* get_if(variant<Us...>* v);
  33. template <class T, class... Us>
  34. const T* get_if(const variant<Us...>* v);
  35. /// Polymorphic axis type
  36. template <class... Ts>
  37. class variant : public iterator_mixin<variant<Ts...>> {
  38. using impl_type = boost::variant<Ts...>;
  39. using raw_types = mp11::mp_transform<detail::remove_cvref_t, impl_type>;
  40. template <class T>
  41. using is_bounded_type = mp11::mp_contains<raw_types, detail::remove_cvref_t<T>>;
  42. template <typename T>
  43. using requires_bounded_type = std::enable_if_t<is_bounded_type<T>::value>;
  44. // maybe metadata_type or const metadata_type, if bounded type is const
  45. using metadata_type = std::remove_reference_t<decltype(
  46. traits::metadata(std::declval<mp11::mp_first<impl_type>>()))>;
  47. public:
  48. // cannot import ctors with using directive, it breaks gcc and msvc
  49. variant() = default;
  50. variant(const variant&) = default;
  51. variant& operator=(const variant&) = default;
  52. variant(variant&&) = default;
  53. variant& operator=(variant&&) = default;
  54. template <typename T, typename = requires_bounded_type<T>>
  55. variant(T&& t) : impl(std::forward<T>(t)) {}
  56. template <typename T, typename = requires_bounded_type<T>>
  57. variant& operator=(T&& t) {
  58. impl = std::forward<T>(t);
  59. return *this;
  60. }
  61. template <class... Us>
  62. variant(const variant<Us...>& u) {
  63. this->operator=(u);
  64. }
  65. template <class... Us>
  66. variant& operator=(const variant<Us...>& u) {
  67. visit(
  68. [this](const auto& u) {
  69. using U = detail::remove_cvref_t<decltype(u)>;
  70. detail::static_if<is_bounded_type<U>>(
  71. [this](const auto& u) { this->operator=(u); },
  72. [](const auto&) {
  73. BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
  74. boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
  75. " is not convertible to a bounded type of ",
  76. boost::core::demangled_name(BOOST_CORE_TYPEID(variant)))));
  77. },
  78. u);
  79. },
  80. u);
  81. return *this;
  82. }
  83. /// Return size of axis.
  84. index_type size() const {
  85. return visit([](const auto& x) { return x.size(); }, *this);
  86. }
  87. /// Return options of axis or option::none_t if axis has no options.
  88. unsigned options() const {
  89. return visit([](const auto& x) { return axis::traits::options(x); }, *this);
  90. }
  91. /// Return reference to const metadata or instance of null_type if axis has no
  92. /// metadata.
  93. const metadata_type& metadata() const {
  94. return visit(
  95. [](const auto& a) -> const metadata_type& {
  96. using M = decltype(traits::metadata(a));
  97. return detail::static_if<std::is_same<M, const metadata_type&>>(
  98. [](const auto& a) -> const metadata_type& { return traits::metadata(a); },
  99. [](const auto&) -> const metadata_type& {
  100. BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
  101. "cannot return metadata of type ",
  102. boost::core::demangled_name(BOOST_CORE_TYPEID(M)),
  103. " through axis::variant interface which uses type ",
  104. boost::core::demangled_name(BOOST_CORE_TYPEID(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(detail::cat(
  122. "cannot return metadata of type ",
  123. boost::core::demangled_name(BOOST_CORE_TYPEID(M)),
  124. " through axis::variant interface which uses type ",
  125. boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type)),
  126. "; use boost::histogram::axis::get to obtain a reference "
  127. "of this axis type")));
  128. },
  129. a);
  130. },
  131. *this);
  132. }
  133. /// Return index for value argument.
  134. /// Throws std::invalid_argument if axis has incompatible call signature.
  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. double value(real_index_type idx) const {
  144. return visit([idx](const auto& a) { return traits::value_as<double>(a, idx); },
  145. *this);
  146. }
  147. /// Return bin for index argument.
  148. /// Only works for axes with value method that returns something convertible
  149. /// to double and will throw a runtime_error otherwise, see
  150. /// axis::traits::value().
  151. auto bin(index_type idx) const {
  152. return visit(
  153. [idx](const auto& a) {
  154. return detail::value_method_switch_with_return_type<double,
  155. polymorphic_bin<double>>(
  156. [idx](const auto& a) { // axis is discrete
  157. const auto x = a.value(idx);
  158. return polymorphic_bin<double>(x, x);
  159. },
  160. [idx](const auto& a) { // axis is continuous
  161. return polymorphic_bin<double>(a.value(idx), a.value(idx + 1));
  162. },
  163. a);
  164. },
  165. *this);
  166. }
  167. template <class... Us>
  168. bool operator==(const variant<Us...>& u) const {
  169. return visit([&u](const auto& x) { return u == x; }, *this);
  170. }
  171. template <class T>
  172. bool operator==(const T& t) const {
  173. // boost::variant::operator==(T) implemented only to fail, cannot use it
  174. auto tp = get_if<T>(this);
  175. return tp && detail::relaxed_equal(*tp, t);
  176. }
  177. template <class T>
  178. bool operator!=(const T& t) const {
  179. return !operator==(t);
  180. }
  181. template <class Archive>
  182. void serialize(Archive& ar, unsigned);
  183. template <class Visitor, class Variant>
  184. friend auto visit(Visitor&&, Variant &&)
  185. -> detail::visitor_return_type<Visitor, Variant>;
  186. template <class T, class... Us>
  187. friend T& get(variant<Us...>& v);
  188. template <class T, class... Us>
  189. friend const T& get(const variant<Us...>& v);
  190. template <class T, class... Us>
  191. friend T&& get(variant<Us...>&& v);
  192. template <class T, class... Us>
  193. friend T* get_if(variant<Us...>* v);
  194. template <class T, class... Us>
  195. friend const T* get_if(const variant<Us...>* v);
  196. private:
  197. boost::variant<Ts...> impl;
  198. };
  199. /// Apply visitor to variant.
  200. template <class Visitor, class Variant>
  201. auto visit(Visitor&& vis, Variant&& var)
  202. -> detail::visitor_return_type<Visitor, Variant> {
  203. return boost::apply_visitor(std::forward<Visitor>(vis), var.impl);
  204. }
  205. /// Return lvalue reference to T, throws unspecified exception if type does not
  206. /// match.
  207. template <class T, class... Us>
  208. T& get(variant<Us...>& v) {
  209. return boost::get<T>(v.impl);
  210. }
  211. /// Return rvalue reference to T, throws unspecified exception if type does not
  212. /// match.
  213. template <class T, class... Us>
  214. T&& get(variant<Us...>&& v) {
  215. return boost::get<T>(std::move(v.impl));
  216. }
  217. /// Return const reference to T, throws unspecified exception if type does not
  218. /// match.
  219. template <class T, class... Us>
  220. const T& get(const variant<Us...>& v) {
  221. return boost::get<T>(v.impl);
  222. }
  223. /// Returns pointer to T in variant or null pointer if type does not match.
  224. template <class T, class... Us>
  225. T* get_if(variant<Us...>* v) {
  226. return boost::relaxed_get<T>(&(v->impl));
  227. }
  228. /// Returns pointer to const T in variant or null pointer if type does not
  229. /// match.
  230. template <class T, class... Us>
  231. const T* get_if(const variant<Us...>* v) {
  232. return boost::relaxed_get<T>(&(v->impl));
  233. }
  234. #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
  235. // pass-through version of get for generic programming
  236. template <class T, class U>
  237. decltype(auto) get(U&& u) {
  238. return static_cast<detail::copy_qualifiers<U, T>>(u);
  239. }
  240. // pass-through version of get_if for generic programming
  241. template <class T, class U>
  242. T* get_if(U* u) {
  243. return std::is_same<T, detail::remove_cvref_t<U>>::value ? reinterpret_cast<T*>(u)
  244. : nullptr;
  245. }
  246. // pass-through version of get_if for generic programming
  247. template <class T, class U>
  248. const T* get_if(const U* u) {
  249. return std::is_same<T, detail::remove_cvref_t<U>>::value ? reinterpret_cast<const T*>(u)
  250. : nullptr;
  251. }
  252. #endif
  253. } // namespace axis
  254. } // namespace histogram
  255. } // namespace boost
  256. #endif