Metaprogramming.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #pragma once
  2. #include <c10/util/TypeList.h>
  3. #include <type_traits>
  4. namespace c10::guts {
  5. /**
  6. * Access information about result type or arguments from a function type.
  7. * Example:
  8. * using A = function_traits<int (float, double)>::return_type // A == int
  9. * using A = function_traits<int (float, double)>::parameter_types::tuple_type
  10. * // A == tuple<float, double>
  11. */
  12. template <class Func>
  13. struct function_traits {
  14. static_assert(
  15. !std::is_same_v<Func, Func>,
  16. "In function_traits<Func>, Func must be a plain function type.");
  17. };
  18. template <class Result, class... Args>
  19. struct function_traits<Result(Args...)> {
  20. using func_type = Result(Args...);
  21. using return_type = Result;
  22. using parameter_types = typelist::typelist<Args...>;
  23. static constexpr auto number_of_parameters = sizeof...(Args);
  24. };
  25. /**
  26. * infer_function_traits: creates a `function_traits` type for a simple
  27. * function (pointer) or functor (lambda/struct). Currently does not support
  28. * class methods.
  29. */
  30. template <typename Functor>
  31. struct infer_function_traits {
  32. using type = function_traits<
  33. c10::guts::detail::strip_class_t<decltype(&Functor::operator())>>;
  34. };
  35. template <typename Result, typename... Args>
  36. struct infer_function_traits<Result (*)(Args...)> {
  37. using type = function_traits<Result(Args...)>;
  38. };
  39. template <typename Result, typename... Args>
  40. struct infer_function_traits<Result(Args...)> {
  41. using type = function_traits<Result(Args...)>;
  42. };
  43. template <typename T>
  44. using infer_function_traits_t = typename infer_function_traits<T>::type;
  45. /**
  46. * make_function_traits: creates a `function_traits` type given a Return type
  47. * and a typelist of Argument types
  48. *
  49. * Example:
  50. * bool f(int, int);
  51. *
  52. * infer_function_traits_t<f> == make_function_traits_t<bool,
  53. * typelist::typelist<int, int>>
  54. */
  55. template <typename Result, typename ArgList>
  56. struct make_function_traits {
  57. static_assert(
  58. false_t<ArgList>::value,
  59. "In guts::make_function_traits<Result, TypeList>, the ArgList argument must be typelist<...>.");
  60. };
  61. template <typename Result, typename... Args>
  62. struct make_function_traits<Result, typelist::typelist<Args...>> {
  63. using type = function_traits<Result(Args...)>;
  64. };
  65. template <typename Result, typename ArgList>
  66. using make_function_traits_t =
  67. typename make_function_traits<Result, ArgList>::type;
  68. /**
  69. * make_offset_index_sequence<Start, N>
  70. * Like make_index_sequence<N>, but starting from Start instead of 0.
  71. *
  72. * Example:
  73. * make_offset_index_sequence<10, 3> == std::index_sequence<10, 11, 12>
  74. */
  75. template <size_t Start, size_t N, size_t... Is>
  76. struct make_offset_index_sequence_impl
  77. : make_offset_index_sequence_impl<Start, N - 1, Start + N - 1, Is...> {
  78. static_assert(
  79. static_cast<int>(Start) >= 0,
  80. "make_offset_index_sequence: Start < 0");
  81. static_assert(static_cast<int>(N) >= 0, "make_offset_index_sequence: N < 0");
  82. };
  83. template <size_t Start, size_t... Is>
  84. struct make_offset_index_sequence_impl<Start, 0, Is...> {
  85. typedef std::index_sequence<Is...> type;
  86. };
  87. template <size_t Start, size_t N>
  88. using make_offset_index_sequence =
  89. typename make_offset_index_sequence_impl<Start, N>::type;
  90. /**
  91. * Use tuple_elements to extract a position-indexed subset of elements
  92. * from the argument tuple into a result tuple.
  93. *
  94. * Example:
  95. * std::tuple<int, const char*, double> t = std::make_tuple(0, "HEY", 2.0);
  96. * std::tuple<int, double> result = tuple_elements(t, std::index_sequence<0,
  97. * 2>());
  98. */
  99. template <class Tuple, size_t... Is>
  100. constexpr auto tuple_elements(Tuple t, std::index_sequence<Is...>) {
  101. return std::tuple<std::tuple_element_t<Is, Tuple>...>(std::get<Is>(t)...);
  102. }
  103. /**
  104. * Use tuple_take to extract the first or last n elements from the argument
  105. * tuple into a result tuple.
  106. *
  107. * Example:
  108. * std::tuple<int, const char*, double> t = std::make_tuple(0, "HEY", 2.0);
  109. * std::tuple<int, const char*> first_two = tuple_take<decltype(t), 2>(t);
  110. * std::tuple<const char*, double> last_two = tuple_take<decltype(t), -2>(t);
  111. */
  112. template <class Tuple, int N, class Enable = void>
  113. struct TupleTake {};
  114. template <class Tuple, int N>
  115. struct TupleTake<Tuple, N, std::enable_if_t<N >= 0, void>> {
  116. static auto call(Tuple t) {
  117. constexpr size_t size = std::tuple_size<Tuple>();
  118. static_assert(N <= size, "tuple_take: N > size");
  119. return tuple_elements(t, std::make_index_sequence<N>{});
  120. }
  121. };
  122. template <class Tuple, int N>
  123. struct TupleTake < Tuple,
  124. N, std::enable_if_t<N<0, void>> {
  125. static auto call(Tuple t) {
  126. constexpr size_t size = std::tuple_size<Tuple>();
  127. static_assert(-N <= size, "tuple_take: -N > size");
  128. return tuple_elements(t, make_offset_index_sequence<size + N, -N>{});
  129. }
  130. };
  131. template <class Tuple, int N>
  132. auto tuple_take(Tuple t) {
  133. return TupleTake<Tuple, N>::call(t);
  134. }
  135. /**
  136. * Use tuple_slice to extract a contiguous subtuple from the argument.
  137. *
  138. * Example:
  139. * std::tuple<int, const char*, double, bool> t = std::make_tuple(0,
  140. * "HEY", 2.0, false); std::tuple<int, const char*> middle_two =
  141. * tuple_slice<decltype(t), 1, 2>(t);
  142. */
  143. template <class Tuple, size_t Start, size_t N>
  144. constexpr auto tuple_slice(Tuple t) {
  145. constexpr size_t size = std::tuple_size<Tuple>();
  146. static_assert(Start + N <= size, "tuple_slice: Start + N > size");
  147. return tuple_elements(t, make_offset_index_sequence<Start, N>{});
  148. }
  149. /**
  150. * Use tuple_map to run a mapping function over a tuple to get a new tuple.
  151. *
  152. * Example 1:
  153. * auto result = tuple_map(std::tuple<int32_t, int32_t, int32_t>(3, 4, 5), []
  154. * (int32_t a) -> int16_t {return a+1;});
  155. * // result == std::tuple<int16_t, int16_t, int16_t>(4, 5, 6)
  156. *
  157. * Example 2:
  158. * struct Mapper {
  159. * std::string operator()(int32_t a) const {
  160. * return std::to_string(a);
  161. * }
  162. * int64_t operator()(const std::string& a) const {
  163. * return atoi(a.c_str());
  164. * }
  165. * };
  166. * auto result = tuple_map(std::tuple<int32_t, std::string>(3, "4"),
  167. * Mapper());
  168. * // result == std::tuple<std::string, int64_t>("3", 4)
  169. *
  170. * Example 3:
  171. * struct A final {
  172. * int32_t func() {
  173. * return 5;
  174. * }
  175. * };
  176. * struct B final {
  177. * std::string func() {
  178. * return "5";
  179. * }
  180. * };
  181. * auto result = tuple_map(std::make_tuple(A(), B()), [] (auto a) { return
  182. * a.func(); });
  183. * // result == std::tuple<int32_t, std::string>(5, "5");
  184. */
  185. namespace detail {
  186. template <class Mapper, class... Args, size_t... Indices>
  187. auto tuple_map(
  188. // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
  189. std::tuple<Args...>&& tuple,
  190. const Mapper& mapper,
  191. std::index_sequence<Indices...>) {
  192. return std::tuple<decltype(mapper(std::forward<Args>(std::get<Indices>(
  193. tuple))))...>(mapper(std::forward<Args>(std::get<Indices>(tuple)))...);
  194. }
  195. } // namespace detail
  196. template <class Mapper, class... Args>
  197. auto tuple_map(std::tuple<Args...>&& tuple, const Mapper& mapper) {
  198. return detail::tuple_map(
  199. std::move(tuple), mapper, std::index_sequence_for<Args...>());
  200. }
  201. } // namespace c10::guts