简体   繁体   中英

compile time loop over templated type

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.

With C++14, you may do:

namespace detail
{
    template <typename T, std::size_t ... Is>
    void display(std::index_sequence<Is...>)
    {
        std::initializer_list<int>{((std::cout << typeid(typename T::template arg<Is>::type).name() << std::endl), 0)...};
    }

}

template <typename T>
void display()
{
    detail::display<T>(std::make_index_sequence<T::arity>());
}

And use it:

using traits = function_traits<decltype(lambda)>;

display<traits>();

Live example

If your stuck with C++11, there is many place to find implementation of make_index_sequence and index_sequence

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM