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.
 
 
 
 

245 lines
9.4 KiB

  1. /*!
  2. * \file detection.h
  3. * \brief Detection idiom based on WG21's N4502 [\ref n4502 1] from Walter E. Brown
  4. *
  5. * \anchor n4502 [1]: www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf
  6. *
  7. * Copyright (C) 2018-2019 Christos Choutouridis
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Lesser General Public License as
  11. * published by the Free Software Foundation, either version 3
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #ifndef __utl_meta_detection_h__
  23. #define __utl_meta_detection_h__
  24. #include <utl/core/impl.h>
  25. #include <utl/meta/operations.h>
  26. #include <type_traits>
  27. /*!
  28. * \ingroup meta
  29. * \defgroup detection
  30. * Detection idiom support header.
  31. */
  32. //! @{
  33. namespace utl {
  34. namespace meta {
  35. /*!
  36. * void_t meta-function that maps a sequence of any types to the type void
  37. */
  38. //! @{
  39. #if defined(UTL_WORKAROUND_CWG_1558)
  40. template<typename... _Ts>
  41. struct void_ {
  42. using type = void;
  43. };
  44. //! void_t type alias
  45. template<typename... _Ts>
  46. using void_t = eval<void_<_Ts...>>;
  47. #else
  48. //! void_ type alias
  49. template <typename...> using void_ = void;
  50. //! void_t type alias
  51. template <typename...> using void_t = void;
  52. #endif
  53. //! @}
  54. /*!
  55. * Not a type to use in detected idiom. This type can
  56. * not be constructed, destructed or copied
  57. */
  58. struct nat_ {
  59. nat_() = delete;
  60. ~nat_() = delete;
  61. nat_(nat_ const&) = delete;
  62. void operator = (nat_ const&) = delete;
  63. };
  64. //! Detector for detection idiom
  65. //! @{
  66. namespace detail {
  67. template <typename Default,
  68. typename AlwaysVoid,
  69. template<typename...> class Op, typename... Args>
  70. struct detector {
  71. using detected = false_;
  72. using type = Default;
  73. };
  74. template <typename Default,
  75. template<typename...> class Op, typename... Args>
  76. struct detector <Default, void_t<Op<Args...>>, Op, Args...> {
  77. using detected = true_;
  78. using type = Op<Args...>;
  79. };
  80. //! helper for detected_or_t
  81. template <typename Default,
  82. template<typename...> class Op, typename... Args>
  83. using detected_or = detector<Default, void, Op, Args...>;
  84. } // namespace detail
  85. //! @}
  86. /*!
  87. * detection interface
  88. */
  89. //! @{
  90. /*!
  91. * Checks if Op<Args...> is a valid expression without evaluating it.
  92. *
  93. * \param Op a meta-callback function to pass Args...
  94. * \param Args... types to pass to Op for checking
  95. * \return status of the operation [bool_]
  96. * \arg true_ if Op<Args...> is valid expression
  97. * \arg false_ if Op<Args...> is not valid
  98. *
  99. * \example
  100. * \code
  101. * // archetypal alias for a copy assignment operation
  102. * template< class T > using copy_assign_t = decltype( declval<T&>() = declval<T const &>() );
  103. *
  104. * template< class T > using is_copy_assignable = is_detected< copy_assign_t, T >;
  105. * \endcode
  106. */
  107. template <template<typename...> class Op, typename... Args>
  108. using is_detected = typename detail::detector<nat_, void, Op, Args...>::detected;
  109. //! Detection predicate
  110. template< template<typename...> class Op, typename... Args>
  111. constexpr bool is_detected_v = is_detected<Op, Args...>::value;
  112. /*!
  113. * Detection tool that evaluates to Op<Args...> if it's valid and to nat_ if not
  114. *
  115. * \param Op metafunction detector
  116. * \param Args... The arguments to pass to \p Op and check if is well formed
  117. * \return The result type
  118. * \arg Op<Args...> if is well formed
  119. * \arg nat_ if Op<Args...> is ill formed
  120. * \example
  121. * \code
  122. * template <typename T> using try_type = typename T::type; // detector
  123. * template <typename T> using try_ppT = decltype (++(std::declval<T>())); // detector
  124. * static_assert( std::is_same<nat_, detected_t<try_type, A<int>> >(), ""); // detection failed
  125. * static_assert( std::is_same<A<int>&, detected_t<try_ppT, A<int>> >(), ""); // detection succeed
  126. *
  127. * // if mFun<int, int> is well formed
  128. * static_assert(std::is_same< mFun<int, int>, detected_t<mFun, int, int> >(), "");
  129. * \endcode
  130. */
  131. template <template<typename...> class Op, typename... Args>
  132. using detected_t = eval <
  133. detail::detector<nat_, void, Op, Args...>
  134. >;
  135. /*!
  136. * Detection tool that evaluates to Op<Args...> if it's valid and to \p Default if not
  137. *
  138. * \param Default The resulting type if detection fail
  139. * \param Op metafunction detector
  140. * \param Args... The arguments to pass to \p Op and check if is well formed
  141. * \return The result type
  142. * \arg Op<Args...> if is well formed
  143. * \arg Default if Op<Args...> is ill formed
  144. * \example
  145. * \code
  146. * template <typename T> using try_type = typename T::type; // detector
  147. * template <typename T> using try_ppT = decltype (++(std::declval<T>())); // detector
  148. * static_assert( std::is_same<Foo, detected_or_t<Foo, try_type, A<int>> >(), ""); // detection failed
  149. * static_assert( std::is_same<A<int>&, detected_or_t<Foo, try_ppT, A<int>> >(), ""); // detection succeed
  150. *
  151. * // if mFun<int, int> is well formed
  152. * static_assert(std::is_same< mFun<int, int>, detected_or_t<void, mFun, int, int> >(), "");
  153. * \endcode
  154. */
  155. template <typename Default,
  156. template<typename...> class Op, typename... Args>
  157. using detected_or_t = eval <
  158. detail::detected_or<Default, Op, Args...>
  159. >;
  160. /*!
  161. * Detection tool that evaluates to true_ if evaluation of Op<Args...>
  162. * is \p Expected and to false_ if not
  163. *
  164. * \param Expected The expected resulting type if detection succeed
  165. * \param Op metafunction detector
  166. * \param Args... The arguments to pass to \p Op and check if is well formed
  167. * \return The result type
  168. * \arg true_ if Op<Args...> is well formed and evaluate to Expected
  169. * \arg false_ Any other case
  170. * \example
  171. * \code
  172. * template <typename T> using try_type = typename T::type; // detector
  173. * template <typename T> using try_ppT = decltype (++(std::declval<T>())); // detector
  174. * static_assert( std::is_same<false_, is_detected_exact<int, try_type, A<int>> >(), ""); // detection failed
  175. * static_assert( std::is_same<true_, is_detected_exact<A<int>&, try_ppT, A<int>> >(), ""); // detection succeed
  176. *
  177. * // if mFun<int, int> is well formed
  178. * static_assert(std::is_same< true_, is_detected_exact<mFun<int, int>, mFun, int, int> >(), "");
  179. * \endcode
  180. */
  181. template <typename Expected,
  182. template<typename...> class Op, typename... Args >
  183. using is_detected_exact = eval <
  184. same_<Expected, detected_t<Op, Args...>>
  185. >;
  186. //! evaluates to true if evaluation of Op<Args...> is \p Expected and to false if not
  187. template <typename Expected,
  188. template<typename...> class Op, typename... Args >
  189. constexpr bool is_detected_exact_v = is_detected_exact< Expected, Op, Args...>::value;
  190. /*!
  191. * Detection tool that evaluates to true_ if evaluation of Op<Args...> is convertible
  192. * to \p To and to false_ if not
  193. *
  194. * \param To The to convert to if detection succeed
  195. * \param Op metafunction detector
  196. * \param Args... The arguments to pass to \p Op and check if is well formed
  197. * \return The result type
  198. * \arg true_ if Op<Args...> is well formed and convertible to To
  199. * \arg false_ Any other case
  200. * \example
  201. * \code
  202. * template <typename T> using try_type = typename T::type; // detector
  203. * template <typename T> using try_ppT = decltype (++(std::declval<T>())); // detector
  204. * static_assert( std::is_same<false_, is_detected_convertible<Foo, try_type, A<int>> >(), ""); // detection failed
  205. * static_assert( std::is_same<true_, is_detected_convertible<A<int>&&, try_ppT, A<int>> >(), "");// detection succeed
  206. *
  207. * // if mFun<int, int> is well formed but not convertible to Foo
  208. * static_assert(std::is_same< false_, is_detected_convertible<Foo, mFun, int, int> >(), "");
  209. * \endcode
  210. */
  211. template <typename To,
  212. template<typename...> class Op, typename... Args >
  213. using is_detected_convertible = eval <
  214. std::is_convertible< detected_t<Op, Args...>, To >
  215. >;
  216. //! evaluates to true if evaluation of Op<Args...> is convertible to \p To
  217. //! and to false if not
  218. template <typename To,
  219. template<typename...> class Op, typename... Args >
  220. constexpr bool is_detected_convertible_v =
  221. is_detected_convertible<To, Op, Args...>::value;
  222. //! @}
  223. }}
  224. //!@}
  225. #endif /* __utl_meta_detection_h__ */