By Nawaz


2011-10-30 05:47:17 8 Comments

Given a lambda, is it possible to figure out it's parameter type and return type? If yes, how?

Basically, I want lambda_traits which can be used in following ways:

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

The motivation behind is that I want to use lambda_traits in a function template which accepts a lambda as argument, and I need to know it's parameter type and return type inside the function:

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!
   //...
}

For the time being, we can assume that the lambda takes exactly one argument.

Initially, I tried to work with std::function as:

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

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

But it obviously would give error. So I changed it to TLambda version of the function template and want to construct the std::function object inside the function (as shown above).

4 comments

@Jon Koelzer 2018-05-10 10:28:35

The answer provided by @KennyTMs works great, however if a lambda has no parameters, using the index arg<0> does not compile. If anyone else was having this problem, I have a simple solution (simpler than using SFINAE related solutions, that is).

Just add void to the end of the tuple in the arg struct after the variadic argument types. i.e.

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

since the arity isn't dependent on the actual number of template parameters, the actual won't be incorrect, and if it's 0 then at least arg<0> will still exist and you can do with it what you will. If you already plan to not exceed the index arg<arity-1> then it shouldn't interfere with your current implementation.

@kennytm 2011-10-30 07:27:53

Funny, I've just written a function_traits implementation based on Specializing a template on a lambda in C++0x which can give the parameter types. The trick, as described in the answer in that question, is to use the decltype of the lambda's 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;
}

Note that this solution does not work for generic lambda like [](auto x) {}.

@GManNickG 2011-10-30 07:30:14

Heh, I was just writing this. Didn't think about tuple_element though, thanks.

@Nawaz 2011-10-30 07:33:13

@GMan: If your approach is not exactly same as this, please post it then. I'm going to test this solution.

@GManNickG 2011-10-30 08:29:58

@Nawaz: The utility Kenny linked to is nearly identical to my own utility I have for my projects. (I just have to Boost.PP iterate since I'm stuck in MSVC C++0x support.) The only thing is I wrote a get_nth_element meta-function, instead of (ab)using tuple_element.

@Luc Danton 2011-10-30 10:14:50

A complete trait would also use a specialization for non-const, for those lambda declared mutable ([]() mutable -> T { ... }).

@cdiggins 2014-01-13 01:29:00

Try the function_traits specialization first and then #include <random>. The compiler goes bonkers.

@Aaron McDaid 2014-09-03 22:50:51

It might also be useful to include typedef ReturnType(simple_function_type)(Args...) into the function_traits class. And also, perhaps typedef std::function<simple_function_type> std_function_type;

@e271p314 2018-01-03 13:49:37

A follow up question here stackoverflow.com/questions/48078718/…

@Andry 2018-01-06 21:15:12

This does not work in case where a lambda has auto arguments: [](auto i). The error states for msvc2015: error C3556: 'ttt::<lambda_ff3930f11af2faf2c0b2ac7ad388c762>::operator ()': incorrect argument to 'decltype' or clang4.0: reference to overloaded function could not be resolved. I know this is legit error, but even so the arity is not reachable because of the error.

@Caleth 2018-01-16 12:19:38

@Andry that's a fundamental problem with function objects that have (potentially) multiple overloads of operator() not with this implementation. auto is not a type, so it can't ever be the answer to traits::template arg<0>::type

@Caleth 2018-01-16 12:22:18

@Andry e.g. what is the arity of struct functor { void operator()(int) {}; void operator()(int, double, bool) {} };?

@Andry 2018-01-17 13:43:36

@Caleth I agree, in overloaded case you can not find the arity. But in case of lambda [](auto i) the arity is known, because you have single operator with templated arguments and you still can not get it's quantity. And even more, you have to check the object on callability, before access the arity, so described traits implementation is not enough in this case.

@Caleth 2018-01-17 13:45:44

@Andry that's overloaded, in the general case

@Andry 2018-01-17 13:46:57

@Caleth Templated != Overloaded

@Caleth 2018-01-17 13:49:20

You can use that lambda in multiple contexts and i will be deduced as multiple types. The class synthesised will have multiple overloads of operator(). I don't know of any method of finding out if all the overloads of a given function have the same arity, which is what you would need.

@Andry 2018-01-17 13:50:22

Why it should have multiple overloads?

@Caleth 2018-01-17 13:50:30

@Andry 2018-01-31 14:17:57

If somebody has interested in better implementation: svn.code.sf.net/p/tacklelib/tacklelib/trunk/utility/…

@helmesjo 2019-01-16 17:49:41

@Andry Page is gone... Got a spare?

@Andry 2019-01-17 07:59:49

@helmesjo sf.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklel‌​ib/… As a solution for broken links: try to search from the root, Luke.

@Columbo 2015-01-29 11:35:14

The specialization method shown in @KennyTMs answer can be extended to cover all cases, including variadic and mutable lambdas:

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.

Note that the arity is not adjusted for variadic operator()s. Instead one can also consider is_variadic.

@Ise Wisteria 2011-10-30 07:17:40

Though I'm not sure this is strictly standard conforming, ideone compiled the following code:

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, "" );
}

However, this provides only the function type, so the result and parameter types have to be extracted from it. If you can use boost::function_traits, result_type and arg1_type will meet the purpose. Since ideone seems not to provide boost in C++11 mode, I couldn't post the actual code, sorry.

@Nawaz 2011-10-30 07:22:43

I think, it is a good start. +1 for that. Now we need to work on function type to extract the required information. (I don't want to use Boost as of now, as I want to learn the stuffs).

Related Questions

Sponsored Content

12 Answered Questions

[SOLVED] Storing C++ template function definitions in a .CPP file

  • 2008-09-22 15:55:52
  • Rob
  • 304722 View
  • 475 Score
  • 12 Answer
  • Tags:   c++ templates

3 Answered Questions

18 Answered Questions

[SOLVED] Distinct() with lambda?

2 Answered Questions

1 Answered Questions

[SOLVED] Template Type Deduction with Lambdas

1 Answered Questions

[SOLVED] Return type of a C++ lambda

  • 2014-01-30 17:10:32
  • Seva Alekseyev
  • 4074 View
  • 8 Score
  • 1 Answer
  • Tags:   c++ c++11 lambda

1 Answered Questions

Get lambda parameters type into template argument pack

2 Answered Questions

1 Answered Questions

[SOLVED] C++0x lambdas with template parameters?

Sponsored Content