meta.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. // Copyright 2015-2018 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_DETAIL_META_HPP
  7. #define BOOST_HISTOGRAM_DETAIL_META_HPP
  8. /* Most of the histogram code is generic and works for any number of axes. Buffers with a
  9. * fixed maximum capacity are used in some places, which have a size equal to the rank of
  10. * a histogram. The buffers are statically allocated to improve performance, which means
  11. * that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank
  12. * (you can nevertheless increase it here if necessary): the simplest non-trivial axis has
  13. * 2 bins; even if counters are used which need only a byte of storage per bin, this still
  14. * corresponds to 4 GB of storage.
  15. */
  16. #ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT
  17. #define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32
  18. #endif
  19. #include <boost/config/workaround.hpp>
  20. #if BOOST_WORKAROUND(BOOST_GCC, >= 60000)
  21. #pragma GCC diagnostic push
  22. #pragma GCC diagnostic ignored "-Wnoexcept-type"
  23. #endif
  24. #include <boost/callable_traits/args.hpp>
  25. #include <boost/callable_traits/return_type.hpp>
  26. #if BOOST_WORKAROUND(BOOST_GCC, >= 60000)
  27. #pragma GCC diagnostic pop
  28. #endif
  29. #include <array>
  30. #include <boost/histogram/fwd.hpp>
  31. #include <boost/mp11/algorithm.hpp>
  32. #include <boost/mp11/function.hpp>
  33. #include <boost/mp11/integer_sequence.hpp>
  34. #include <boost/mp11/list.hpp>
  35. #include <boost/mp11/utility.hpp>
  36. #include <functional>
  37. #include <iterator>
  38. #include <limits>
  39. #include <tuple>
  40. #include <type_traits>
  41. namespace boost {
  42. namespace histogram {
  43. namespace detail {
  44. template <class T>
  45. using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
  46. template <class T, class U>
  47. using convert_integer = mp11::mp_if<std::is_integral<remove_cvref_t<T>>, U, T>;
  48. // to be replaced by official version from mp11
  49. template <class E, template <class...> class F, class... Ts>
  50. using mp_eval_or = mp11::mp_eval_if_c<!(mp11::mp_valid<F, Ts...>::value), E, F, Ts...>;
  51. template <class T1, class T2>
  52. using copy_qualifiers = mp11::mp_if<
  53. std::is_rvalue_reference<T1>, T2&&,
  54. mp11::mp_if<std::is_lvalue_reference<T1>,
  55. mp11::mp_if<std::is_const<typename std::remove_reference<T1>::type>,
  56. const T2&, T2&>,
  57. mp11::mp_if<std::is_const<T1>, const T2, T2>>>;
  58. template <class L>
  59. using mp_last = mp11::mp_at_c<L, (mp11::mp_size<L>::value - 1)>;
  60. template <class T, class Args = boost::callable_traits::args_t<T>>
  61. using args_type =
  62. mp11::mp_if<std::is_member_function_pointer<T>, mp11::mp_pop_front<Args>, Args>;
  63. template <class T, std::size_t N = 0>
  64. using arg_type = typename mp11::mp_at_c<args_type<T>, N>;
  65. template <class T>
  66. using return_type = typename boost::callable_traits::return_type<T>::type;
  67. template <class F, class V,
  68. class T = copy_qualifiers<V, mp11::mp_first<remove_cvref_t<V>>>>
  69. using visitor_return_type = decltype(std::declval<F>()(std::declval<T>()));
  70. template <bool B, typename T, typename F, typename... Ts>
  71. constexpr decltype(auto) static_if_c(T&& t, F&& f, Ts&&... ts) {
  72. return std::get<(B ? 0 : 1)>(std::forward_as_tuple(
  73. std::forward<T>(t), std::forward<F>(f)))(std::forward<Ts>(ts)...);
  74. }
  75. template <typename B, typename... Ts>
  76. constexpr decltype(auto) static_if(Ts&&... ts) {
  77. return static_if_c<B::value>(std::forward<Ts>(ts)...);
  78. }
  79. template <typename T>
  80. constexpr T lowest() {
  81. return std::numeric_limits<T>::lowest();
  82. }
  83. template <>
  84. constexpr double lowest() {
  85. return -std::numeric_limits<double>::infinity();
  86. }
  87. template <>
  88. constexpr float lowest() {
  89. return -std::numeric_limits<float>::infinity();
  90. }
  91. template <typename T>
  92. constexpr T highest() {
  93. return std::numeric_limits<T>::max();
  94. }
  95. template <>
  96. constexpr double highest() {
  97. return std::numeric_limits<double>::infinity();
  98. }
  99. template <>
  100. constexpr float highest() {
  101. return std::numeric_limits<float>::infinity();
  102. }
  103. template <std::size_t I, class T, std::size_t... N>
  104. decltype(auto) tuple_slice_impl(T&& t, mp11::index_sequence<N...>) {
  105. return std::forward_as_tuple(std::get<(N + I)>(std::forward<T>(t))...);
  106. }
  107. template <std::size_t I, std::size_t N, class T>
  108. decltype(auto) tuple_slice(T&& t) {
  109. static_assert(I + N <= mp11::mp_size<remove_cvref_t<T>>::value,
  110. "I and N must describe a slice");
  111. return tuple_slice_impl<I>(std::forward<T>(t), mp11::make_index_sequence<N>{});
  112. }
  113. #define BOOST_HISTOGRAM_DETECT(name, cond) \
  114. template <class T, class = decltype(cond)> \
  115. struct name##_impl {}; \
  116. template <class T> \
  117. using name = typename mp11::mp_valid<name##_impl, T>
  118. #define BOOST_HISTOGRAM_DETECT_BINARY(name, cond) \
  119. template <class T, class U, class = decltype(cond)> \
  120. struct name##_impl {}; \
  121. template <class T, class U = T> \
  122. using name = typename mp11::mp_valid<name##_impl, T, U>
  123. BOOST_HISTOGRAM_DETECT(has_method_metadata, (std::declval<T&>().metadata()));
  124. // resize has two overloads, trying to get pmf in this case always fails
  125. BOOST_HISTOGRAM_DETECT(has_method_resize, (std::declval<T&>().resize(0)));
  126. BOOST_HISTOGRAM_DETECT(has_method_size, &T::size);
  127. BOOST_HISTOGRAM_DETECT(has_method_clear, &T::clear);
  128. BOOST_HISTOGRAM_DETECT(has_method_lower, &T::lower);
  129. BOOST_HISTOGRAM_DETECT(has_method_value, &T::value);
  130. BOOST_HISTOGRAM_DETECT(has_method_update, (&T::update));
  131. BOOST_HISTOGRAM_DETECT(has_method_reset, (std::declval<T>().reset(0)));
  132. template <typename T>
  133. using get_value_method_return_type_impl = decltype(std::declval<T&>().value(0));
  134. template <typename T, typename R>
  135. using has_method_value_with_convertible_return_type =
  136. typename std::is_convertible<mp_eval_or<void, get_value_method_return_type_impl, T>,
  137. R>::type;
  138. BOOST_HISTOGRAM_DETECT(has_method_options, (&T::options));
  139. BOOST_HISTOGRAM_DETECT(has_allocator, &T::get_allocator);
  140. BOOST_HISTOGRAM_DETECT(is_indexable, (std::declval<T&>()[0]));
  141. BOOST_HISTOGRAM_DETECT(is_transform, (&T::forward, &T::inverse));
  142. BOOST_HISTOGRAM_DETECT(is_indexable_container,
  143. (std::declval<T>()[0], &T::size, std::begin(std::declval<T>()),
  144. std::end(std::declval<T>())));
  145. BOOST_HISTOGRAM_DETECT(is_vector_like,
  146. (std::declval<T>()[0], &T::size, std::declval<T>().resize(0),
  147. std::begin(std::declval<T>()), std::end(std::declval<T>())));
  148. BOOST_HISTOGRAM_DETECT(is_array_like,
  149. (std::declval<T>()[0], &T::size, std::tuple_size<T>::value,
  150. std::begin(std::declval<T>()), std::end(std::declval<T>())));
  151. BOOST_HISTOGRAM_DETECT(is_map_like,
  152. (std::declval<typename T::key_type>(),
  153. std::declval<typename T::mapped_type>(),
  154. std::begin(std::declval<T>()), std::end(std::declval<T>())));
  155. // ok: is_axis is false for axis::variant, operator() is templated
  156. BOOST_HISTOGRAM_DETECT(is_axis, (&T::size, &T::index));
  157. BOOST_HISTOGRAM_DETECT(is_iterable,
  158. (std::begin(std::declval<T&>()), std::end(std::declval<T&>())));
  159. BOOST_HISTOGRAM_DETECT(is_iterator,
  160. (typename std::iterator_traits<T>::iterator_category()));
  161. BOOST_HISTOGRAM_DETECT(is_streamable,
  162. (std::declval<std::ostream&>() << std::declval<T&>()));
  163. BOOST_HISTOGRAM_DETECT(is_incrementable, (++std::declval<T&>()));
  164. BOOST_HISTOGRAM_DETECT(has_operator_preincrement, (++std::declval<T&>()));
  165. BOOST_HISTOGRAM_DETECT_BINARY(has_operator_equal,
  166. (std::declval<const T&>() == std::declval<const U&>()));
  167. BOOST_HISTOGRAM_DETECT_BINARY(has_operator_radd,
  168. (std::declval<T&>() += std::declval<U&>()));
  169. BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rsub,
  170. (std::declval<T&>() -= std::declval<U&>()));
  171. BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rmul,
  172. (std::declval<T&>() *= std::declval<U&>()));
  173. BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rdiv,
  174. (std::declval<T&>() /= std::declval<U&>()));
  175. template <typename T>
  176. using is_storage =
  177. mp11::mp_bool<(is_indexable_container<T>::value && has_method_reset<T>::value)>;
  178. template <typename T>
  179. struct is_tuple_impl : std::false_type {};
  180. template <typename... Ts>
  181. struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
  182. template <typename T>
  183. using is_tuple = typename is_tuple_impl<T>::type;
  184. template <typename T>
  185. struct is_axis_variant_impl : std::false_type {};
  186. template <typename... Ts>
  187. struct is_axis_variant_impl<axis::variant<Ts...>> : std::true_type {};
  188. template <typename T>
  189. using is_axis_variant = typename is_axis_variant_impl<T>::type;
  190. template <typename T>
  191. using is_any_axis = mp11::mp_or<is_axis<T>, is_axis_variant<T>>;
  192. template <typename T>
  193. using is_sequence_of_axis = mp11::mp_and<is_iterable<T>, is_axis<mp11::mp_first<T>>>;
  194. template <typename T>
  195. using is_sequence_of_axis_variant =
  196. mp11::mp_and<is_iterable<T>, is_axis_variant<mp11::mp_first<T>>>;
  197. template <typename T>
  198. using is_sequence_of_any_axis =
  199. mp11::mp_and<is_iterable<T>, is_any_axis<mp11::mp_first<T>>>;
  200. template <typename T>
  201. struct is_weight_impl : std::false_type {};
  202. template <typename T>
  203. struct is_weight_impl<weight_type<T>> : std::true_type {};
  204. template <typename T>
  205. using is_weight = is_weight_impl<remove_cvref_t<T>>;
  206. template <typename T>
  207. struct is_sample_impl : std::false_type {};
  208. template <typename T>
  209. struct is_sample_impl<sample_type<T>> : std::true_type {};
  210. template <typename T>
  211. using is_sample = is_sample_impl<remove_cvref_t<T>>;
  212. // poor-mans concept checks
  213. template <class T, class = std::enable_if_t<is_iterator<remove_cvref_t<T>>::value>>
  214. struct requires_iterator {};
  215. template <class T, class = std::enable_if_t<is_iterable<remove_cvref_t<T>>::value>>
  216. struct requires_iterable {};
  217. template <class T, class = std::enable_if_t<is_axis<remove_cvref_t<T>>::value>>
  218. struct requires_axis {};
  219. template <class T, class = std::enable_if_t<is_any_axis<remove_cvref_t<T>>::value>>
  220. struct requires_any_axis {};
  221. template <class T,
  222. class = std::enable_if_t<is_sequence_of_axis<remove_cvref_t<T>>::value>>
  223. struct requires_sequence_of_axis {};
  224. template <class T,
  225. class = std::enable_if_t<is_sequence_of_axis_variant<remove_cvref_t<T>>::value>>
  226. struct requires_sequence_of_axis_variant {};
  227. template <class T,
  228. class = std::enable_if_t<is_sequence_of_any_axis<remove_cvref_t<T>>::value>>
  229. struct requires_sequence_of_any_axis {};
  230. template <class T,
  231. class = std::enable_if_t<is_any_axis<mp11::mp_first<remove_cvref_t<T>>>::value>>
  232. struct requires_axes {};
  233. template <class T, class U, class = std::enable_if_t<std::is_convertible<T, U>::value>>
  234. struct requires_convertible {};
  235. template <class T>
  236. auto make_default(const T& t) {
  237. return static_if<has_allocator<T>>([](const auto& t) { return T(t.get_allocator()); },
  238. [](const auto&) { return T(); }, t);
  239. }
  240. template <class T>
  241. using tuple_size_t = typename std::tuple_size<T>::type;
  242. template <class T>
  243. constexpr std::size_t get_size_impl(std::true_type, const T&) noexcept {
  244. return std::tuple_size<T>::value;
  245. }
  246. template <class T>
  247. std::size_t get_size_impl(std::false_type, const T& t) noexcept {
  248. using std::begin;
  249. using std::end;
  250. return static_cast<std::size_t>(std::distance(begin(t), end(t)));
  251. }
  252. template <class T>
  253. std::size_t get_size(const T& t) noexcept {
  254. return get_size_impl(mp11::mp_valid<tuple_size_t, T>(), t);
  255. }
  256. template <class T>
  257. using buffer_size =
  258. mp_eval_or<std::integral_constant<std::size_t, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>,
  259. tuple_size_t, T>;
  260. template <class T, std::size_t N>
  261. class sub_array : public std::array<T, N> {
  262. public:
  263. explicit sub_array(std::size_t s) : size_(s) {}
  264. sub_array(std::size_t s, T value) : size_(s) { std::array<T, N>::fill(value); }
  265. // need to override both versions of std::array
  266. auto end() noexcept { return std::array<T, N>::begin() + size_; }
  267. auto end() const noexcept { return std::array<T, N>::begin() + size_; }
  268. auto size() const noexcept { return size_; }
  269. private:
  270. std::size_t size_ = N;
  271. };
  272. template <class U, class T>
  273. using stack_buffer = sub_array<U, buffer_size<T>::value>;
  274. template <class U, class T, class... Ts>
  275. auto make_stack_buffer(const T& t, Ts&&... ts) {
  276. return stack_buffer<U, T>(get_size(t), std::forward<Ts>(ts)...);
  277. }
  278. template <class T>
  279. constexpr bool relaxed_equal(const T& a, const T& b) noexcept {
  280. return static_if<has_operator_equal<T>>(
  281. [](const auto& a, const auto& b) { return a == b; },
  282. [](const auto&, const auto&) { return true; }, a, b);
  283. }
  284. template <class T>
  285. using get_scale_type_helper = typename T::value_type;
  286. template <class T>
  287. using get_scale_type = mp_eval_or<T, detail::get_scale_type_helper, T>;
  288. struct one_unit {};
  289. template <class T>
  290. T operator*(T&& t, const one_unit&) {
  291. return std::forward<T>(t);
  292. }
  293. template <class T>
  294. T operator/(T&& t, const one_unit&) {
  295. return std::forward<T>(t);
  296. }
  297. template <class T>
  298. using get_unit_type_helper = typename T::unit_type;
  299. template <class T>
  300. using get_unit_type = mp_eval_or<one_unit, detail::get_unit_type_helper, T>;
  301. template <class T, class R = get_scale_type<T>>
  302. R get_scale(const T& t) {
  303. return t / get_unit_type<T>();
  304. }
  305. template <class T, class Default>
  306. using replace_default = mp11::mp_if<std::is_same<T, use_default>, Default, T>;
  307. template <class T, class U>
  308. using is_convertible_helper =
  309. mp11::mp_apply<mp11::mp_all, mp11::mp_transform<std::is_convertible, T, U>>;
  310. template <class T, class U>
  311. using is_convertible = mp_eval_or<std::false_type, is_convertible_helper, T, U>;
  312. template <class T>
  313. auto make_unsigned_impl(std::true_type, const T t) noexcept {
  314. return static_cast<typename std::make_unsigned<T>::type>(t);
  315. }
  316. template <class T>
  317. auto make_unsigned_impl(std::false_type, const T t) noexcept {
  318. return t;
  319. }
  320. template <class T>
  321. auto make_unsigned(const T t) noexcept {
  322. return make_unsigned_impl(std::is_integral<T>{}, t);
  323. }
  324. } // namespace detail
  325. } // namespace histogram
  326. } // namespace boost
  327. #endif