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).
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) {}.
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.
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.
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.
If you're looking for a complete solution for all types in C++ that can be invoked, a lot of these answers work but miss some of the corner cases, like
A reference to a lambda
Functions and function pointers
Here's a complete solution to my knowledge (except for generic lambdas) - let me know in the comments if anything is missing:
template <typename>
struct closure_traits;
template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
: closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};
template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
using arguments = std::tuple<Args...>;
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
template <std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = ReturnTypeT;
};
template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
};
// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
using class_type = ClassTypeT;
};
// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
: closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};
Disclaimer: the std::remove_reference was inspired by this code.
Related
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).
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) {}.
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.
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.
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.
If you're looking for a complete solution for all types in C++ that can be invoked, a lot of these answers work but miss some of the corner cases, like
A reference to a lambda
Functions and function pointers
Here's a complete solution to my knowledge (except for generic lambdas) - let me know in the comments if anything is missing:
template <typename>
struct closure_traits;
template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
: closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};
template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
using arguments = std::tuple<Args...>;
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
template <std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = ReturnTypeT;
};
template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
};
// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
using class_type = ClassTypeT;
};
// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
: closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};
Disclaimer: the std::remove_reference was inspired by this code.
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).
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) {}.
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.
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.
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.
If you're looking for a complete solution for all types in C++ that can be invoked, a lot of these answers work but miss some of the corner cases, like
A reference to a lambda
Functions and function pointers
Here's a complete solution to my knowledge (except for generic lambdas) - let me know in the comments if anything is missing:
template <typename>
struct closure_traits;
template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
: closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};
template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
using arguments = std::tuple<Args...>;
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
template <std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = ReturnTypeT;
};
template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
};
// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
using class_type = ClassTypeT;
};
// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
: closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};
Disclaimer: the std::remove_reference was inspired by this code.
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).
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) {}.
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.
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.
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.
If you're looking for a complete solution for all types in C++ that can be invoked, a lot of these answers work but miss some of the corner cases, like
A reference to a lambda
Functions and function pointers
Here's a complete solution to my knowledge (except for generic lambdas) - let me know in the comments if anything is missing:
template <typename>
struct closure_traits;
template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
: closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};
template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
using arguments = std::tuple<Args...>;
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
template <std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = ReturnTypeT;
};
template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
};
// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
using class_type = ClassTypeT;
};
// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
: closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};
Disclaimer: the std::remove_reference was inspired by this code.
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).
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) {}.
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.
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.
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.
If you're looking for a complete solution for all types in C++ that can be invoked, a lot of these answers work but miss some of the corner cases, like
A reference to a lambda
Functions and function pointers
Here's a complete solution to my knowledge (except for generic lambdas) - let me know in the comments if anything is missing:
template <typename>
struct closure_traits;
template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
: closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};
template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
using arguments = std::tuple<Args...>;
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
template <std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = ReturnTypeT;
};
template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
};
// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
using class_type = ClassTypeT;
};
// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
: closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};
Disclaimer: the std::remove_reference was inspired by this code.
I've written a traits class that lets me extract information about the arguments and type of a function or function object in C++0x (tested with gcc 4.5.0). The general case handles function objects:
template <typename F>
struct function_traits {
template <typename R, typename... A>
struct _internal { };
template <typename R, typename... A>
struct _internal<R (F::*)(A...)> {
// ...
};
typedef typename _internal<decltype(&F::operator())>::<<nested types go here>>;
};
Then I have a specialization for plain functions at global scope:
template <typename R, typename... A>
struct function_traits<R (*)(A...)> {
// ...
};
This works fine, I can pass a function into the template or a function object and it works properly:
template <typename F>
void foo(F f) {
typename function_traits<F>::whatever ...;
}
int f(int x) { ... }
foo(f);
What if, instead of passing a function or function object into foo, I want to pass a lambda expression?
foo([](int x) { ... });
The problem here is that neither specialization of function_traits<> applies. The C++0x draft says that the type of the expression is a "unique, unnamed, non-union class type". Demangling the result of calling typeid(...).name() on the expression gives me what appears to be gcc's internal naming convention for the lambda, main::{lambda(int)#1}, not something that syntactically represents a C++ typename.
In short, is there anything I can put into the template here:
template <typename R, typename... A>
struct function_traits<????> { ... }
that will allow this traits class to accept a lambda expression?
I think it is possible to specialize traits for lambdas and do pattern matching on the signature of the unnamed functor. Here is the code that works on g++ 4.5. Although it works, the pattern matching on lambda appears to be working contrary to the intuition. I've comments inline.
struct X
{
float operator () (float i) { return i*2; }
// If the following is enabled, program fails to compile
// mostly because of ambiguity reasons.
//double operator () (float i, double d) { return d*f; }
};
template <typename T>
struct function_traits // matches when T=X or T=lambda
// As expected, lambda creates a "unique, unnamed, non-union class type"
// so it matches here
{
// Here is what you are looking for. The type of the member operator()
// of the lambda is taken and mapped again on function_traits.
typedef typename function_traits<decltype(&T::operator())>::return_type return_type;
};
// matches for X::operator() but not of lambda::operator()
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...)>
{
typedef R return_type;
};
// I initially thought the above defined member function specialization of
// the trait will match lambdas::operator() because a lambda is a functor.
// It does not, however. Instead, it matches the one below.
// I wonder why? implementation defined?
template <typename R, typename... A>
struct function_traits<R (*)(A...)> // matches for lambda::operator()
{
typedef R return_type;
};
template <typename F>
typename function_traits<F>::return_type
foo(F f)
{
return f(10);
}
template <typename F>
typename function_traits<F>::return_type
bar(F f)
{
return f(5.0f, 100, 0.34);
}
int f(int x) { return x + x; }
int main(void)
{
foo(f);
foo(X());
bar([](float f, int l, double d){ return f+l+d; });
}
The void_t trick can help. How does `void_t` work?
Unless you have C++17, you'll need to include the definition of void_t:
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
Add an extra template argument to the original template, defaulted to void:
template <typename T, typename = void>
struct function_traits;
The traits object for simple functions is the same as you already have:
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
For non-const methods:
template <typename R, typename... A>
struct function_traits<R (C::*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
Don't forget const methods:
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...) const> // const
{
using return_type = R;
using class_type = C;
using args_type = std:: tuple< A... >;
};
Finally, the important trait. Given a class type, including lambda types, we want to forward from T to decltype(&T::operator()). We want to ensure that this trait is only available for types T for which ::operator() is available, and this is what void_t does for us. To enforce this constraint, we need to put &T::operator() into the trait signature somewhere, hence template <typename T> struct function_traits<T, void_t< decltype(&T::operator())
template <typename T>
struct function_traits<T, void_t< decltype(&T::operator()) > >
: public function_traits< decltype(&T::operator()) >
{
};
The operator() method in (non-mutable, non-generic) lambdas is const, which explains why we need the const template above.
But ultimately this is very restrictive. This won't work with generic lambdas, or objects with templated operator(). If you reconsider your design, you find find a different approach that is more flexible.
By delegating some of the work to a series of function templates instead of a class template, you can extract the relevant info.
First though, I should say that the relevant method is a const method, for a lambda (for a non-capturing, non-generic, non-mutable lambda). So you will not be able to tell the difference between a true lambda and this:
struct {
int operator() (int) const { return 7; }
} object_of_unnamed_name_and_with_suitable_method;
Therefore, I must assume that you don't want "special treatment" for lambdas, and you don't want to test if a type is a lambda type, and that instead you want to simply extract the return type, and the type of all arguments, for any object which is simple enough. By "simple enough" I mean, for example, that the operator() method is not itself a template. And, for bonus information, a boolean to tell us if an operator() method was present and used, as opposed to a plain old function.
// First, a convenient struct in which to store all the results:
template<bool is_method_, bool is_const_method_, typename C, typename R, typename ...Args>
struct function_traits_results {
constexpr static bool is_method = is_method_;
constexpr static bool is_const_method = is_const_method_;
typedef C class_type; // void for plain functions. Otherwise,
// the functor/lambda type
typedef R return_type;
typedef tuple<Args...> args_type_as_tuple;
};
// This will extract all the details from a method-signature:
template<typename>
struct intermediate_step;
template<typename R, typename C, typename ...Args>
struct intermediate_step<R (C::*) (Args...)> // non-const methods
: public function_traits_results<true, false, C, R, Args...>
{
};
template<typename R, typename C, typename ...Args>
struct intermediate_step<R (C::*) (Args...) const> // const methods
: public function_traits_results<true, true, C, R, Args...>
{
};
// These next two overloads do the initial task of separating
// plain function pointers for functors with ::operator()
template<typename R, typename ...Args>
function_traits_results<false, false, void, R, Args...>
function_traits_helper(R (*) (Args...) );
template<typename F, typename ..., typename MemberType = decltype(&F::operator()) >
intermediate_step<MemberType>
function_traits_helper(F);
// Finally, the actual `function_traits` struct, that delegates
// everything to the helper
template <typename T>
struct function_traits : public decltype(function_traits_helper( declval<T>() ) )
{
};