用宏自动生成你的代码
标签: c++ 程序 宏 分类: 程序设计 2009-02-10 17:11
(原创文,转载请注明出处!http://3dlijia.blog.sohu.com/)
引:
C语言宏,被无数C语言教材称之为万恶之源。但事情总是有2面的,C语言宏在给我们带来无数抓狂的编译报错外,也能极大的方便我们的编码,甚至能自动帮我们生成代码!本文将向你详细讲解一种用宏生成一蔟相识函数的方法,并提供几种常用的宏代码。如果你对C语言宏不太了解,并对简化你的工作很感兴趣,那么请继续看下去;如果你是C++高手,或对此不感兴趣,那么请绕道。
在编码过程中,你也许经常遇到这种情况,你需要定义一系列这样的函数:
template < class T1, class T2 >
void fn(T1 param1, T2 param2)
{ ... }
template < class T1, class T2, class T3 >
void fn(T1 param1, T2 param2, T3 param3)
{ ... }
在C++环境下,不建议使用变参数函数,因此采用不同参数数量的重载函数是一个经常使用的手段。因此常常要写这样的一系列函数,如果只有1~2条还好办,但是现实通常是不能如愿的。而且这种情况通常是不能用常规的简单定义一个宏,用参数来解决的。如果你是程序员,是否经常为此而脱力呢?好在此事还是有方法解决的,下面我将详细讲解用宏来解决这种问题的技巧。
建立你的宏工具箱:
复杂的东西总是由简单的东西组合而成,下面我将从简单的宏开始,一步一接近我们解决问题的方法。当然每一步的宏都可以作为你个常用的宏工具,放入你的工具箱中。
工具1:打印宏内容
在一切开始之前,我们需要一个工具来显示我们生成的宏的展开内容。正所谓在攻击之前你得先了解你的敌人。好在的确有这样一个宏可以方便的显示一个宏到底生成了什么,代码很简单:
#define MACRO_PRINT(x) MACRO_PRINT_UNWARP(x)
#define MACRO_PRINT_UNWARP(x) #x
此宏会将你想看的宏展开后变成字符串,所以你可以这样使用:
#define MY_TEST_MACRO(t) t + t * t
cout << MACRO_PRINT( MY_TEST_MACRO(XX) );
显示结果为:
XX + XX * XX
原理很简单,MACRO_PRINT的第1条语句将x的内容展开,第2条语句#x将已经展开的x变成一个字符串“x”
工具2:连接宏
你可以连接2个字符从而产生新的字符,用到了宏语句##。代码如下,同样很简单:
#define MACRO_APPEND(x,y) MACRO_APPEND_UNWARP(x,y)
#define MACRO_APPEND_UNWARP(x,y) x ## y
第1条语句展开x和y的内容,第2条语句连接x和y。使用效果如下:
MACRO_APPEND(name_ , 13)
用工具1的打印宏打印一下,可以看到结果是“name_13”
工具3:宏查找表
在c语言中,我们可以用数组来找到某个下标表示的值,比如:name[12], table[4]。c++中有map等stl容器,也有类似写法:name_map["wolf"]。那么用宏可以制作相识的用法吗?当然是可以的,不过这个表只能是静态的(表项是事先写好的),而且调用方法也不同。看下面的代码(其中核心是使用了工具2的连接宏):
//查找表其实只是一个概念上的偷换,其实他的本质就是一个连接宏
#define MACRO_TABLE(table_name_, id) MACRO_APPEND(table_name_,id)
//和连接宏相比它多一个静态表项:
#define table_name_0 aaa
#define table_name_1 bbb
#define table_name_2 ccc
……
使用的时候,这样:
MACRO_TABLE(table_name_, 2) //结果应该是ccc
MACRO_TABLE(table_name_, 0) //结果应该是aaa
讲到这里有人可能不明白这样做的用意,这种表只能是静态的,这样和直接手动敲个“aaa”、“ccc”有啥区别?乍一看多敲这么多字还反倒麻烦了。不过不要着急,过会儿下面你就知道他的用处。
工具4:整数的自增与自减宏
这个宏的作用很简单,你写的是2,它帮你变成1(自减),或者你写的是5,它帮你变成6(自增)。实现方法————当然是查找表的思路咯:
#define MACRO_INC(x) MACRO_TABLE(TABLE_INC_, x)
#define TABLE_INC_0 1
#define TABLE_INC_1 2
#define TABLE_INC_2 3
#define TABLE_INC_3 4
#define TABLE_INC_4 5
……
调用的时候 MACRO_INC(3) 其实是表示 4
相应的自减的宏也很简单:
#define MACRO_DEC(x) MACRO_TABLE(TABLE_DEC_, x)
#define TABLE_DEC_0 0
#define TABLE_DEC_1 0
#define TABLE_DEC_2 1
#define TABLE_DEC_3 2
#define TABLE_DEC_4 3
……
这个东西貌似也没啥大用处……,现在说好像还有点早,继续看下面一条。
工具5:项展开
这条貌似有点用了,将某内容重复n次,并按1~n编号。实现方法嘛………… 还是宏查找表……(是不是有种被忽悠的感觉?)
#define MACRO_LOOP(f_name, n) MACRO_TABLE(MACRO_LOOP_, n)(f_name)
//查找表有点特殊,带一个参数。上面的宏先连接MACRO_LOOP_和n,再与后面的(f_name)参数组合构成查找表项
#define MACRO_LOOP_0(f_name)
#define MACRO_LOOP_1(f_name) MACRO_LOOP_0(f_name) f_name(1)
#define MACRO_LOOP_2(f_name) MACRO_LOOP_1(f_name) f_name(2)
#define MACRO_LOOP_3(f_name) MACRO_LOOP_2(f_name) f_name(3)
#define MACRO_LOOP_4(f_name) MACRO_LOOP_3(f_name) f_name(4)
……
这个表采用了类似递归的调用方法,展开效果如下:
MACRO_LOOP(f,2) //结果应该为:f(1) f(2)
MACRO_LOOP(f,4) //结果应该为:f(1) f(2) f(3) f(4)
MACRO_LOOP(f,0) //结果应该啥也没有
可以看到,宏展开成了一组类似函数调用的列表。并且宏参数允许你输入自己的函数名。为什么要这样做?这当然是有理由的,这种写法可以继续将f()看作下一级带参数的宏继续展开。废话少说,上面的简单工具都弄清楚后,下面开始真正实用的宏列表生成。
工具6:列表生成
在开始这个工具讲解之前,需要额外的几个项展开工具。上面的工具5的项展开工具最后展开的项是只带序号参数的,我们现在需要另外2个项展开工具,分别是:带序号和其他1个参数的、带序号和其他2个参数的
//带1个其他参数的
#define MACRO_LOOP_1P(f_name, param1, n) MACRO_TABLE(MACRO_LOOP_1P_, n)(f_name,param1)
//表
#define MACRO_LOOP_1P_0(f_name,param1)
#define MACRO_LOOP_1P_1(f_name,param1) MACRO_LOOP_1P_0(f_name,param1) f_name(1,param1)
#define MACRO_LOOP_1P_2(f_name,param1) MACRO_LOOP_1P_1(f_name,param1) f_name(2,param1)
……
//带2个其他参数的
#define MACRO_LOOP_2P(f_name, param1, param2, n) MACRO_TABLE(MACRO_LOOP_2P_, n)(f_name,param1,param2)
//表
#define MACRO_LOOP_2P_0(f_name,param1,param2)
#define MACRO_LOOP_2P_1(f_name,param1,param2) MACRO_LOOP_2P_0(f_name,param1,param2) f_name(1,param1,param2)
#define MACRO_LOOP_2P_2(f_name,param1,param2) MACRO_LOOP_2P_1(f_name,param1,param2) f_name(2,param1,param2)
……
上面2个宏分别生成这样的展开项:
f_name(1,param1) f_name(2,param1) f_name(3,param1) …… f_name(n,param1)
f_name(1,param1,param2) f_name(2,param1,param2) …… f_name(n,param1,param2)
有了上面2个辅助展开项后,另外我们还需要2个宏表,因为实际情况是:我们生成的列表的中间项和最末项通常是不同的,比如,中间项有分割符,最末项没有。为了处理这种情况,需要这2个宏表:
//表1:
#define MACRO_LOOP_END_1P_0(f_name,param1)
#define MACRO_LOOP_END_1P_1(f_name,param1) f_name(1,param1)
#define MACRO_LOOP_END_1P_2(f_name,param1) f_name(2,param1)
……
//表2:
#define MACRO_LOOP_END_2P_0(f_name,param1,param2)
#define MACRO_LOOP_END_2P_1(f_name,param1,param2) f_name(1,param1,param2)
#define MACRO_LOOP_END_2P_2(f_name,param1,param2) f_name(2,param1,param2)
……
6.1 列表“ A1, A2, A3, ……, An”
代码如下:
#define MACRO_An_LIST( A, count ) MACRO_REPEAT_1PARAM(count, MACRO_An_ITEM, MACRO_An_ITEM_LAST, A)
#define MACRO_REPEAT_1PARAM(n, f_loop, f_last, param1) MACRO_LOOP_1P(f_loop, param1, MACRO_DEC(n)) MACRO_TABLE(MACRO_LOOP_END_1P_, n)(f_last,param1)
#define MACRO_An_ITEM(n,A) A##n,
#define MACRO_An_ITEM_LAST(n,A) A##n
调用效果:
MACRO_An_LIST( Type, 3 ) //结果为:“ Type1, Type2, Type3”
这是如何实现的呢?我们先分析第2条语句,这条语句可以拆开成2条,其中第1部分 MACRO_LOOP_1P 生成一个 n-1 项的 f_loop(n-1,param1)的列表,而第2部分生成最后一项 f_last(n,param1) 。列表生成后将f_loop替换成MACRO_An_ITEM,将f_last替换成MACRO_An_ITEM_LAST就可以得到最终的结果。
6.2 列表“ A1 B1, A2 B2, …… An Bn”
代码如下,请自行分析:
#define MACRO_An_Bn_LIST( A, B, count ) MACRO_REPEAT_2PARAM(count, MACRO_An_Bn_ITEM, MACRO_An_Bn_ITEM_LAST, A, B)
#define MACRO_REPEAT_2PARAM(n, f_loop, f_last, param1, param2) MACRO_LOOP_2P(f_loop,param1,param2,MACRO_DEC(n)) MACRO_TABLE(MACRO_LOOP_END_1P_, n)(f_last,param1,param2)
#define MACRO_An_Bn_ITEM(n,A,B) A##n B##n,
#define MACRO_An_Bn_ITEM_LAST(n,A,B) A##n B##n
调用效果:
MACRO_An_Bn_LIST( Type, P, 3 ) //结果为:“ Type1 P1, Type2 P2, Type3 P3”
6.3 列表“ A1 Sep A2 Sep …… Sep An”其中Sep为分隔符
代码如下,请自行分析:
#define MACRO_An_Sep_LIST( A, Sep, count ) MACRO_REPEAT_2PARAM(count, MACRO_An_Sep_ITEM, MACRO_An_Sep_ITEM_LAST, A, Sep)
#define MACRO_An_Sep_ITEM(n,A,Sep) A##n Sep
#define MACRO_An_Sep_ITEM_LAST(n,A,Sep) A##n
调用效果:
MACRO_An_Sep_LIST( Type, |, 3 ) //结果为:“ Type1 | Type2 | Type3”
还记得开始的时候提到的那一蔟让人脱力的函数吗?有了上面这些宏工具,生成那一系列函数将非常简单:
#define MACRO_FUN_BUILD(n) \
template < MACRO_An_LIST( class T, n ) > \
void fn( MACRO_An_Bn_LIST( T, param, n ) ) \
{ ... }
MACRO_FUN_BUILD(n)将生成含参数个数为n的函数,要生成一系列函数你还得给每个函数写一行这个宏。不过我非常的懒,我想1行就完成所有的工作。方法很简单,再用展开项宏来展开一次:
#define MACRO_FUN_BUILD_ALL(n) MACRO_LOOP(MACRO_FUN_BUILD, n)
大功告成!现在我只需要调用1次 MACRO_FUN_BUILD_ALL(30) 就可以产生从1个参数到30个参数的30个函数的定义。想想如果要自己手动写……
后记:
当然,要使用这些宏,需要很多的查找表。难道也要我们手动敲上去?非也,仔细瞧瞧,这些查找表项都相当有规律。相信聪明如你,已经知道怎么做了吧。
阅读(2572) | 评论(0) | 转发(0) |