tinlyx - 2 months ago 14

C++ Question

I was wondering if there is a standard way to get the type signature (i.e. the return type and the types) of its parameters of any given lambda?

The reason I ask is that I've always wondered what exactly is the type

`auto`

`auto l =[](int x,int y)->int{return x+y;}`

`auto`

My understanding is that a standard lambda is nothing more than a function object, and it is its own type. So, even if two lambdas have the same return type and parameter types, they are still two different, unrelated classes/functors. But this there a way to capture the fact that they are the same in terms of type signature?

I think the type signature I am looking for can be something like a

`std::function<>`

A more useful/involved question is, if it's possible to extract the type signature, this is possible to write a general wrapper function to convert any lambda function to a

`std::function`

Answer

According to Can the 'type' of a lambda expression be expressed?, there is actually a simple way in current c++ (without needing c++1y) to figure out the return_type and parameter types of a lambda. Adapting this, it is not difficult to assemble a `std::function`

typed signature type (called `f_type`

below) for each lambda.

I. With this abstract type, it is actually possible to have an alternative way to `auto`

for expressing the type signature of a lambda, namely `function_traits<..>::f_type`

below. Note: the `f_type`

is not the real type of a lambda, but rather a summary of a lambda's type signature in functional terms. It is however, probably more useful than the real type of a lambda because *every single lambda is its own type*.

As shown in the code below, just like one can use `vector<int>::iterator_type i = v.begin()`

, one can also do `function_traits<lambda>::f_type f = lambda`

, which is an alternative to the mysterious `auto`

. Of course, this similarity is only formal. The code below involves converting the lambda to a `std::function`

with the cost of type erasure on construction of `std::function`

object and a small cost for making indirect call through the `std::function`

object. But these implementation issues for using `std::function`

aside (which I don't believe are fundamental and should stand forever), it is possible, after all, to explicitly express the (abstract) type signature of any given lambda.

II. It is also possible to write a `make_function`

wrapper (pretty much like `std::make_pair`

and `std::make_tuple`

) to automatically convert a lambda `f`

( and other callables like function pointers/functors) to `std::function`

, with the same type-deduction capabilities.

Test code is below:

```
#include <cstdlib>
#include <tuple>
#include <functional>
#include <iostream>
using namespace std;
// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
//enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) > {
typedef function<ReturnType (Args...)> f_type;
};
// for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType (*)(Args...)> {
typedef function<ReturnType (Args...)> f_type;
};
template <typename L>
typename function_traits<L>::f_type make_function(L l){
return (typename function_traits<L>::f_type)(l);
}
long times10(int i) { return long(i*10); }
struct X {
double operator () (float f, double d) { return d*f; }
};
// test code
int main()
{
auto lambda = [](int i) { return long(i*10); };
typedef function_traits<decltype(lambda)> traits;
traits::f_type ff = lambda;
cout << make_function([](int i) { return long(i*10); })(2) << ", " << make_function(times10)(2) << ", " << ff(2) << endl;
cout << make_function(X{})(2,3.0) << endl;
return 0;
}
```