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
.]
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
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) {}
.
Check the type of a generic lambda parameter
The approach with decltype
already goes into the right direction.
Two types can be compared with std::is_same_v<Type1, Type2>
To prevent the compiler from complaining about the undefined .specialMember
for the other calls, if constexpr
is needed.
This amounts to:
auto const my_lambda = [](auto obj) {
// do something with obj
if constexpr (std::is_same_v<decltype(obj), SpecialType>)
auto x = obj.specialMember; // Access to a special member that is present in SpecialType, but not the other types this lambda is called with
}
If the parameter is passed as a reference, an additional std::decay_t
is needed to strip the parameter type from any const&
and the like.
auto const my_lambda = [](auto const& obj) {
// do something with obj
if constexpr (std::is_same_v<std::decay_t<decltype(obj)>, SpecialType>)
auto x = obj.specialMember; // Access to a special member that is present in SpecialType, but not the other types this lambda is called with
}
How to specify argument type in lambda?
The question was: "How to specify argument type in python lambda?".
Although the other answers are good, they do not provide answer to this main question.
The answer is: you cannot.
From the PEP specification of function annotations:
lambda 's syntax does not support annotations. The syntax of lambda could be changed to support annotations, by requiring parentheses around the parameter list. However it was decided [12] not to make this change because:
It would be an incompatible change.
Lambda's are neutered anyway.
The lambda can always be changed to a function.
For reference: https://stackoverflow.com/a/33833896/7051394
Is it possible to type hint a lambda function?
You can, sort of, in Python 3.6 and up using PEP 526 variable annotations. You can annotate the variable you assign the lambda
result to with the typing.Callable
generic:
from typing import Callable
func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)
This doesn't attach the type hinting information to the function object itself, only to the namespace you stored the object in, but this is usually all you need for type hinting purposes.
However, you may as well just use a function statement instead; the only advantage that a lambda
offers is that you can put a function definition for a simple expression inside a larger expression. But the above lambda is not part of a larger expression, it is only ever part of an assignment statement, binding it to a name. That's exactly what a def func(var1: str, var2: str): return var1.index(var2)
statement would achieve.
Note that you can't annotate *args
or **kwargs
arguments separately either, as the documentation for Callable
states:
There is no syntax to indicate optional or keyword arguments; such function types are rarely used as callback types.
That limitation does not apply to a PEP 544 protocol with a __call__
method; use this if you need a expressive definition of what arguments should be accepted. You need Python 3.8 or install the typing-extensions
project for a backport:
from typing_extensions import Protocol
class SomeCallableConvention(Protocol):
def __call__(self, var1: str, var2: str, spam: str = "ham") -> int:
...
func: SomeCallableConvention = lambda var1, var2, spam="ham": var1.index(var2) * spam
For the lambda
expression itself, you can't use any annotations (the syntax on which Python's type hinting is built). The syntax is only available for def
function statements.
From PEP 3107 - Function Annotations:
lambda 's syntax does not support annotations. The syntax of lambda could be changed to support annotations, by requiring parentheses around the parameter list. However it was decided not to make this change because:
- It would be an incompatible change.
- Lambda's are neutered anyway.
- The lambda can always be changed to a function.
You can still attach the annotations directly to the object, the function.__annotations__
attribute is a writable dictionary:
>>> def func(var1: str, var2: str) -> int:
... return var1.index(var2)
...
>>> func.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}
>>> lfunc = lambda var1, var2: var1.index(var2)
>>> lfunc.__annotations__
{}
>>> lfunc.__annotations__['var1'] = str
>>> lfunc.__annotations__['var2'] = str
>>> lfunc.__annotations__['return'] = int
>>> lfunc.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}
Not that dynamic annotations like these are going to help you when you wanted to run a static analyser over your type hints, of course.
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.
Related Topics
What Can Make C++ Rtti Undesirable to Use
C++11 Type Trait to Differentiate Between Enum Class and Regular Enum
Interpolate from One Color to Another
Can You Allocate a Very Large Single Chunk of Memory ( > 4Gb ) in C or C++
How to Execute a Piece of Code Only Once
Is "Std::Cout" Usable in Android-Ndk
How to Install/Configure Opencv3.2.0 with C++, Visual Studio 2017
Type Erasure in C++: How Boost::Shared_Ptr and Boost::Function Work
Opinions on Type-Punning in C++
What's the Time Complexity of Iterating Through a Std::Set/Std::Map
C++ Objects: When Should I Use Pointer or Reference
Manual for Cross-Compiling a C++ Application from Linux to Windows
Compile-Time Map and Inverse Map Values
Cygwin Make Bash Command Not Found
C++ Template Function Compiles in Header But Not Implementation
Lambda Expression VS Functor in C++