全局函数和成员函数指针不是类型,而是一种变量,虽然它们在本质上有很多区别,但是从模板的角度来说,却是非常类似的。那么如何尽可能的获取它们的traits呢?就这个问题,我做了一些简单的研究,所有的编译结果基于VS.net 2005。
首先,我们不可能用类模板来提取traits信息,也就是说,这种traits手法和一般的type_traits不一样,也弱很多。这个的原因是,类模板如果要接受数值,那么就一定要指定类型,而指针类型正是我们要提取的信息,当然这就陷入了一种循环依赖之中。
那么,我将目光转向使用函数模板,事实上,函数模板本身就是用来提取变量信息,所以用这个就再恰当不过了。由于函数模板必须调用函数才能够特化,而函数中除了返回值的sizeof信息以外,其他的都是动态获得的,所以我们能在静态挖掘的信息恐怕只有经过巧妙构造的sizeof信息了。最容易想到的是,用sizeof信息获得参数的个数或者获得成员函数所在类的大小。
例如,我想提取全局函数的参数个数,我可以这样实现这个function_arg_count函数:
#include <boost/type_traits.hpp>
/**
Contain size information.
*/
template <int N>
struct size_of_type
{
char element_[N + 1];
};
/** Global function with no arguments. */
template <typename R>
size_of_type<boost::function_traits<R ()>::arity>
function_arg_count(R (*)());
/** Global function with 1 arguments. */
template <typename R, typename T1>
size_of_type<boost::function_traits<R (T1)>::arity>
function_arg_count(R (*)(T1));
// ...
/** Global function with 10 arguments. */
template <typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10>
size_of_type<boost::function_traits<R (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)>::arity>
function_arg_count(R (*)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10));
然后,使用方法是:
int main()
{
std::cout << sizeof(function_arg_count(test_func1)) << std::endl;
std::cout << sizeof(function_arg_count(test_func2)) << std::endl;
std::cout << sizeof(function_arg_count(test_func3)) << std::endl;
return 0;
}
其中,test_func1、test_func2和test_func3声明如下:
int test_func1(void *);
int test_func2(char, int []);
void test_func3(char, int [], float*******);
运行结果会是:
2
3
4
如果要解析成员函数,只需要在函数模板中增加一个模板参数即可。不过此时就不能用boost的function_traits了,因为这个function_traits不支持成员函数的traits,我必须自己写一个traits类来实现这个功能。
以获取0个参数的函数为例:
/**
Member function traits helper.
*/
template <typename Function>
struct member_function_traits_helper;
template <typename R, typename Parent>
struct member_function_traits_helper<R (Parent::*)()>
{
BOOST_STATIC_CONSTANT(int, arity = 0);
typedef R result_type;
typedef Parent parent_type;
};
/**
Member function traits.
*/
template <typename Function>
struct member_function_traits :
public member_function_traits_helper<Function>
{
};
/** Member function with no arguments. */
template <typename R, typename Parent>
size_of_type<member_function_traits<R (Parent::*)()>::arity>
function_arg_count(R (Parent::*)());
这样就OK了,其他的类似,包括需要自己扩展这个member_function_traits。
使用方法是:
int main()
{
std::cout << sizeof(function_arg_count(&test_class::func0)) << std::endl;
std::cout << sizeof(function_arg_count(&test_class::func1)) << std::endl;
std::cout << sizeof(function_arg_count(&test_class::func2)) << std::endl;
return 0;
}
其中,test_class的声明是:
class test_class
{
public: // Note: You can only get traits of public function.
void func0();
int func1(char);
std::string& func2(int, double&);
};
运行结果是:
1
2
3
至于如何获得成员函数所在的类的大小,相信已经是非常自然而然,就不再敷述。
值得注意的是,除了这些编译期间的traits,我还可以使用typeid获得运行时的特征,这样也可以解决某些traits的需求。现在只举一个例子:
template <int N>
struct arg_index
{
};
template <typename R,
typename T1,
typename T2,
typename argN,
typename Ret>
Ret function_arg_typename(R (T1, T2), argN);
template <typename R,
typename T1,
typename T2>
T1 function_arg_typename<>(R (T1, T2), arg_index<0>);
template <typename R,
typename T1,
typename T2>
T2 function_arg_typename<>(R (T1, T2), arg_index<1>);
使用方法如下:
int main()
{
std::cout << typeid(function_arg_typename(test_func2, arg_index<0>())).name() << std::endl;
std::cout << typeid(function_arg_typename(test_func2, arg_index<1>())).name() << std::endl;
}
其中,test_func2声明如下:
int test_func2(int , char *);
运行结果是:
int
char *
<iframe frameborder="0" id="gn_notemagic" style="position: absolute; display: block; opacity: 0.7; z-index: 500; width: 16px; height: 20px; top: 54px; right: 396px;" src="http://www.google.com/gn/static_files/blank.html"></iframe>