|
本帖最后由 nadesico19 于 2015-12-30 11:03 编辑
function-like 的宏是一个兼有 powerful 和 tricky 这两方面性质的工具,虽然不少人认为滥用之会陷入难以阅读与调试的尴尬境地,不过善用之则可以在一些场景下显著提升工作效率。
当然,只将其当做简单的代码模板称不上“善用之”,预处理期的条件分支、循环以及数据结构必不可少。
为了实现这些机制,首先来看一个基础问题:宏的嵌套展开。
- #define CONCAT(lhs_, rhs_) lhs_ ## rhs_
- CONCAT(1, 2)
- CONCAT(1, CONCAT(2, 3))
- // gcc -E ...
- // ...
- // 12
- // 1CONCAT(2, 3)
复制代码
可以看出,第二行的展开并不成功,这主要归因于展开规则(总结自标准):
1. 当扫描一个 function-like 的宏时,首先会展开其所有的参数宏(以及参数宏内的参数宏),但如果该参数宏与该宏重名,则不予展开。
为了解决这个问题,我们可以借助第二条规则
2. 当扫描并展开完一个 function-like 的宏之后,会对展开的内容进行再扫描,继续展开其中的其他宏。
- #define CONCAT(lhs_, rhs_) CONCAT_I(lhs_, rhs_)
- #define CONCAT_I(lhs_, rhs_) lhs_ ## rhs_
- CONCAT(1, 2)
- CONCAT(1, CONCAT(2, CONCAT(3, 4)))
- // gcc -E ...
- // ...
- // 12
- // 1234
复制代码
我们的方案是将宏重命名,这使得再扫描过程中宏与参数宏将不再重名,即可顺利展开。
以下是 CONCAT(1, CONCAT(2, CONCAT(3, 4))) 的展开过程,可以帮助理解,为了方便我们将三个CONCAT按出现顺序命名为C1、C2、C3
- CONCAT(1, CONCAT(2, CONCAT(3, 4)))
- -> CONCAT_I(1, CONCAT(2, CONCAT(3, 4))) // 扫描C1。C2与C1同名,不展开。
- -> CONCAT_I(1, CONCAT_I(2, CONCAT(3, 4))) // 重新扫描C1。C2与C1不同名,展开;C3与C2同名,不展开。
- -> CONCAT_I(1, CONCAT_I(2, CONCAT_I(3, 4))) // 重新扫描C2。C3与C2不同名,展开。
- -> CONCAT_I(1, CONCAT_I(2, 34)) // C3展开完毕
- -> CONCAT_I(1, 234) // C2展开完毕
- -> 1234 // C1展开完毕
复制代码
水水的就是这样。 |
评分
-
查看全部评分
|