Chad Layton Chad Layton - 2 months ago 9
C++ Question

Transform typelist with function at runtime

I have a typelist. I would like to create a tuple with the results of calling a function on each type in that list and then use that as arguments to another functor. So something like this:

template<typename F>
struct function_traits;

template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...) const> {
using return_type = R;
using param_types = std::tuple<Args...>;
};

template<typename T> struct function_traits : public
function_traits<decltype(&T::operator())> {};

template <typename T>
T* get_arg(int id)
{
// Actual implementation omitted. Uses the id parameter to
// do a lookup into a table and return an existing instance
// of type T.
return new T();
}

template <typename Func>
void call_func(Func&& func, int id)
{
using param_types = function_traits<Func>::param_types>;

func(*get_arg<param_types>(id)...); // <--- Problem is this line
}

call_func([](int& a, char& b) { }, 3);


The problem is that
func(*get_arg<param_types>(id)...);
doesn't actually compile since param_types is a tuple and not a parameter pack. The compiler generates this error: "there are no parameter packs available to expand". What I would liked to have happened is for that line to expand to:

func(*get_arg<int>(id), *get_arg<char>(id));


And to have that work for any number of arguments. Is there any way to get that result?

This question seems similar but does not solve my problem by itself: "unpacking" a tuple to call a matching function pointer. I have a type list and from that I want to generate a list of values to use as function arguments. If I had the list of values I could expand them and call the function as outlined in that question, but I do not.

Answer

Not sure that is what do you want.

I don't know how to expand, inside call_func(), the parameters pack of params_type but, if you afford the use of a helper struct and a compiler with C++14...

I've prepared the following example with support for return type.

#include <tuple>

template<typename F>
struct function_traits;

template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...) const> {
    using return_type = R;
    using param_types = std::tuple<Args...>;
};

template<typename T> struct function_traits : public
function_traits<decltype(&T::operator())> {};

template <typename T, typename ... Args>
T get_arg (std::tuple<Args...> const & tpl)
 { return std::get<typename std::decay<T>::type>(tpl); } 

template <typename ...>
struct call_func_helper;

template <typename Func, typename Ret, typename ... Args>
struct call_func_helper<Func, Ret, std::tuple<Args...>>
 {
   template <typename T, typename R = Ret>
      static typename std::enable_if<false == std::is_same<void, R>::value, R>::type
                fn (Func const & func, T const & t)
       { return func(get_arg<Args>(t)...); }

   template <typename T, typename R = Ret>
      static typename std::enable_if<true == std::is_same<void, R>::value, R>::type
                fn (Func const & func, T const & t)
       { func(get_arg<Args>(t)...); }
 };

template <typename Func,
          typename T,
          typename R = typename function_traits<Func>::return_type>
R call_func (Func const & func, T const & id)
 {
    using param_types = typename function_traits<Func>::param_types;

    return call_func_helper<Func, R, param_types>::fn(func, id);
 }

int main()
 {
   call_func([](int const & a, char const & b) { }, std::make_tuple(3, '6'));

   return 0;
}

Hope this helps.