还有存放成员函数地址的链表节点
struct MethodNode
{
MethodNode(MethodNode*& head, TestMethodPtr method)
{
mNext = head;
head = this;
mMethod = method;
}
MethodNode* mNext;
TestMethodPtr mMethod;
};
还有提取成员函数地址的函数
template <class OutputClass, class InputClass>
union horrible_union{
OutputClass out;
InputClass in;
};
template <class OutputClass, class InputClass>
inline void union_cast(OutputClass& out, const InputClass input){
horrible_union<OutputClass, InputClass> u;
static_assert(sizeof(InputClass) == sizeof(u) && sizeof(InputClass) == sizeof(OutputClass), "out and in should be the same size");
u.in = input;
out = u.out;
}
template<typename Ty>
TestMethodPtr GetTestMethod(void(Ty::*testMethod)())
{
TestMethodPtr methodPtr;
union_cast(methodPtr, testMethod);
return methodPtr;
}
方法是每定义一个测试函数,在其上面就先定义一个链表节点变量,其构造函数记录测试函数地址,并把自身加入到链表中。但是,在此之前,我们将遭遇到编译器的抵触。比如
struct TestCase
{
typedef TestCase ThisType;
MethodNode* mMethods = nullptr;
TestMethodPtr mTestMethodfn1 = GetTestMethod(&fn1);
void fn1(){}
};
vc下面,编译器报错 error C2276: “&”: 绑定成员函数表达式上的非法操作
原来在就地初始化的时候,不能以这种方式获取到地址。然后,试试在TestCase里面的其他函数中,包括静态函数,就可以将取地址符号用到成员函数前面。
这好像分明是编译器在故意刁难,不过,任何代码上的问题都可以通过引入中间层来予以解决。用内部类。
struct TestCase
{
typedef TestCase ThisType;
MethodNode* mMethods = nullptr;
struct Innerfn1 : public MethodNode
{
Innerfn1(ThisType* pThis) : MethodNode(pThis->mMethods, GetTestMethod(&ThisType::fn1))
{
}
} mTestMethodfn1 = this;
void fn1(){}
struct Innerfn2 : public MethodNode
{
Innerfn2(ThisType* pThis) : MethodNode(pThis->mMethods, GetTestMethod(&ThisType::fn2))
{
}
} mTestMethodfn2 = this;
void fn2(){}
};
有多少个测试方法,就动用多少种内部类。然后,一旦定义一个测试类的变量,那么这些内部类的构造函数就执行了,把测试方法串联在一块,逆序,也就是说最后定义测试方法反而跑到前面去了。这样子就自动记录下来所有的测试方法的地址。有了这些函数地址信息,后面怎么玩都可以。包括漂亮的测试结果显示,日志记录,甚至嵌入到vs的单元测试界面中,又或者是生成配置文件,各种花招,怎么方便就怎么玩。这个时候,可以拿来主义,把cppunit,gtest等的优点都吸收过来。
是否觉得这还不够,好像有很多事情要做。比如说,测试方法逆序了,在同一个测试类的变量上执行这些测试方法,会不会就扰乱类的内部信息了,每次new一个测试类,所有的测试方法都要重复记录,内部类变量要占内存……。咳咳,这些都可以一一解决。这里只是用最简明的方式展示自动记录测试方法,产品级的写法肯定大有讲究了。
可以看到上面的代码都是有意做成很相似的,这些都是准备给宏大展身手的。这些低级宏太容易编写了,任何经历mfc或者boost代码折磨的猿猴,都完全能够胜任,这就打住了。对了,这里的自动记录成员函数的宏手法,可以大量地使用到其他地方,比如说,自动生成消息映射表,比mfc的那一套要好一百倍,应用范围太广了。当初老朽以为就只能用于单元测试框架的编写上面,想不到其威力如此巨大,消息系统全靠它了。C++的每一项奇技淫巧和功能被发现后,其价值都难以估量,好像bs所说的,他老人家不会给c++增添一项特性,其应用范围一早就可以预料的。对付一个问题,C++有一百种解决方案,当然里面只有几种才最贴切问题领域,但是很多时候,我们往往只选择或者寻找到另外的那90多种,最后注定要悲剧。