0%

宏的黑魔法


1. 前言

写编译型语言多多少少都会遇到宏的使用,尤其是写oc,将一些事情放在预处理阶段做的话还是有很多好处的。
例如:

  • 不扩大二进制体积
  • 不占用运行时
  • 代码简洁
  • 深入理解预处理过程

很多优秀的框架都采用大量宏来维持简介的代码结构,治理友情链接同学对rac这个框架的宏的分析高手同学的友情链接

2. “#”和”##”的存在意义

“#”和”##”这两个宏用来把宏的参数变成字符串或者和其他字符相连,试想没有这两个宏语法是否可以实现相同功能,或者一个可以实现另一个。

为了消除二义性,如果不用#而是用"A"的形式来生成字符串的话,

1
#define stringfiy(A) "A"

stringfiy(a) 的结果到底是 “a” 还是 “A” 是不明确的

同样

1
#define CONCATG(A, B) AB

CONCATG(a, b) 的结果到底是 ab 还是 AB 是不明确的

无法通过 # 来实现 ##

1
2
3
#define MY_CONTACT(A, B) #A#B
MY_CONTACT(a, b) //"1""1"
MY_CONTACT(a, b) //"a""b"

生成的是字符串,而不是宏的名字,无法进一步做宏替换。

无法通过 ## 来实现 #

1
#define stringfiy(A) "##A##"

stringfiy(a) 只会得到字符串"##A##"

3. 中间宏的使用

在课上例子里用到了_CONCAT宏,这个宏其实和直接使用##语法基本等效,但是直接使用##会有一些问题。直接使用_CONCAT因为展开后马上要执行##,所以类似 _CONCAT(1, _CONCAT(2, 3)) 这样的嵌套操作是无法进行的,第一次展开会得到1_CONCAT(2, 3)这样的结果,发生错误,如果再定义一层 CONCAT

1
#define CONCAT(A, B) _CONCAT(A, B))

CONCAT(1, CONCAT(2, 3))展开后不会马上进行 ##,所以可以顺利得到123

1
2
3
4
5
6
7
CONCAT(1, CONCAT(2, 3))
_CONCAT(1, CONCAT(2, 3))
_CONCAT(1, _CONCAT(2, 3))
_CONCAT(1, 2 ## 3))
_CONCAT(1, 23))
1 ## 23
123

4. 宏的举例使用

5. 例子的改进

尝试让IS_EQ宏支持大的数在前面。

通过把-1换成SUB1,让宏可以正常识别来解决,缺点是 DEC 和 ARG_AT 会失去通用性。

1
2
3
4
5
6
#define _ARG_ATSUB1(_0, ...) SUB1
#define _IS_EQ0_SUB1 0

//修改 DEC
#define DEC(N) \
ARG_AT(N, SUB1, 0, 1, 2, 3, 4)

完整的宏定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define ARG_AT(INDEX, ...) _ARG_AT##INDEX(__VA_ARGS__)

#define _ARG_ATSUB1(_0, ...) SUB1
#define _ARG_AT0(_0, ...) _0
#define _ARG_AT1(_0, _1, ...) _1
#define _ARG_AT2(_0, _1, _2, ...) _2
#define _ARG_AT3(_0, _1, _2, _3, ...) _3
#define _ARG_AT4(_0, _1, _2, _3, _4, ...) _4
#define _ARG_AT5(_0, _1, _2, _3, _4, _5, ...) _5

#define DEC(N) \
ARG_AT(N, SUB1, 0, 1, 2, 3, 4)

#define IS_EQ(A, B) _CONCAT(_IS_EQ, A)(B)

#define _CONCAT(A, B) A ## B

#define _IS_EQ0(B) _CONCAT(_IS_EQ0_, B)

#define _IS_EQ0_SUB1 0
#define _IS_EQ0_0 1
#define _IS_EQ0_1 0
#define _IS_EQ0_2 0
#define _IS_EQ0_3 0
#define _IS_EQ0_4 0
#define _IS_EQ0_5 0

#define _IS_EQ1(B) _IS_EQ0(DEC(B))
#define _IS_EQ2(B) _IS_EQ1(DEC(B))
#define _IS_EQ3(B) _IS_EQ2(DEC(B))
#define _IS_EQ4(B) _IS_EQ3(DEC(B))
#define _IS_EQ5(B) _IS_EQ4(DEC(B))

6. 创建IS_GT(A, B)宏

现在尝试创建一个IS_GT(A, B)宏,当 A 比 B 大的时候为1, 否则为0。

IS_EQ相比只要稍微修改一下 IS_GT0_N 最后映射的值就实现了 IS_GT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define IS_GT(A, B)   _CONCAT(_IS_GT, A)(B)

#define _IS_GT0(B) _CONCAT(_IS_GT0_, B)


#define _IS_GT0_SUB1 1
#define _IS_GT0_0 0
#define _IS_GT0_1 0
#define _IS_GT0_2 0
#define _IS_GT0_3 0
#define _IS_GT0_4 0
#define _IS_GT0_5 0

#define _IS_GT1(B) _IS_GT0(DEC(B))
#define _IS_GT2(B) _IS_GT1(DEC(B))
#define _IS_GT3(B) _IS_GT2(DEC(B))
#define _IS_GT4(B) _IS_GT3(DEC(B))
#define _IS_GT5(B) _IS_GT4(DEC(B))