写一些能够被自动调用的函数

在日常开发测试中,偶尔会有这样的需求,在一个cpp文件中,定义一些函数,而这些函数会在一定条件下被自动调用。

例如,我是在刷leetcode题的时候,遇到了超时的问题,于是乎想写几个benchmark函数测试一下性能。可是如果以后每次测性能都要重新写一遍的话,又很麻烦。于是乎,我有了这样一个需求:

1
2
3
4
5
6
7
8
9
10
11
12
// leetXX.cpp
....

MY_BENCH(bench1)
{
...
}
MY_BENCH(bench2)
{
...
}

如果普通的调用这个程序的话,这些MY_BENCH函数不会被调用。
当我以

1
./leetXX bench 100 ...

的形式运行时,这些bench函数就会以参数中的循环次数运行并被统计运行时间:

1
2
bench1 loop 100 times, cost XXXms.
bench2 loop 100 times, cost XXXms.

编写宏

不像java,有万能的反射。首先想到的办法是,像go一样,在包的init中,把实现注册到一个全局的插槽中,这样别的项目只需要import这个包,它的实现就会被自动使用。c++里会自动运行的函数果然还是变量定义时的构造函数。

所以,首先可以写出基础的类型定义和插槽:

1
2
3
4
5
6
7
struct _bench_base_t
{
string name;
_bench_base_t(const char* n) : name(n){}
virtual void bench() = 0;
};
vector<_bench_base_t*> _benchs;

然后,在一个统一的地方进行调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void do_bench(_bench_base_t* bench, int loop)
{
...
int LOOP = loop;
while(LOOP--) bench->bench();
...
}

void do_bench(int loop)
{
for(auto bench : _benchs)
do_bench(bench, loop);
}

int main(int argc, char** argv)
{
if((argc > 2) && (argv[1] == string("bench")))
{
....
}
...
}

最后则是关键的宏定义:

1
2
3
4
5
#define LEET_BENCH(fn) struct fn : public _bench_base_t{	\
fn() : _bench_base_t(#fn){ \
_benchs.emplace_back(this);} \
void bench();}fn; \
void fn::bench()

LEET_BENCH宏定义了一个派生类及实例,并在构造函数中把自己注册到全局的插槽中。

之后只要接函数体,就大功告成:

1
2
3
4
5
// in leetXX.cpp
LEET_BENCH(dfs100w)
{
....
}

需要注意的是,宏不能替换字符串中的内容,所以这里使用#fn的方式拿到替换后的fn的内容。