¿Es posible calcular el tipo de parámetro y el tipo de retorno de una lambda?


Dada una lambda, ¿es posible averiguar su tipo de parámetro y tipo de retorno? Si es así, ¿cómo?

Básicamente, quiero lambda_traits que se puede usar de las siguientes maneras:

auto lambda = [](int i) { return long(i*10); };

lambda_traits<decltype(lambda)>::param_type  i; //i should be int
lambda_traits<decltype(lambda)>::return_type l; //l should be long

La motivación detrás es que quiero usar lambda_traits en una plantilla de función que acepte un lambda como argumento, y necesito saber su tipo de parámetro y tipo de retorno dentro de la función:

template<typename TLambda>
void f(TLambda lambda)
{
   typedef typename lambda_traits<TLambda>::param_type  P;
   typedef typename lambda_traits<TLambda>::return_type R;

   std::function<R(P)> fun = lambda; //I want to do this!
   //...
}

Por el momento, podemos asumir que la lambda toma exactamente un argumento.

Inicialmente, I intentó trabajar con std::function como:

template<typename T>
A<T> f(std::function<bool(T)> fun)
{
   return A<T>(fun);
}

f([](int){return true;}); //error

Pero obviamente daría error (ideone). Así que lo cambié a la versión TLambda de la plantilla de función y quiero construir el objeto std::function dentro de la función (como se muestra arriba).

Author: Nawaz, 2011-10-30

4 answers

Gracioso, acabo de escribir un function_traits implementation based on Especialización de una plantilla en una lambda en C++0x que puede dar los tipos de parámetros. El truco, como se describe en la respuesta a esa pregunta, es usar el decltype de la lambda operator().

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
    enum { arity = sizeof...(Args) };
    // arity is the number of arguments.

    typedef ReturnType result_type;

    template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        // the i-th argument is equivalent to the i-th tuple element of a tuple
        // composed of those arguments.
    };
};

// test code below:
int main()
{
    auto lambda = [](int i) { return long(i*10); };

    typedef function_traits<decltype(lambda)> traits;

    static_assert(std::is_same<long, traits::result_type>::value, "err");
    static_assert(std::is_same<int, traits::arg<0>::type>::value, "err");

    return 0;
}

Tenga en cuenta que esta solución no funciona para lambda genérico como [](auto x) {}.

 145
Author: kennytm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-16 13:20:33

Aunque no estoy seguro de que esto se ajuste estrictamente al estándar, ideone compiló el siguiente código:

template< class > struct mem_type;

template< class C, class T > struct mem_type< T C::* > {
  typedef T type;
};

template< class T > struct lambda_func_type {
  typedef typename mem_type< decltype( &T::operator() ) >::type type;
};

int main() {
  auto l = [](int i) { return long(i); };
  typedef lambda_func_type< decltype(l) >::type T;
  static_assert( std::is_same< T, long( int )const >::value, "" );
}

Sin embargo, esto proporciona solo el tipo de función, por lo que el resultado y el parámetro los tipos tienen que ser extraídos de él. Si se puede utilizar boost::function_traits, result_type y arg1_type cumplirá el propósito. Dado que ideone parece no proporcionar impulso en el modo C++11, no pude publicar el código real, lo siento.

 11
Author: Ise Wisteria,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-30 07:17:40

El método de especialización que se muestra en la respuesta @ KennyTM se puede extender para cubrir todos los casos, incluyendo lambdas variádicas y mutables:

template <typename T>
struct closure_traits : closure_traits<decltype(&T::operator())> {};

#define REM_CTOR(...) __VA_ARGS__
#define SPEC(cv, var, is_var)                                              \
template <typename C, typename R, typename... Args>                        \
struct closure_traits<R (C::*) (Args... REM_CTOR var) cv>                  \
{                                                                          \
    using arity = std::integral_constant<std::size_t, sizeof...(Args) >;   \
    using is_variadic = std::integral_constant<bool, is_var>;              \
    using is_const    = std::is_const<int cv>;                             \
                                                                           \
    using result_type = R;                                                 \
                                                                           \
    template <std::size_t i>                                               \
    using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; \
};

SPEC(const, (,...), 1)
SPEC(const, (), 0)
SPEC(, (,...), 1)
SPEC(, (), 0)

Demo.

Tenga en cuenta que la aridad no se ajusta para variádicas operator() s. En su lugar, también se puede considerar is_variadic.

 6
Author: Columbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-29 11:55:02

La respuesta proporcionada por @KennyTM funciona muy bien, sin embargo, si una lambda no tiene parámetros, utilizando el índice arg no se compila. Si alguien más estaba teniendo este problema, tengo una solución simple (más simple que usar soluciones relacionadas con SFINAE, es decir).

Simplemente agregue void al final de la tupla en la estructura arg después de los tipos de argumentos variádicos. es decir,

template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type;
    };

Dado que el arity no depende del número real de parámetros de plantilla, el real no será incorrecto, y si es 0, entonces al menos arg seguirá existiendo y puedes hacer con él lo que quieras. Si ya planea no exceder el índice arg<arity-1>, entonces no debería interferir con su implementación actual.

 1
Author: Jon Koelzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-10 10:28:35