在C++中将函数参数作为元组检索

9

假设我有以下这些函数声明:

void foo(int x, float y);

class X {
  void anotherFoo(double a, int c);
};

我该如何获取与函数参数对应的元组?在上述情况下,它将是:
boost::tuple<int, float>
boost::tuple<X*, double, int>

或者更好的方法是将结果类型作为第0个元素:

boost::tuple<void, int, float>
boost::tuple<void, X*, double, int>

我知道boost::function_types::parameter_types可以精确实现这一点。然而,我对它是如何实现的原理感兴趣。


我会使用 struct。使用元组的目的/优势是什么? - kol
@kol 减少样板代码,减少噪音 - daramarak
你是想在编译时还是运行时自动生成元组类型,或者是从寄存器/堆栈中提取参数,还是两者兼备,或者是其他需求? - Rup
应该全部在编译时完成 - 我只需要将参数作为结构体/元组,以便我可以在元编程中使用它们。 - Karel Petranek
你尝试过阅读boost源代码吗? - alex vasi
@alex:是的 :( 光靠阅读太复杂了。有太多的间接引用和零注释。 - Karel Petranek
2个回答

11
你可以通过以下方式获得与你的参数类型相对应的元组类型

template <typename R, typename... T>
std::tuple<T...> function_args(R (*)(T...))
{
    return std::tuple<T...>();
}

// get the tuple type
typedef decltype(function_args(foo)) FooArgType;
// create a default-initialised tuple
auto args = function_args(foo);

这是您想要的吗? 请注意,您可能需要添加一个或多个function_args的重载,例如为类方法添加一个类类型参数。


如果函数的一个或多个参数类型无法进行默认初始化,导致无法实例化,那么 decltype 还能起作用吗?如果不能,是否有一些指针的解决方法可以解决这个问题? - Steve Jessop
在typedef或其他地方仍然可以使用decltype(function_args(foo)),但在这种情况下,您永远不能真正调用function_args - Useless
这个可以,谢谢。有没有避免使用 decltype 的方法(因为它只有在 C++11 中才支持)?Boost 可以在非 C++11 编译器上工作,所以应该可以找到一种解决方法。 - Karel Petranek
我猜你可以使用元组typedef返回一个帮助类实例:然后你只需使用模板函数类型推断来保存编写帮助类类型参数。请注意,可变模板语法也仅适用于C++11——如果您无法使用它,则需要为不同数量的参数编写大量重载。 - Useless
@Useless: 没有使用decltype,我该如何实现元组typedef?我知道我必须为旧编译器提供许多重载,但我猜这是无法避免的。 - Karel Petranek

2

终于找到了一种使用部分特化在C++03中实现此操作的方法。需要大量重载以处理不同数量的参数和const/volatile函数,但思路如下:

/* An empty template struct, this gets chosen if the given template parameter is not a member function */
template <typename _Func>
struct MemberFunctionInfo {  };

/* Specialization for parameterless functions */
template <typename _Result, typename _Class>
struct MemberFunctionInfo<_Result (_Class::*) ()>  {
    typedef _Class class_type;
    typedef _Result result_type;
    typedef boost::tuple<> parameter_types;
    enum { arity = 0 };
};

/* Specialization for parameterless const functions */
template <typename _Result, typename _Class>
struct MemberFunctionInfo<_Result (_Class::*) () const> : MemberFunctionInfo<_Result (_Class::*) ()> { };

/* Specialization for functions with one parameter */
template <typename _Result, typename _Class, typename P0>
struct MemberFunctionInfo<_Result (_Class::*) (P0)>  {
    typedef _Class class_type;
    typedef _Result result_type;
    typedef boost::tuple<P0> parameter_types;
    enum { arity = 1 };
};

/* Specialization for const functions with one parameter */
template <typename _Result, typename _Class, typename P0>
struct MemberFunctionInfo<_Result (_Class::*) (P0) const> : MemberFunctionInfo<_Result (_Class::*) (P0)> { };

.
.
.

示例用法:

template <typename MemFunc>
int getArity(MemFunc fn)  {
  // Can also use MemberFunctionInfo<MemFunc>::parameter_types with boost::mpl
  return MemberFunctionInfo<MemFunc>::arity;
}

上面的解决方案有一些缺陷。它不能处理函数引用,非成员函数或volatile/const volatile 成员函数,但只需添加更多特化即可解决这些问题。
对于C++11,@Useless提到的方法更加清晰简洁,应该首选。

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