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.
 
 
 
 

228 lines
7.5 KiB

  1. /*!
  2. * \file utl/utility/invoke.h
  3. * \brief invoke() and invoke traits implementation
  4. */
  5. #ifndef __utl_utility_invoke_h__
  6. #define __utl_utility_invoke_h__
  7. #include <utl/core/impl.h>
  8. #include <utl/meta/meta.h>
  9. #include <type_traits>
  10. #include <functional>
  11. #include <utility>
  12. //! \defgroup utility Utility
  13. /*!
  14. * \ingroup utility
  15. * \defgroup util_invoke Invoke
  16. */
  17. //! @{
  18. namespace utl {
  19. #if !defined __cpp_lib_is_invocable
  20. namespace detail {
  21. template <class T>
  22. struct is_ref_wrapper : meta::false_ {};
  23. template <class U>
  24. struct is_ref_wrapper<std::reference_wrapper<U>> : meta::true_ {};
  25. // 1
  26. template <class T, class Type, class T1, class... Args,
  27. meta::enable_if_t<
  28. std::is_member_function_pointer<std::decay_t<Type T::*>>::value &&
  29. std::is_base_of<T, std::decay_t<T1>>::value,
  30. int> =0
  31. >
  32. decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
  33. return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);
  34. }
  35. // 2
  36. template <class T, class Type, class T1, class... Args,
  37. meta::enable_if_t<
  38. std::is_member_function_pointer<std::decay_t<Type T::*>>::value &&
  39. is_ref_wrapper<std::decay_t<T1>>::value,
  40. int> =0
  41. >
  42. decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
  43. return (t1.get().*f)(std::forward<Args>(args)...);
  44. }
  45. // 3
  46. template <class T, class Type, class T1, class... Args,
  47. meta::enable_if_t<
  48. std::is_member_function_pointer<std::decay_t<Type T::*>>::value &&
  49. !std::is_base_of<T, std::decay_t<T1>>::value &&
  50. !is_ref_wrapper<std::decay_t<T1>>::value,
  51. int> =0
  52. >
  53. decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
  54. return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
  55. }
  56. // 4
  57. template <class T, class Type, class T1, class... Args,
  58. meta::enable_if_t<
  59. std::is_member_object_pointer<std::decay_t<Type T::*>>::value &&
  60. std::is_base_of<T, std::decay_t<T1>>::value,
  61. int> =0
  62. >
  63. decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
  64. return std::forward<T1>(t1).*f;
  65. }
  66. // 5
  67. template <class T, class Type, class T1, class... Args,
  68. meta::enable_if_t<
  69. std::is_member_object_pointer<std::decay_t<Type T::*>>::value &&
  70. is_ref_wrapper<std::decay_t<T1>>::value,
  71. int> =0
  72. >
  73. decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
  74. return t1.get().*f;
  75. }
  76. // 6
  77. template <class T, class Type, class T1, class... Args,
  78. meta::enable_if_t<
  79. std::is_member_object_pointer<std::decay_t<Type T::*>>::value &&
  80. !std::is_base_of<T, std::decay_t<T1>>::value &&
  81. !is_ref_wrapper<std::decay_t<T1>>::value,
  82. int> =0
  83. >
  84. decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
  85. return (*std::forward<T1>(t1)).*f;
  86. }
  87. template <class F, class... Args>
  88. decltype(auto) invoke_impl_(F&& f, Args&&... args) {
  89. return std::forward<F>(f)(std::forward<Args>(args)...);
  90. }
  91. } // namespace detail
  92. //! Invoke the Callable object \c fn with the parameters args.
  93. //! As by INVOKE(std::forward<F>(f), std::forward<Args>(args)...).
  94. //!
  95. //! \note
  96. //! This implementation fills the lack of an invoke() utility for builds
  97. //! pre-c++17.
  98. //!
  99. //! \param fn Callable object to be invoked
  100. //! \param args Arguments to pass to \c fn
  101. //! \return The return of the Callable underling functionality
  102. //!
  103. template<typename Callable, typename... Args>
  104. inline decltype(auto) invoke(Callable&& fn, Args&&... args) {
  105. return detail::invoke_impl_(
  106. std::forward<Callable>(fn), std::forward<Args>(args)...
  107. );
  108. }
  109. //!
  110. //! \brief
  111. //! Determines whether \c F can be invoked with the arguments \c Args....
  112. //!
  113. //! Formally, determines whether invoke(declval<Fn>(), declval<ArgTypes>()...)
  114. //! is well formed when treated as an unevaluated operand,
  115. //! where invoke is \c Callable.
  116. //!
  117. //! \note
  118. //! This implementation fills the lack of an invoke() utility for builds
  119. //! pre-c++17.
  120. //!
  121. //! \tparam F The candidate type to check if its invocable
  122. //! \tparam Args The arguments for the call
  123. //! \return If \c F is invocable
  124. //! \arg true Is invocable
  125. //! \arg false Is not invocable
  126. template <typename F, typename... Args>
  127. struct is_invocable :
  128. std::is_constructible<
  129. std::function<void(Args ...)>,
  130. std::reference_wrapper<typename std::remove_reference<F>::type>
  131. > { };
  132. //! \brief
  133. //! Determines whether \c F can be invoked with the arguments \c Args...
  134. //! to yield a result that is convertible to \c R.
  135. //!
  136. //! Formally, determines whether invoke(declval<Fn>(), declval<ArgTypes>()...)
  137. //! is well formed when treated as an unevaluated operand, where invoke is \c Callable.
  138. //!
  139. //! \tparam R The return type of invocable functionality
  140. //! \tparam F The candidate type to check if its invocable
  141. //! \tparam Args The arguments to pass to \c F
  142. //! \return If \c F is invocable
  143. //! \arg true Is invocable
  144. //! \arg false Is not invocable
  145. template <typename R, typename F, typename... Args>
  146. struct is_invocable_r :
  147. std::is_constructible<
  148. std::function<R(Args ...)>,
  149. std::reference_wrapper<typename std::remove_reference<F>::type>
  150. > { };
  151. /*!
  152. * invoke_result (SFINAE friendly)
  153. */
  154. //! @{
  155. namespace detail {
  156. template<typename Callable, typename... Args>
  157. struct try_invoke {
  158. using type = decltype (
  159. detail::invoke_impl_(std::declval<Callable&&>(), std::declval<Args&&>()...)
  160. );
  161. };
  162. template<bool B, typename Callable, typename... Args>
  163. struct invoke_result_ {
  164. using type = meta::nil_;
  165. };
  166. template <typename Callable, typename... Args>
  167. struct invoke_result_ <true, Callable, Args...> {
  168. using type = meta::invoke_t<
  169. meta::quote<try_invoke>, Callable, Args...
  170. >;
  171. };
  172. }
  173. //! trait that deduces the return type of an INVOKE expression at compile time.
  174. //!
  175. //! \tparam Callable The candidate type to check if its invocable
  176. //! \tparam Args The arguments to pass to \c F
  177. //!
  178. //! \b member \n
  179. //! \::type The return type of the \c Callable type if invoked with the arguments Args....
  180. template <typename Callable, typename... Args>
  181. using invoke_result = detail::invoke_result_<
  182. is_invocable<Callable, Args...>::value,
  183. Callable,
  184. Args...
  185. >;
  186. //! trait that deduces the return type of an INVOKE expression at compile time.
  187. //!
  188. //! \tparam Callable The candidate type to check if its invocable
  189. //! \tparam Args The arguments to pass to \c F
  190. //!
  191. //! \return The type of the \c Callable type if invoked with the arguments Args....
  192. template<typename Callable, typename... Args>
  193. using invoke_result_t = meta::eval <
  194. invoke_result<Callable, Args...>
  195. >;
  196. //! @}
  197. #else
  198. #endif
  199. }
  200. //! @}
  201. #endif /* __utl_utility_invoke_h__ */