Micro template library A library for building device drivers
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

359 linhas
12 KiB

  1. /*!
  2. * \file invoke.h
  3. * \brief Template meta-programming utilities for callables
  4. *
  5. * \copyright
  6. * Copyright (C) 2018 Christos Choutouridis <christos@choutouridis.net>\n
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation, either version 3
  10. * of the License, or (at your option) any later version.\n
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Lesser General Public License for more details.\n
  15. * You should have received a copy of the GNU Lesser General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #ifndef __utl_meta_invoke_h__
  19. #define __utl_meta_invoke_h__
  20. #include <utl/core/impl.h>
  21. #include <utl/meta/basic.h>
  22. #include <utl/meta/detection.h>
  23. #include <utl/meta/operations.h>
  24. /*!
  25. * \ingroup meta
  26. * \defgroup invoke
  27. * A meta-programming invoke() analogous. A \c meta::invocable shall contain a nested
  28. * template type named \b apply which is bind to actual invocable meta-function.
  29. *
  30. * - We can use \c wrap<> or even better \c quote<> in order to wrap a metafunction to a type (metafunction class)
  31. * - We can pass these wrapped types to other metafunctions
  32. * - We can \c invoke<> the inner \c apply from a wrapped metafunction class.
  33. */
  34. //! @{
  35. namespace utl {
  36. namespace meta{
  37. /*!
  38. * \name identity implementation
  39. */
  40. //! @{
  41. //! Identity is a metafunction always return the input type
  42. template <typename _Tp>
  43. struct identity {
  44. #if defined (UTL_WORKAROUND_CWG_1558)
  45. // redirect unused Ts... via void_t
  46. template <typename... Ts>
  47. using apply = first_of<_Tp, void_t<Ts...>>; //!< identity is invokable, must also have apply
  48. #else
  49. template <typename...>
  50. using apply = _Tp; //!< identity is invokable, must also have apply
  51. #endif
  52. using type = _Tp; //!< identity
  53. };
  54. //! identity type alias
  55. template <typename _Tp>
  56. using identity_t = eval<identity<_Tp>>;
  57. //! @}
  58. /*!
  59. * name invoke, invoke_t
  60. */
  61. //! @{
  62. /*!
  63. * Invoke the nested apply meta-function from \c Fn with the arguments \c Args.
  64. * \note
  65. * This is an analogous to the std::invoke()
  66. */
  67. template <typename Fn, typename... Args>
  68. using invoke = typename Fn::template apply<Args...>;
  69. /*!
  70. * Evaluate the invocation of the nested apply metafunction from \p Fn
  71. * with the arguments \p Args.
  72. */
  73. template <typename Fn, typename... Args>
  74. using invoke_t = eval< invoke <Fn, Args...>>;
  75. //! @}
  76. //! \name wrap
  77. //! @{
  78. /*!
  79. * wrap is a higher-order primitive that wraps an n-ary Metafunction
  80. * to create a corresponding Metafunction Class (Invocable). This way
  81. * we can pass Metafunctions as types to other metafunctions and let
  82. * them \c invoke the inner templated apply
  83. */
  84. template <template <typename...> class F>
  85. struct wrap {
  86. template <typename... Args>
  87. using apply = F<Args...>;
  88. };
  89. //! Wrap a template \p F taking literal constants of type \p T into an Invokable
  90. template <typename T, template <T...> class F>
  91. struct wrap_i {
  92. // requires meta::Integral
  93. template <typename... Ts>
  94. using apply = F<Ts::type::value...>;
  95. };
  96. //! @}
  97. //! \name Is applicable trait
  98. //! @{
  99. namespace details {
  100. template<template<typename...> class F, typename... T>
  101. struct is_applicable_ {
  102. template<template<typename...> class G, typename = G<T...>>
  103. static true_ check (int); //< T.. can be passed to G
  104. template<template<typename...> class>
  105. static false_ check (...); //< all other combinations
  106. using type = decltype(check<F>(0));
  107. };
  108. template<typename F, typename... T>
  109. struct is_applicable_q_ {
  110. template<typename G, typename Ret = invoke_t<G, T...>>
  111. static Ret check (int); //< T.. can be passed to G
  112. template<typename...>
  113. static nil_ check (...); //< all other combinations
  114. using type = if_ <
  115. not_same_<
  116. nil_,
  117. decltype(check<F>(0))
  118. >, true_, false_
  119. >;
  120. //!\note
  121. //! check doesn't return \c true_ or \c false_. The reason is that the \p F is
  122. //! probably quoted/deferred. This implies an embedded is_applicable<> check
  123. //! inside defer<> and so its guaranteed to be well formed.
  124. //! Thus we return the actual evaluated type and make the check for nil_
  125. };
  126. template<typename T, template <T...> class F, T... Is>
  127. struct is_applicable_i_ {
  128. template<typename TT, template<TT...> class G, typename = G<Is...>>
  129. static true_ check (int); //< Is... can be passed to G
  130. template<typename TT, template<TT...> class G>
  131. static false_ check (...); //< all other combinations
  132. using type = decltype(check<T, F>(0));
  133. };
  134. }
  135. //! check if we can instantiate \p F with parameters \p T
  136. template<template<typename...> class F, typename... T>
  137. using is_applicable_t = eval<
  138. details::is_applicable_<F, T...>
  139. >;
  140. //! check if we can invoke \p Q with parameters \p T
  141. template<typename Q, typename... T>
  142. using is_applicable_qt = eval <
  143. details::is_applicable_q_ <Q, T...>
  144. >;
  145. //! check if we can instantiate \p F with parameters \p Is of type \p T
  146. template <typename T, template<T...> class F, T... Is>
  147. using is_applicable_it = eval<
  148. details::is_applicable_i_<T, F, Is...>
  149. >;
  150. //! @}
  151. //! \name defer
  152. //! @{
  153. namespace details {
  154. template<template<typename...> class F, typename... Ts>
  155. struct defer_ {
  156. using type = F<Ts...>;
  157. };
  158. template<typename T, template<T...> class F, T... Is>
  159. struct defer_i_ {
  160. using type = F<Is...>;
  161. };
  162. //! \note
  163. //! We use struct instead of:
  164. //! template<template<typename...> class F, typename... Ts>
  165. //! using defer_ = F<Ts...>;
  166. //!
  167. //! The use of struct here is due to Core issue 1430 \ref link1 and is used
  168. //! as suggested by Roy Crihfield in \ref link2.
  169. //! In short, this is due to language's inability to expand Ts... into
  170. //! a fixed parameter list of an alias template.
  171. //!
  172. //! \anchor link1 https://wg21.link/cwg1430
  173. //! \anchor link2 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59498
  174. }
  175. //! defer alias template for F<Ts...>
  176. template<template<class...> class F, class... Ts>
  177. using defer = if_<
  178. details::is_applicable_<F, Ts...>,
  179. details::defer_<F, Ts...>,
  180. nil_ //!< Safe, nil_ is dereferencable
  181. >;
  182. //! defer_i alias template for F<T, Is...>
  183. template <typename T, template<T...> class F, T... Is>
  184. using defer_i = if_ <
  185. details::is_applicable_i_<T, F, Is...>,
  186. details::defer_i_<T, F, Is...>,
  187. nil_ //!< Safe, nil_ is dereferencable
  188. >;
  189. //! @}
  190. //! \name quote
  191. //! @{
  192. /*!
  193. * quote deferred is a higher-order primitive that wraps an n-ary Metafunction
  194. * to create a corresponding Metafunction Class (Invocable) using defer<> to
  195. * postpone the evaluation of Metafunction. This is a safe version of \c wrap<>.
  196. * Again this way we can pass Metafunctions as types to other metafunctions and let
  197. * them \c invoke the inner templated apply
  198. */
  199. template <template <typename...> class F>
  200. struct quote {
  201. template <typename... Args>
  202. using apply = eval<
  203. defer<F, Args...> //!< defer here to avoid DR1430
  204. >;
  205. };
  206. //! Wrap a template \p F taking literal constants of type \p T into an Invokable
  207. template <typename T, template <T...> class F>
  208. struct quote_i {
  209. // requires meta::Integral
  210. template <typename... Ts>
  211. using apply = eval<
  212. defer_i<T, F, Ts::type::value...> //!< defer here to avoid DR1430
  213. >;
  214. };
  215. //! @}
  216. //! \name compose
  217. //! @{
  218. namespace details {
  219. template <template <typename...> class... Fns> struct compose_f_ {};
  220. // recursive call to all invokes
  221. template <template <typename...> class Fn0,
  222. template <typename...> class... Fns>
  223. struct compose_f_<Fn0, Fns...> {
  224. template <typename... Args>
  225. using apply = invoke<
  226. quote<Fn0>,
  227. invoke<compose_f_<Fns...>, Args...>
  228. >;
  229. };
  230. // Termination specialization, finally pass the arguments
  231. template <template <typename...> class Fn0>
  232. struct compose_f_<Fn0> {
  233. template <typename ...Args>
  234. using apply = invoke<quote<Fn0>, Args...>;
  235. };
  236. template<typename ...Fns> struct compose_ {};
  237. // recursive call to all invokes
  238. template<typename Fn0, typename ...Fns>
  239. struct compose_<Fn0, Fns...> {
  240. template <typename ...Args>
  241. using apply = invoke<
  242. Fn0,
  243. invoke<compose_<Fns...>, Args...>
  244. >;
  245. };
  246. // Termination specialization, finally pass the arguments
  247. template<typename Fn0>
  248. struct compose_<Fn0> {
  249. template <typename... Args>
  250. using apply = invoke<Fn0, Args...>;
  251. };
  252. }
  253. /*!
  254. * Create an invocable from other invocables(quoted metafunctions) by composition.
  255. * \note
  256. * This implies from N invocables in \p Fns the first N-1 has to be unary.
  257. * Thats because of the "return" type of metafunction. They can only return one
  258. * type. So for n-ary invocables in the N-1 places the typelist<> is the solution.
  259. *
  260. * \code
  261. * static_assert( std::is_same<
  262. * invoke<compose<quote<F1>, quote<F2>, quote<F3>>, int>, F1<F2<F3<int>>>
  263. * >, "");
  264. * \endcode
  265. */
  266. template <typename... Fns>
  267. using compose = details::compose_<Fns...>;
  268. /*!
  269. * Create an invocable from other metafunctions by composition.
  270. * \note
  271. * This implies from N invocables in \p Fns the first N-1 has to be unary.
  272. * Thats because of the "return" type of metafunction. They can only return one
  273. * type. So for n-ary invocables in the N-1 places the typelist<> is the solution.
  274. *
  275. * \code
  276. * static_assert( std::is_same<
  277. * invoke<compose_f<F1, F2, F3>, int>, F1 <F2 <F3 <int>>>
  278. * >, "");
  279. * \endcode
  280. */
  281. template <template <typename...> class... Fns>
  282. using compose_f = details::compose_f_<Fns...>;
  283. //! @}
  284. /*!
  285. * Applies the invocable \p Fn by binding the arguments \p Ts
  286. * to the front of \p Fn.
  287. */
  288. template<typename Fn, typename... Ts>
  289. struct bind_front {
  290. template<typename... Us>
  291. using apply = invoke<Fn, Ts..., Us...>;
  292. };
  293. /*!
  294. * Applies the Invocable \p Fn by binding the arguments \p Ts
  295. * to the back of \p Fn.
  296. */
  297. template<typename Fn, typename... Ts>
  298. struct bind_back {
  299. template<typename... Us>
  300. using apply = invoke<Fn, Us..., Ts...>;
  301. };
  302. /*
  303. * ========== meta:: predicates ============
  304. */
  305. template <typename T1>
  306. struct same_as {
  307. template <typename T2>
  308. struct apply : same_<T1, T2> { };
  309. };
  310. template <typename T1>
  311. struct not_same_as {
  312. template <typename T2>
  313. struct apply : not_same_<T1, T2> { };
  314. };
  315. }}
  316. //! @}
  317. #endif /* __utl_meta_invoke_h__ */