如何在运行时检查方法签名

How to check method signature in runtime

在 C++03 中有什么方法可以在运行时确定方法参数的类型吗?我正在考虑这样的方式:

#include <cstdio>

template<class T, class U>
struct is_same {
    static const bool result = false;
};

template<class T>
struct is_same<T, T> {
    static const bool result = true;
};

template<class ToFind, class Ret, class T, class Arg>
bool hasArg1(ToFind, Ret (T::*)(Arg)){return is_same<ToFind, Arg>::result;}

template<class ToFind, class Ret, class T, class Arg1, class Arg2>
bool hasArg1(ToFind, Ret (T::*)(Arg1, Arg2)){return is_same<ToFind, Arg1>::result;}

struct A
{
    int fun1(int a){return a+1;}
};

int main() {
    std::printf("int arg1: %s\n", hasArg1(1, &A::fun1) ? "yes" : "no");
}

但我想要这样的东西:

hasArg1<int>(&A::fun1)

而不是

hasArg1(1, &A::fun1)

只需删除第一个函数参数:

template<class ToFind, class Ret, class T, class Arg>
bool hasArg1(Ret (T::*)(Arg)){return is_same<ToFind, Arg>::result;}

template<class ToFind, class Ret, class T, class Arg1, class Arg2>
bool hasArg1(Ret (T::*)(Arg1, Arg2)){return is_same<ToFind, Arg1>::result;}

现在 hasArg1<int>(&A::fun1) 如您所愿。

See it Live

但请记住,如果 A::fun1 过载,此方法将不起作用。


现在,正如您在问题下所指出的那样。此类事情的运行时检查用处不大。通常您希望在编译时获得该信息,以影响代码生成并可能基于此进行优化。 与后来的修订相比,其编译时功能有限,但在编译时进行此检查并非不可能。以下是修改代码的方法:

template<bool C, typename T = void>
struct enable_if;

template<typename T>
struct enable_if<true, T> { typedef T type; };

template<int s> struct tag { char _[s]; };

template<class ToFind>
tag<1> hasArg1(...);

template<class ToFind, class Ret, class T, class Arg>
tag<2> hasArg1(Ret (T::*)(Arg), enable_if<is_same<ToFind, Arg>::result, void>* = 0);

// Add hasArg1 overloads to support members with more arguments

#define HAS_ARG1(ToFind, member) (sizeof(hasArg1<ToFind>(member)) != sizeof(tag<1>))

首先我们添加一个 "fallback" 重载,return 是一个具有预期大小的类型。然后我们添加另一个重载,根据您自己的修改。检查被降级到另一个函数参数。当重载解析期间检查失败时,参数格式错误并且替换失败,只剩下后备,因为 SFINAE 太棒了!

如果检查通过,则第二个重载格式正确且匹配更好,因为省略号在重载决策中的转换序列中优先级最低。

宏是为了语法糖而添加的,因为后面的细节要一遍又一遍地输入很乏味。我们在 sizeof 运算符内部进行重载解析。通过其 return 类型选择的过载将反映在 sizeof(hasArg1<ToFind>(member)) 报告中。所以我们可以根据 sizeof(tag<1>) (回退)检查它。由于 sizeof 是一个编译时运算符,我们有一个编译时常量来告诉我们 member 的第一个参数是否是 ToFind.

并且为了证明它是一个编译时常量,我们可以实例化

tag<HAS_ARG1(int, &A::fun1)> test_compile_time;

就像我们一样here, in GCC 4.1.2 in C++98 mode