/*! * \file detection.h * \brief Detection idiom based on WG21's N4502 from Walter E. Brown */ #ifndef __utl_meta_detection_h__ #define __utl_meta_detection_h__ #include #include #include /*! * \ingroup meta * \defgroup detection Detection * Detection idiom support header. */ //! @{ namespace utl { namespace meta { /*! * \name void_t implementation */ //! @{ #if defined(UTL_WORKAROUND_CWG_1558) template struct void_ { using type = void; }; //! void_t type alias template using void_t = eval>; #else //! void_ meta-function that maps a sequence of any types to the type void template using void_ = void; //! void_t meta-function that maps a sequence of any types to the type void template using void_t = void; #endif //! @} /*! * \brief * Not a type to use in detected idiom. * * This type can not be constructed, destructed or copied. */ struct nat_ { nat_() = delete; ~nat_() = delete; nat_(nat_ const&) = delete; void operator = (nat_ const&) = delete; }; //! \name Detector for detection idiom //! @{ namespace details { template class Op, typename... Args> struct detector { using detected = false_; using type = Default; }; template class Op, typename... Args> struct detector >, Op, Args...> { using detected = true_; using type = Op; }; //! helper for detected_or_t template class Op, typename... Args> using detected_or = detector; } // namespace details //! @} /*! * \name detection interface */ //! @{ /*! * Checks if Op is a valid expression without evaluating it. * * \tparam Op a meta-callback function to pass Args... * \tparam Args... types to pass to Op for checking * \return status of the operation [bool_] * \arg true_ if Op is valid expression * \arg false_ if Op is not valid * * \code * // archetypal alias for a copy assignment operation * template< class T > using copy_assign_t = decltype( declval() = declval() ); * * template< class T > using is_copy_assignable = is_detected< copy_assign_t, T >; * \endcode */ template class Op, typename... Args> using is_detected = typename details::detector::detected; //! Detection predicate template< template class Op, typename... Args> constexpr bool is_detected_v = is_detected::value; /*! * Detection tool that evaluates to Op if it's valid and to nat_ if not * * \tparam Op metafunction detector * \tparam Args... The arguments to pass to \p Op and check if is well formed * \return The result type * \arg Op if is well formed * \arg nat_ if Op is ill formed * * \code * template using try_type = typename T::type; // detector * template using try_ppT = decltype (++(std::declval())); // detector * static_assert( std::is_same> >(), ""); // detection failed * static_assert( std::is_same&, detected_t> >(), ""); // detection succeed * * // if mFun is well formed * static_assert(std::is_same< mFun, detected_t >(), ""); * \endcode */ template class Op, typename... Args> using detected_t = eval < details::detector >; /*! * Detection tool that evaluates to Op if it's valid and to \p Default if not * * \tparam Default The resulting type if detection fail * \tparam Op metafunction detector * \tparam Args... The arguments to pass to \p Op and check if is well formed * \return The result type * \arg Op if is well formed * \arg Default if Op is ill formed * * \code * template using try_type = typename T::type; // detector * template using try_ppT = decltype (++(std::declval())); // detector * static_assert( std::is_same> >(), ""); // detection failed * static_assert( std::is_same&, detected_or_t> >(), ""); // detection succeed * * // if mFun is well formed * static_assert(std::is_same< mFun, detected_or_t >(), ""); * \endcode */ template class Op, typename... Args> using detected_or_t = eval < details::detected_or >; /*! * Detection tool that evaluates to true_ if evaluation of Op * is \p Expected and to false_ if not * * \tparam Expected The expected resulting type if detection succeed * \tparam Op metafunction detector * \tparam Args... The arguments to pass to \p Op and check if is well formed * \return The result type * \arg true_ if Op is well formed and evaluate to Expected * \arg false_ Any other case * * \code * template using try_type = typename T::type; // detector * template using try_ppT = decltype (++(std::declval())); // detector * static_assert( std::is_same> >(), ""); // detection failed * static_assert( std::is_same&, try_ppT, A> >(), ""); // detection succeed * * // if mFun is well formed * static_assert(std::is_same< true_, is_detected_exact, mFun, int, int> >(), ""); * \endcode */ template class Op, typename... Args > using is_detected_exact = eval < same> >; //! evaluates to true if evaluation of Op is \p Expected and to false if not template class Op, typename... Args > constexpr bool is_detected_exact_v = is_detected_exact< Expected, Op, Args...>::value; /*! * Detection tool that evaluates to true_ if evaluation of Op is convertible * to \p To and to false_ if not * * \tparam To The to convert to if detection succeed * \tparam Op metafunction detector * \tparam Args... The arguments to pass to \p Op and check if is well formed * \return The result type * \arg true_ if Op is well formed and convertible to To * \arg false_ Any other case * * \code * template using try_type = typename T::type; // detector * template using try_ppT = decltype (++(std::declval())); // detector * static_assert( std::is_same> >(), ""); // detection failed * static_assert( std::is_same&&, try_ppT, A> >(), "");// detection succeed * * // if mFun is well formed but not convertible to Foo * static_assert(std::is_same< false_, is_detected_convertible >(), ""); * \endcode */ template class Op, typename... Args > using is_detected_convertible = eval < std::is_convertible< detected_t, To > >; //! evaluates to true if evaluation of Op is convertible to \p To //! and to false if not template class Op, typename... Args > constexpr bool is_detected_convertible_v = is_detected_convertible::value; //! @} }} //!@} #endif /* __utl_meta_detection_h__ */