Micro template library A library for building device drivers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

369 lines
12 KiB

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