I have been searching through SO, and other forums looking for a way to determine the parameters and return type of a lambda, and then act on those parameters in order to do a type lookup on a repo of objects that have already been instantiated. The point is to create a way to do dependency injection on some arbitrarily defined lambda expression. For instance, if I have something like the following:
auto lambda = [] (MyObject o) -> string { return o.name(); };
I could determine the parameter types of the lambda, and lookup a corresponding object of type MyObject
, and then call lambda
passing that object "automagically".
So far, I've found ways to determine the lambdas parameter list types and return types by using the following templates:
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.
};
};
Which I found in this SO post (very neat).
Now, what I'd like to do, is implement some sort of compile time equivalent of the following:
// first get the type of the lambda using the above 'function_traits' template
typedef function_traits<decltype(lambda)> traits;
// now iterate over the traits and get the type of each argument, then print out the type name
for (size_t i = 0u; i < size_t(traits::arity); ++i)
{
// The point to focus on here is `traits::args<i>::type`
std::cout << typeid(traits::args<i>::type).name() << std::end;
}
What I've posted above is impossible. The issue in the code above is that i
is not a constant, and is evaluated at run-time, as opposed to compile time (where the rest of this template magic is evaluated). I have tried a few different things to try to figure out a way to go about this (template recursion among them), but I haven't been able to find a solution that does exactly what I want.
So, the root of my question really is, how do you "iterate" over a template? I am new to TMP, and making the mental shift from run-time to compile-time logic has been challenging. If anyone has some suggestions for a newbie that would be great.