|
- /*!
- * \file utl/meta/invoke.h
- * \brief Template meta-programming utilities for callables
- */
- #ifndef __utl_meta_invoke_h__
- #define __utl_meta_invoke_h__
-
- #include <utl/core/impl.h>
- #include <utl/meta/basic.h>
- #include <utl/meta/detection.h>
-
- /*!
- * \ingroup meta
- * \defgroup meta_invoke Invoke
- * A meta-programming invoke() analogous.
- *
- * This module provides <b>higher order</b> tools to meta. utl::meta's metafunctions inputs are types.
- * The metafunctions though are templates in the form of `template <typename...> class`.
- * So we can not pass metafunctions to other metafunctions. The utl::meta provides tools to wrap metafunctions
- * in a type. This way we create the concepts of:
- *
- * - <b>\c invocable</b> which is a type containing a metafunction inside.
- * - <b>\c evaluation</b> which is the way of unwrapping the metafunction inside the invocable type.
- *
- * In order to accomplish that, by convention, a \c meta::invocable shall contain a nested
- * template type named \c apply which is bind to actual invocable meta-function. Then we can:
- *
- * - Use \c wrap<> or even better \c quote<> in order to wrap a metafunction to a type (metafunction class)
- * - Pass these wrapped types to other metafunctions
- * - \c invoke<> the inner \c apply from a wrapped metafunction class.
- */
- //! @{
- namespace utl {
- namespace meta{
-
- /*!
- * \name identity implementation
- */
- //! @{
-
- //! Identity is a metafunction always return the input type
- template <typename Tp>
- struct identity {
- #if defined (UTL_WORKAROUND_CWG_1558)
- // redirect unused Ts... via void_t
- template <typename... Ts>
- using apply = first_of<Tp, void_t<Ts...>>; //!< identity is invokable, must also have apply
- #else
- template <typename...>
- using apply = Tp; //!< identity is invokable, must also have apply
- #endif
- using type = Tp; //!< identity
- };
-
- //! identity type alias
- template <typename Tp>
- using identity_t = eval<identity<Tp>>;
- //! @}
-
- /*!
- * \name invoke, invoke_t
- */
- //! @{
- /*!
- * Invoke the nested apply meta-function from \c Fn with the arguments \c Args.
- * \note
- * This is an analogous to the std::invoke()
- */
- template <typename Fn, typename... Args>
- using invoke = typename Fn::template apply<Args...>;
-
- /*!
- * Evaluate the invocation of the nested apply metafunction from \p Fn
- * with the arguments \p Args.
- */
- template <typename Fn, typename... Args>
- using invoke_t = eval< invoke <Fn, Args...>>;
- //! @}
-
- //! \name wrap
- //! @{
-
- /*!
- * \brief
- * Wraps an n-ary Metafunction \c F to a Metafunction Class
- *
- * wrap is a higher-order primitive that wraps an n-ary Metafunction
- * to create a corresponding Metafunction Class (Invocable). This way
- * we can pass Metafunctions as types to other metafunctions and let
- * them \c invoke the inner templated apply.
- *
- * \tparam F The metafunction to wrap
- */
- template <template <typename...> class F>
- struct wrap {
- template <typename... Args>
- using apply = F<Args...>;
- };
-
- /*!
- * \brief
- * Wraps an n-ary Metafunction \c F taking literal constants of type \c T
- * to a Metafunction Class
- *
- * wrap_i is a higher-order primitive that wraps an n-ary Metafunction
- * to create a corresponding Metafunction Class (Invocable).
- * This way we can pass Metafunctions as types to other metafunctions and let
- * them \c invoke the inner templated apply.
- *
- * \tparam T Type of integral constants
- * \tparam F The metafunction to wrap
- */
- template <typename T, template <T...> class F>
- struct wrap_i {
- // requires meta::Integral
- template <typename... Ts>
- using apply = F<Ts::type::value...>;
- };
- //! @}
-
- //! \name Is applicable trait
- //! @{
- namespace details {
-
- template<template<typename...> class F, typename... T>
- struct is_applicable_ {
- template<template<typename...> class G, typename = G<T...>>
- static true_ check (int); //< T.. can be passed to G
- template<template<typename...> class>
- static false_ check (...); //< all other combinations
-
- using type = decltype(check<F>(0));
- };
-
- template<typename F, typename... T>
- struct is_applicable_q_ {
- template<typename G, typename Ret = invoke_t<G, T...>>
- static Ret check (int); //< T.. can be passed to G
- template<typename...>
- static nil_ check (...); //< all other combinations
-
- using type = if_ <
- not_same<
- nil_,
- decltype(check<F>(0))
- >, true_, false_
- >;
- //!\note
- //! check doesn't return \c true_ or \c false_. The reason is that the \p F is
- //! probably quoted/deferred. This implies an embedded is_applicable<> check
- //! inside defer<> and so its guaranteed to be well formed.
- //! Thus we return the actual evaluated type and make the check for nil_
- };
-
- template<typename T, template <T...> class F, T... Is>
- struct is_applicable_i_ {
- template<typename TT, template<TT...> class G, typename = G<Is...>>
- static true_ check (int); //< Is... can be passed to G
- template<typename TT, template<TT...> class G>
- static false_ check (...); //< all other combinations
-
- using type = decltype(check<T, F>(0));
- };
- }
-
- /*!
- * \brief
- * Check if we can instantiate metafunction \c F with parameters \p Ts
- *
- * \tparam F The metafunction
- * \tparam Ts The parameters to \c F
- */
- template<template<typename...> class F, typename... Ts>
- using is_applicable_t = eval<
- details::is_applicable_<F, Ts...>
- >;
-
-
- /*!
- * \brief
- * Check if we can invoke the quoted metafunction \c Q with parameters \p Ts
- *
- * \tparam Q The quoted metafunction
- * \tparam Ts The parameters to \c Q
- *
- * \sa utl::meta::quote
- */
- template<typename Q, typename... Ts>
- using is_applicable_qt = eval <
- details::is_applicable_q_ <Q, Ts...>
- >;
-
- /*!
- * \brief
- * Check if we can instantiate metafunction \c F with parameters \p Is of type \c T
- *
- * \tparam T The type of \c Is
- * \tparam F The metafunction
- * \tparam Is The parameters to \c F
- */
- template <typename T, template<T...> class F, T... Is>
- using is_applicable_it = eval<
- details::is_applicable_i_<T, F, Is...>
- >;
-
- //! @}
-
- //! \name defer
- //! @{
- namespace details {
- template<template<typename...> class F, typename... Ts>
- struct defer_ {
- using type = F<Ts...>;
- };
-
- template<typename T, template<T...> class F, T... Is>
- struct defer_i_ {
- using type = F<Is...>;
- };
-
- //! \note
- //! We use struct instead of:
- //! template<template<typename...> class F, typename... Ts>
- //! using defer_ = F<Ts...>;
- //!
- //! The use of struct here is due to Core issue 1430 [1] and is used
- //! as suggested by Roy Crihfield in [2].
- //! In short, this is due to language's inability to expand Ts... into
- //! a fixed parameter list of an alias template.
- //!
- //! [1] https://wg21.link/cwg1430\n
- //! [2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59498
- }
-
- /*!
- * \brief
- * Postpone the instantiation of a metafunction \c F with parameters \c Ts
- *
- * This metafunction first checks if the given arguments \c Ts are applicable to
- * the metafunction \c F and if so nest inside (a layer deeper) the <em>metafunction call</em>.
- *
- * \tparam F The metafunction
- * \tparam Ts The parameters (arguments) to \c F
- */
- template<template<class...> class F, class... Ts>
- using defer = if_<
- details::is_applicable_<F, Ts...>,
- details::defer_<F, Ts...>,
- nil_ //!< Safe, nil_ is dereferencable
- >;
-
- /*!
- * \brief
- * Postpone the instantiation of a metafunction \c F with parameters \c Is of type \c T.
- *
- * This metafunction first checks if the given arguments \c Is of type \c T are applicable to
- * the metafunction \c F and if so nest inside (a layer deeper) the <em>metafunction call</em>.
- *
- * \tparam T The type of parameters
- * \tparam F The metafunction
- * \tparam Is The parameters (arguments) to \c F
- */
- template <typename T, template<T...> class F, T... Is>
- using defer_i = if_ <
- details::is_applicable_i_<T, F, Is...>,
- details::defer_i_<T, F, Is...>,
- nil_ //!< Safe, nil_ is dereferencable
- >;
- //! @}
-
- //! \name quote
- //! @{
- /*!
- * \brief
- * Wraps an n-ary Metafunction \c F to a Metafunction Class using meta::defer<>
- *
- * quote is a higher-order primitive that wraps an n-ary Metafunction
- * to create a corresponding Metafunction Class (Invocable) using defer<> to
- * postpone the evaluation of Metafunction. This is a safe version of \c wrap<>.\n
- * Again this way we can pass Metafunctions as types to other metafunctions and let
- * them \c invoke the inner templated apply
- *
- * \tparam F The metafunction to wrap
- */
- template <template <typename...> class F>
- struct quote {
- template <typename... Args>
- using apply = eval<
- defer<F, Args...> //!< defer here to avoid DR1430
- >;
- };
-
-
- /*!
- * \brief
- * Wraps an n-ary Metafunction \c F taking literal constants of type \c T
- * to a Metafunction Class using meta::defer<>
- *
- * quote is a higher-order primitive that wraps an n-ary Metafunction
- * to create a corresponding Metafunction Class (Invocable) using defer<> to
- * postpone the evaluation of Metafunction. This is a safe version of \c wrap<>.\n
- * Again this way we can pass Metafunctions as types to other metafunctions and let
- * them \c invoke the inner templated apply
- *
- * \tparam T Type of integral constants
- * \tparam F The metafunction to wrap
- */
- template <typename T, template <T...> class F>
- struct quote_i {
- // requires meta::Integral
- template <typename... Ts>
- using apply = eval<
- defer_i<T, F, Ts::type::value...> //!< defer here to avoid DR1430
- >;
- };
- //! @}
-
- //! \name compose
- //! @{
- namespace details {
- template <template <typename...> class... Fns> struct compose_f_ {};
-
- // recursive call to all invokes
- template <template <typename...> class Fn0,
- template <typename...> class... Fns>
- struct compose_f_<Fn0, Fns...> {
- template <typename... Args>
- using apply = invoke<
- quote<Fn0>,
- invoke<compose_f_<Fns...>, Args...>
- >;
- };
- // Termination specialization, finally pass the arguments
- template <template <typename...> class Fn0>
- struct compose_f_<Fn0> {
- template <typename ...Args>
- using apply = invoke<quote<Fn0>, Args...>;
- };
-
-
- template<typename ...Fns> struct compose_ {};
-
- // recursive call to all invokes
- template<typename Fn0, typename ...Fns>
- struct compose_<Fn0, Fns...> {
- template <typename ...Args>
- using apply = invoke<
- Fn0,
- invoke<compose_<Fns...>, Args...>
- >;
- };
- // Termination specialization, finally pass the arguments
- template<typename Fn0>
- struct compose_<Fn0> {
- template <typename... Args>
- using apply = invoke<Fn0, Args...>;
- };
- }
-
- /*!
- * \brief
- * Create an invocable from other invocables(quoted metafunctions) by composition.
- * \note
- * This implies from N invocables in \p Fns the first N-1 has to be unary.
- * Thats because of the "return" type of metafunction. They can only return one
- * type. So for n-ary invocables in the N-1 places the typelist<> is the solution.
- *
- * \code
- * static_assert( std::is_same<
- * invoke<compose<quote<F1>, quote<F2>, quote<F3>>, int>, F1<F2<F3<int>>>
- * >, "");
- * \endcode
- */
- template <typename... Fns>
- using compose = details::compose_<Fns...>;
-
- /*!
- * \brief
- * Create an invocable from other metafunctions by composition.
- * \note
- * This implies from N invocables in \p Fns the first N-1 has to be unary.
- * Thats because of the "return" type of metafunction. They can only return one
- * type. So for n-ary invocables in the N-1 places the typelist<> is the solution.
- *
- * \code
- * static_assert( std::is_same<
- * invoke<compose_f<F1, F2, F3>, int>, F1 <F2 <F3 <int>>>
- * >, "");
- * \endcode
- */
- template <template <typename...> class... Fns>
- using compose_f = details::compose_f_<Fns...>;
- //! @}
-
- /*!
- * \brief
- * Applies the invocable \p Fn by binding the arguments \p Ts to the front of \p Fn.
- */
- template<typename Fn, typename... Ts>
- struct bind_front {
- template<typename... Us>
- using apply = invoke<Fn, Ts..., Us...>;
- };
-
- /*!
- * \brief
- * Applies the Invocable \p Fn by binding the arguments \p Ts to the back of \p Fn.
- */
- template<typename Fn, typename... Ts>
- struct bind_back {
- template<typename... Us>
- using apply = invoke<Fn, Us..., Ts...>;
- };
-
-
- /*
- * ========== meta:: predicates ============
- */
- template <typename T1>
- struct same_as {
- template <typename T2>
- struct apply : same<T1, T2> { };
- };
-
- template <typename T1>
- struct not_same_as {
- template <typename T2>
- struct apply : not_same<T1, T2> { };
- };
-
- }}
-
- //! @}
-
- #endif /* __utl_meta_invoke_h__ */
|