宏元编程

来源:互联网 发布:java和.net哪个好 编辑:程序博客网 时间:2024/06/07 12:29

宏元编程定义

宏元编程目的是利用宏在展开时候的特性减少重复代码,提供更高层的编程抽象。

宏的定义

宏有两种形式,带参数和不带参数

#define identifier replacement-list#define identifier(a1,a2,...an) replacement-list

说明:宏的实参必须是除了逗号和小括号之外的预处理标记,或者有一对小括号包围的一组预处理标记。

举例:

#define FUN(x) x  // ok#define Fun1(,)   // failed#define Fun2(()   // failed

两个预编译期的特殊标记##和#
##含义是把两端的预处理标记和在一起生成一个新的预处理标记
#会把后面跟的预处理标记生成一个字符串
例如:
#define PARAM(n) p##n#define TO_STRING(x) #x
可以这样应用
void func(PARAM(1), PARAM(2), PARAM(3)){}char* name = TO_STRING(hello);
展开后
void func(p1, p2, p3){}char* name = "hello";

宏展开

1,参数不是宏。先把宏body里的形参用实参替换,再把调用宏函数的地方用宏的body替换
2,宏的参数还是个宏,先会把宏的参数展开,然后按照a的说明,实参替换形参,body替换函数调用
#define MUL(p1,p2) p1*p2#define ADD(p1,p2) p1+p2MUL(3,4) => 3*4 = 12MUL(3,ADD(4,5)) => 3 * 4 + 5 = 17 
我们希望的展开结果是3*(4+5)=27
所以在宏函数的定义最好加上(),避免展开后由于运算符的默认优先级导致计算结果和预期不一致情况
#define MUL(p1,p2) (p1*p2)#define ADD(p1,p2) (p1+p2)MUL(3,4) => (3*4) = 12MUL(3,ADD(4,5)) => (3 * (4 + 5)) = 27 
3,token连接符号##
#define CAT(p1,p2) p1##p2CAT(3,4) => 34
说明:参数是宏,如果外层宏使用##连接参数宏的展开结果,参数宏会被当成一个token,不进行展开
#define CAT(p1,p2) p1##p2#define ADD(p1,p2) (p1+p2)CAT(3,ADD(4,5)) => 3ADD(4,5)
如果希望先进行展开,在通过##连接可以这么做
#define CAT(p1,p2) CAT1(p1,p2)#define CAT1(p1,p2) p1##p2#define ADD(p1,p2) (p1+p2)CAT(3,ADD(4,5)) => 3(4 + 5)

p2在调用CAT1会先展开成(4+5)

宏计算

一些简单的函数,我们可以在预编译期直接给出结果,如INC和DEC宏函数的定义如下:
#define INC_5 6#define INC_4 5#define INC_3 4#define INC_2 3#define INC_1 2#define INC_0 1#define INC(n)  INC_ ## n#define DEC_5 4#define DEC_4 3#define DEC_3 2#define DEC_2 1#define DEC_1 0#define DEC(n)  DEC_ ## n
INC(3) =>  4
DEC(3) => 2

宏实现递归

利用宏实现阶乘计算
#define FACTORIAL(n) n*FACTORIAL(n-1)  #define FACTORIAL(1) 1int r = FACTORIAL(3)
我们期望编译器在预处理阶段把上面代码处理成
int r = 3 * 2 * 1;
理想是美好的,然而现实是残酷的。。
预处理器展开宏结果为
int r = 1;
把FACTORIAL(1)的定义去掉,结果变成
int r = 3*FACTORIAL(3-1);

结论:c++的预处理器发现同名的宏就不会再继续展开,而是把它当做结果直接替换。并且预编译期不会做数值计算,只是简单的替换。
ps:如果定义了FACTORIAL(1)预处理器把括号里的1当成参数,所以FACTORIAL(3)结果是1。如果把FACTORIAL(1)定义去掉,宏如果支持递归替换就没办法结束(递归都必须有出口条件),所以展开结果变成3*FACTORIAL(3-1)

难道我们要放弃吗?
上帝为你关了一扇门,同时也会为你开另一扇门。

既然FACTORIAL遇到相同名字的宏会停止展开,我们可以换一个名字。
#define FACTORIAL_5 5*FACTORIAL_4#define FACTORIAL_4 4*FACTORIAL_3#define FACTORIAL_3 3*FACTORIAL_2#define FACTORIAL_2 2*FACTORIAL_1#define FACTORIAL_1 1#define FACTORIAL(n)  FACTORIAL_ ## n
int r = FACTORIAL(3); 
展开为int r = 3*2*1;

参考:
http://www.cppblog.com/kevinlynx/archive/2008/03/19/44828.html
http://www.cppblog.com/kevinlynx/archive/2008/08/20/59451.html


0 0
原创粉丝点击