在运行时使用函数转换类型列表

6

我有一个类型列表。我想创建一个元组,其中包含对该列表中每种类型调用函数的结果,并将其用作另一个函子的参数。类似于这样:

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);

问题在于func(*get_arg<param_types>(id)...);并不会真正编译,因为param_types是一个元组而不是参数包。编译器会生成这个错误:“没有可用于扩展的参数包”。我希望发生的是该行被拓展成:
func(*get_arg<int>(id), *get_arg<char>(id));

能够在任意数量的参数中使用,并获得该结果的方法是什么?是否有办法实现这个目标?

这个问题看起来很相似,但它本身不能解决我的问题: "unpacking" a tuple to call a matching function pointer。我有一个类型列表,我想从中生成一个值列表作为函数参数。如果我有值列表,我可以按照那个问题中的方法扩展它们并调用函数,但我没有。


id 参数并不是非常重要。在我的用例中,我会在 get_arg 中使用它来确定返回 T 的哪个实例。Lambda 的参数应该是调用 typelist 中每种类型的 get_arg 的结果,类似于我上面写的:func(*get_arg<int>(id),*get_arg<char>(id)); - Chad Layton
不是这样的。我有一个类型列表,而不是值列表。我不知道如何通过对每个类型调用get_arg函数来创建值列表。如果我能做到这一点,那么这个问题就会有帮助。 - Chad Layton
我有一个类型列表,我想从中生成值列表用作函数参数。 这是一个类型列表:intstd::list<char>double。如果您不知道要从中生成什么值,那么没有人知道。不清楚您的问题是什么。 - Mike Kinghan
哦,我想我应该提供get_args的实现。我为了简洁而省略了它,但也许我让你误以为我在问如何实现那个函数?我真正遇到的问题是这一行:func(*get_arg<param_types>(id)...)。它无法编译,因为param_types是一个元组而不是参数包。我的愿望是它会扩展到这个样子:func(*get_arg<int>(id), *get_arg<char>(id)); 我会更新我的问题。 - Chad Layton
显示剩余2条评论
1个回答

2

不确定这是否是你想要的。

我不知道如何在call_func()内展开params_type的参数包,但是如果您使用一个帮助结构和一个支持C++14的编译器...

我已经准备了以下示例,支持返回类型。

#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;
}

希望这可以帮到您。

不错!我需要仔细琢磨一下,但我认为这个可以满足我所有的需求。我感到(愉快地)惊讶的是,您能够将<Func,Ret,std :: tuple <Args ...>>用作lambda的特化?我本以为它需要像function_traits上的更丑陋的R(T :: *)(Args ...)const特化那样才能实现。 - Chad Layton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接