callable.h
Go to the documentation of this file.
1 //
2 // Created by Eduard Valeyev on 3/1/22.
3 //
4 
5 #ifndef TTG_META_CALLABLE_H
6 #define TTG_META_CALLABLE_H
7 
8 #include "ttg/util/meta.h"
9 #include "ttg/util/typelist.h"
10 
11 #ifdef TTG_USE_BUNDLED_BOOST_CALLABLE_TRAITS
13 #else
15 #endif
16 
17 namespace ttg::meta {
18 
20  // nongeneric callables
22  // handled using Boost.CallableTraits ... to detect whether a callable is generic or not detect existence of
23  // boost::callable_traits::args_t
24  template <typename Callable, typename = void>
25  struct is_generic_callable : std::true_type {};
26 
27  template <typename Callable>
28  struct is_generic_callable<Callable, ttg::meta::void_t<boost::callable_traits::args_t<Callable, ttg::typelist>>>
29  : std::false_type {};
30 
31  template <typename Callable>
33 
43  template <typename Callable, typename Enabler = void>
44  constexpr std::pair<bool, std::pair<ttg::typelist<>, ttg::typelist<>>> callable_args = {true, {}};
45 
46  template <typename Callable>
47  constexpr auto callable_args<Callable, ttg::meta::void_t<boost::callable_traits::args_t<Callable, ttg::typelist>>> =
48  std::pair<bool, std::pair<ttg::typelist<boost::callable_traits::return_type_t<Callable>>,
50 
52  // generic callables
54 
56 
60  template <std::size_t N>
61  constexpr auto ordinal2index(std::size_t ordinal, std::array<std::size_t, N> extents) {
62  std::array<std::size_t, N> idx = {};
63  for (size_t d = 0; d < N; ++d) {
64  idx[d] = ordinal % extents[d];
65  ordinal /= extents[d];
66  }
67  return idx;
68  }
69 
83  template <std::size_t Ordinal, typename Func, typename... Typelists, std::size_t... ArgIdx>
84  auto compute_arg_binding_types_impl(Func& func, typelist<Typelists...> argument_type_lists,
85  std::index_sequence<ArgIdx...> arg_idx = {}) {
86  using arg_typelists_t = typelist<Typelists...>;
87  constexpr auto Order = sizeof...(Typelists);
88  constexpr std::array<std::size_t, Order> extents = {
89  std::tuple_size_v<std::tuple_element_t<ArgIdx, arg_typelists_t>>...};
90  constexpr auto tensor_size = (extents[ArgIdx] * ...);
91  static_assert(tensor_size >= Ordinal);
92  if constexpr (tensor_size == Ordinal) {
93  return typelist<typelist<>, typelist<>>{};
94  } else {
95  constexpr auto idx = ordinal2index(Ordinal, extents);
96  auto args = typelist<std::tuple_element_t<idx[ArgIdx], std::tuple_element_t<ArgIdx, arg_typelists_t>>...>{};
97  using args_sans_void_t = drop_void_t<decltype(args)>;
98  if constexpr (is_invocable_typelist_v<Func, args_sans_void_t>) {
99  using return_type = invoke_result_typelist_t<Func, args_sans_void_t>;
100  return ttg::typelist<ttg::typelist<return_type>, decltype(args)>{};
101  } else {
102  return compute_arg_binding_types_impl<Ordinal + 1>(func, argument_type_lists, arg_idx);
103  }
104  }
105  }
106 
121  template <std::size_t Ordinal, typename ReturnType, typename Func, typename... Typelists, std::size_t... ArgIdx>
122  auto compute_arg_binding_types_r_impl(Func& func, typelist<Typelists...> argument_type_lists,
123  std::index_sequence<ArgIdx...> arg_idx = {}) {
124  using arg_typelists_t = typelist<Typelists...>;
125  constexpr auto Order = sizeof...(Typelists);
126  constexpr std::array<std::size_t, Order> extents = {
127  std::tuple_size_v<std::tuple_element_t<ArgIdx, arg_typelists_t>>...};
128  constexpr auto tensor_size = (extents[ArgIdx] * ...);
129  static_assert(tensor_size >= Ordinal);
130  if constexpr (tensor_size == Ordinal) {
131  return typelist<>{};
132  } else {
133  constexpr auto idx = ordinal2index(Ordinal, extents);
134  auto args = typelist<std::tuple_element_t<idx[ArgIdx], std::tuple_element_t<ArgIdx, arg_typelists_t>>...>{};
135  if constexpr (is_invocable_typelist_r_v<ReturnType, Func, drop_void_t<decltype(args)>>) {
136  return args;
137  } else {
138  return compute_arg_binding_types_r_impl<Ordinal + 1, ReturnType>(func, argument_type_lists, arg_idx);
139  }
140  }
141  }
142 
153  template <typename Func, typename... Typelists>
154  auto compute_arg_binding_types(Func& func, typelist<Typelists...> argument_type_lists) {
155  constexpr auto is_generic__args = callable_args<Func&>;
156  constexpr bool is_generic = is_generic__args.first;
157  if constexpr (is_generic) {
158  return compute_arg_binding_types_impl<0>(func, argument_type_lists,
159  std::make_index_sequence<sizeof...(Typelists)>{});
160  } else {
161  return is_generic__args.second;
162  }
163  }
164 
174  template <typename ReturnType, typename Func, typename... Typelists>
175  auto compute_arg_binding_types_r(Func& func, typelist<Typelists...> argument_type_lists) {
176  constexpr auto is_generic__args = callable_args<Func&>;
177  constexpr bool is_generic = is_generic__args.first;
178  if constexpr (is_generic) {
179  return compute_arg_binding_types_r_impl<0, ReturnType>(func, argument_type_lists,
180  std::make_index_sequence<sizeof...(Typelists)>{});
181  } else {
182  return is_generic__args.second.second;
183  }
184  }
185 
188  template <typename T, typename = void>
190 
191  template <typename T>
192  struct candidate_argument_bindings<T, std::enable_if_t<!std::is_reference_v<T> && !std::is_void_v<T>>> {
193  using type = std::conditional_t<std::is_const_v<T>, typelist<const T&>,
194  typelist<
195  // RATIONALE for this order of binding detection tries:
196  // - to be able to distinguish arguments declared as auto& vs auto&& should try
197  // binding to T&& first since auto& won't bind to it
198  // - HOWEVER argument declared as const T& will bind to either T&& or const T&,
199  // so this order will detect such argument as binding to T&&, which will
200  // indicate to the runtime that the argument is CONSUMABLE and may cause
201  // creation of extra copies. Thus you should not try to use nongeneric
202  // data arguments in generic task functions; for purely nongeneric functions
203  // a different introspection mechanism (Boost.CallableTraits) is used
204  T&&, const T&
205  // - no need to check T& since auto& and auto&& both bind to it
206  //, T&
207  >>;
208  };
209 
210  template <>
211  struct candidate_argument_bindings<void, void> {
212  using type = typelist<>;
213  };
214 
215  template <>
216  struct candidate_argument_bindings<const void, void> {
217  using type = typelist<>;
218  };
219 
220  template <typename T>
222 } // namespace ttg::meta
223 
224 #endif // TTG_META_CALLABLE_H
typename make_index_sequence_t< I... >::type make_index_sequence
detail::try_but_fail_if_invalid< typename detail::traits< detail::shallow_decay< T > >::template expand_args< Container >, cannot_expand_the_parameter_list_of_first_template_argument > args_t
Definition: args.hpp:29
auto compute_arg_binding_types_r(Func &func, typelist< Typelists... > argument_type_lists)
Definition: callable.h:175
auto compute_arg_binding_types_impl(Func &func, typelist< Typelists... > argument_type_lists, std::index_sequence< ArgIdx... > arg_idx={})
Definition: callable.h:84
auto compute_arg_binding_types(Func &func, typelist< Typelists... > argument_type_lists)
Definition: callable.h:154
constexpr std::pair< bool, std::pair< ttg::typelist<>, ttg::typelist<> > > callable_args
Definition: callable.h:44
void void_t
Definition: meta.h:23
constexpr bool is_generic_callable_v
Definition: callable.h:32
constexpr auto ordinal2index(std::size_t ordinal, std::array< std::size_t, N > extents)
Definition: callable.h:61
auto compute_arg_binding_types_r_impl(Func &func, typelist< Typelists... > argument_type_lists, std::index_sequence< ArgIdx... > arg_idx={})
Definition: callable.h:122
typename candidate_argument_bindings< T >::type candidate_argument_bindings_t
Definition: callable.h:221
top-level TTG namespace contains runtime-neutral functionality
Definition: keymap.h:8
std::conditional_t< std::is_const_v< T >, typelist< const T & >, typelist< T &&, const T & > > type
Definition: callable.h:207
A container for types.
Definition: typelist.h:24