Is it possible to figure out the parameter type and return type of a lambda?
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) {}
.
deduce return type of lambda with arguments
If you are willing to limit yourself to non-generic lambdas (and in general to class objects with exactly one operator()
overload), then something like this should work (not tested):
template <typename T, typename R, typename... Args>
R ResultOf(R (T::*)(Args...));
Used as
using R = decltype(ResultOf(&decltype(my_lambda)::operator()));
This could be wrapped in helper classes for nicer syntax; we leave this as an exercise for the reader.
How does compiler deduce return type from this lambda expression?
websocket::stream_base::decorator
is a template function with one template type parameter.
template<class Decorator>
decorator( Decorator&& f );
In this call
ws_.set_option(websocket::stream_base::decorator(
[](websocket::response_type& res)
{
res.set(http::field::server,
std::string(BOOST_BEAST_VERSION_STRING) +
" websocket-server-async");
}));
the function is called accepting the lambda expression
[](websocket::response_type& res)
{
res.set(http::field::server,
std::string(BOOST_BEAST_VERSION_STRING) +
" websocket-server-async");
}
as its template argument. The lambda expression has by default the return type void
because there is no return statement in the lambda expression.
How does the lambda expression know, it should return a Decorator&&
It is the lambda expression itself is used as an argument for the declared function template parameter Decorator&&
.
Finding out the return type of a function, lambda or function
Assuming that:
- You want only the return type.
- You don't know what are/will be the types of arguments (so neither
decltype()
norstd::result_of<>
is an option. - The functor object passed as argument will not have an overloaded or generic
operator()
.
then you can use the below trait that infers the return type of any functor object:
template <typename F>
struct return_type_impl;
template <typename R, typename... Args>
struct return_type_impl<R(Args...)> { using type = R; };
template <typename R, typename... Args>
struct return_type_impl<R(Args..., ...)> { using type = R; };
template <typename R, typename... Args>
struct return_type_impl<R(*)(Args...)> { using type = R; };
template <typename R, typename... Args>
struct return_type_impl<R(*)(Args..., ...)> { using type = R; };
template <typename R, typename... Args>
struct return_type_impl<R(&)(Args...)> { using type = R; };
template <typename R, typename... Args>
struct return_type_impl<R(&)(Args..., ...)> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...)> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...)> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) &> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) &> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) &&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) &&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile&&> { using type = R; };
template <typename T, typename = void>
struct return_type
: return_type_impl<T> {};
template <typename T>
struct return_type<T, decltype(void(&T::operator()))>
: return_type_impl<decltype(&T::operator())> {};
template <typename T>
using return_type_t = typename return_type<T>::type;
Tests:
#include <type_traits>
template <typename F>
void test(F h)
{
static_assert(std::is_same<return_type_t<F>, int>{}, "!");
return_type_t<F> i = 1;
}
int function(int i) { return 2*i; }
int c_varargs_function(...) { return 1; }
struct A
{
int mem_function(double, float) { return 1; }
};
int main()
{
// Function
test(function);
// C-style variadic function
test(c_varargs_function);
// Non-generic lambda
test([](int i) { return 2*i; });
// Member function
test(&A::mem_function);
}
DEMO
how to deduce return type from lambda?
foo
takes function pointer while you're passing a lambda, but implicit conversion (from lambda to function pointer) won't be considered in template argument deduction for T
, which makes the invocation failing.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
You can specify template argument explicitly:
foo<int>([]()->int{ return 1; });
// ^^^^^
Or convert lambda to function pointer by operator+
or static_cast
.
foo(+[]()->int{ return 1; });
// ^
foo(static_cast<int(*)()>([]()->int{ return 1; }));
// ^^^^^^^^^^^^^^^^^^^^^^ ^
BTW: Omitted parameter list used with trailing return type is a C++23 lambda feature. (So I added ()
s.) And as @max66 suggested without trailing return type the lambda works fine too; the return type would be deduced automatically, such as foo(+[]{ return 1; });
.
Is it possible to get the type of lambda parameter at runtime?
My first reaction would be “just use intThat(i -> i == 42)
”, but apparently, the implementation is dropping the information that it has an ArgumentMatcher<Integer>
and later-on relying on the same Reflection approach that doesn’t work.
You can’t get the lambda parameter type reflectively and there are existing Q&As explaining why it’s not possible and not even intended. Note that this is not even lambda specific. There are scenarios with ordinary classes, where the type is not available too.
In the end, there is no point in trying to make this automatism work, when it requires additional declarations on the use side, like the explicit type cast (ArgumentMatcher<Integer>)
. E.g. instead of
argThat((ArgumentMatcher<Integer>) integer -> integer == 42)
you could also use
argThat(obj -> obj instanceof Integer && (Integer)obj == 42)
or even
argThat(Integer.valueOf(42)::equals)
Though, when we are at these trivial examples, eq(42)
would do as well or even when(someInterface.method(42)).thenReturn(42)
.
However, when you often have to match complex integer expressions in such contexts with broader argument types, a solution close to your original attempt is to declare a reusable fixed type matcher interface
interface IntArgMatcher extends ArgumentMatcher<Integer> {
@Override boolean matches(Integer arg0);
}
and use
when(someInterface.method(argThat((IntArgMatcher)i -> i == 42))).thenReturn(42);
i == 42
being a placeholder for a more complex expression
Returning a lambda function with parameter types constrained by generic type parameters
The wildcard means "of a specific, but unknown type". You can't pass any argument that will satisfy a parameter of type ?
, since it may be of the wrong type. Change the return type to ConstrainedBiFunction<Object, String>
and it will be able to accept any input type, since every class implicitly extends Object
:
public static ConstrainedBiFunction<Object, String> buildBiFxnWithSameTypeArgs() {
return (inputOne, inputTwo) -> String.valueOf(inputOne) + String.valueOf(inputTwo);
}
Note that this can still be used for methods with restrictions on the input type, using the PECS principle. For example:
// input parameter must be of type Integer or any supertype,
// so that we can safely pass in an Integer
String combine(ConstrainedBiFunction<? super Integer, String> function) {
return function.apply(1, 2);
}
void test() {
ConstrainedBiFunction<Object, String> concat = buildBiFxnWithSameTypeArgs();
ConstrainedBiFunction<Integer, String> sum = (a, b) -> String.valueOf(a + b);
System.out.println(combine(concat)); // "12"
System.out.println(combine(sum)); // "3"
}
Can we get the type of a lambda argument?
It's not desirable in the general case. (Note that it's quite easy for std::function<T(A)>
to specify what e.g. argument_type
is: it's just A
! It's available in the type definition.)
It would be possible to require each and every function object type to specify its argument types, and in turn mandate that the closure types generated from lambda expression do so. In fact, pre-C++0x features like adaptable functors would only work for such types.
However, we're moving from that with C++0x and for good reasons. The simplest of which is simply overloading: a functor type with a templated operator()
(a.k.a a polymorphic functor) simply takes all kind of arguments; so what should argument_type
be? Another reason is that generic code (usually) attempts to specify the least constraints on the types and objects it operates on in order to more easily be (re)used.
In other words, generic code is not really interested that given Functor f
, typename Functor::argument
be int
. It's much more interesting to know that f(0)
is an acceptable expression. For this C++0x gives tools such as decltype
and std::declval
(conveniently packaging the two inside std::result_of
).
The way I see it you have two choices: require that all functors passed to your template use a C++03-style convention of specifying an argument_type
and the like; use the technique below; or redesign. I'd recommend the last option but it's your call since I don't know what your codebase looks like or what your requirements are.
For a monomorphic functor type (i.e. no overloading), it is possible to inspect the operator()
member. This works for the closure types of lambda expressions.
So we declare these helpers
template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...));
template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...) const);
// volatile or lvalue/rvalue *this not required for lambdas (phew)
that accept a pointer to member function taking at least one argument. And now:
template<typename F>
struct first_argument {
typedef decltype( helper(&F::operator()) ) type;
};
[ an elaborate trait could successively query the lvalue-rvalue/const/volatile overloads and expose the first argument if it's the same for all overloads, or use std::common_type
.]
Related Topics
How to Check Whether a String Contains Every Letter in the Alphabet in C++
How to Fill 2D Array With the User'S Input
How to Measure Memory Usage from Inside a C++ Program
How to Add a Background Image to the Qmainwindow
Divide Array into K Contiguos Partitions Such That Sum of Maximum Partition Is Minimum
Store Hex Value as String (Arduino Project)
How to Convert String to Ip Address and Vice Versa
C++ - Initializing Variables in Header VS With Constructor
C++ Convert String to Hexadecimal and Vice Versa
Vscode C++ Task.Json Include Path and Libraries
How to Get Enum Item Name from Its Value
Qt Delete Selected Row in Qtableview
Two Dimensional Array With Random Numbers Doesn't Change
Automatically Add All Files in a Folder to a Target Using Cmake
How to Fill an Array With Random Values from a Range
How to Execute a Command and Get the Output of the Command Within C++ Using Posix