Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1813217
  • 博文数量: 274
  • 博客积分: 2366
  • 博客等级: 大尉
  • 技术积分: 1880
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-22 09:37
文章分类

全部博文(274)

文章存档

2022年(1)

2020年(10)

2019年(7)

2018年(18)

2017年(26)

2016年(32)

2015年(43)

2014年(30)

2013年(44)

2012年(36)

2011年(17)

2010年(10)

分类: LINUX

2013-05-16 17:11:15

用宏自动生成你的代码
标签: 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个函数的定义。想想如果要自己手动写……


 


 


 


 


后记:


当然,要使用这些宏,需要很多的查找表。难道也要我们手动敲上去?非也,仔细瞧瞧,这些查找表项都相当有规律。相信聪明如你,已经知道怎么做了吧。


 


 


 


 


 


 
阅读(2511) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~