设为首页收藏本站

ISO/IEC C++ China Unofficial

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 491|回复: 11

【PP疼?①】嵌套展开

[复制链接]

8

主题

64

帖子

269

积分

超级版主

Rank: 8Rank: 8

威望
12
经验
169
贡献
12
发表于 2015-12-30 11:04:15 | 显示全部楼层 |阅读模式
本帖最后由 nadesico19 于 2015-12-30 11:03 编辑

function-like 的宏是一个兼有 powerful 和 tricky 这两方面性质的工具,虽然不少人认为滥用之会陷入难以阅读与调试的尴尬境地,不过善用之则可以在一些场景下显著提升工作效率。

当然,只将其当做简单的代码模板称不上“善用之”,预处理期的条件分支、循环以及数据结构必不可少。

为了实现这些机制,首先来看一个基础问题:宏的嵌套展开。


  1. #define CONCAT(lhs_, rhs_) lhs_ ## rhs_
  2. CONCAT(1, 2)
  3. CONCAT(1, CONCAT(2, 3))

  4. // gcc -E ...
  5. // ...
  6. // 12
  7. // 1CONCAT(2, 3)
复制代码


可以看出,第二行的展开并不成功,这主要归因于展开规则(总结自标准):

1. 当扫描一个 function-like 的宏时,首先会展开其所有的参数宏(以及参数宏内的参数宏),但如果该参数宏与该宏重名,则不予展开。

为了解决这个问题,我们可以借助第二条规则

2. 当扫描并展开完一个 function-like 的宏之后,会对展开的内容进行再扫描,继续展开其中的其他宏。


  1. #define CONCAT(lhs_, rhs_) CONCAT_I(lhs_, rhs_)
  2. #define CONCAT_I(lhs_, rhs_) lhs_ ## rhs_
  3. CONCAT(1, 2)
  4. CONCAT(1, CONCAT(2, CONCAT(3, 4)))

  5. // gcc -E ...
  6. // ...
  7. // 12
  8. // 1234
复制代码


我们的方案是将宏重命名,这使得再扫描过程中宏与参数宏将不再重名,即可顺利展开。

以下是 CONCAT(1, CONCAT(2, CONCAT(3, 4))) 的展开过程,可以帮助理解,为了方便我们将三个CONCAT按出现顺序命名为C1、C2、C3


  1. CONCAT(1, CONCAT(2, CONCAT(3, 4)))
  2. -> CONCAT_I(1, CONCAT(2, CONCAT(3, 4)))      // 扫描C1。C2与C1同名,不展开。
  3. -> CONCAT_I(1, CONCAT_I(2, CONCAT(3, 4)))    // 重新扫描C1。C2与C1不同名,展开;C3与C2同名,不展开。
  4. -> CONCAT_I(1, CONCAT_I(2, CONCAT_I(3, 4)))  // 重新扫描C2。C3与C2不同名,展开。
  5. -> CONCAT_I(1, CONCAT_I(2, 34))              // C3展开完毕
  6. -> CONCAT_I(1, 234)                          // C2展开完毕
  7. -> 1234                                      // C1展开完毕
复制代码


水水的就是这样。

评分

参与人数 3威望 +3 贡献 +3 收起 理由
帅气可爱魔理沙 + 1 + 1 =w=
岩川黑鬼 + 1 + 1 我就偶尔被迫写这种cat2...
LH_Mouse + 1 + 1 喵!

查看全部评分

回复

使用道具 举报

9

主题

25

帖子

137

积分

注册会员

魔理魔理魔~

Rank: 2

威望
5
经验
97
贡献
5
发表于 2015-12-30 18:23:04 | 显示全部楼层
/me 欺负 楼主

点评

抓住沙沙就是一顿揉搓蹭(●´艸`)ヾ  发表于 2015-12-31 09:08
Yare Yare Daze
回复 支持 反对

使用道具 举报

10

主题

108

帖子

465

积分

超级版主

RA2DIY 特别行政区行政长官

Rank: 8Rank: 8

威望
4
经验
346
贡献
3
发表于 2015-12-31 00:43:04 这篇帖子是使用手机发表的! | 显示全部楼层
不能递归替换已经是硬伤了,不过拿来倒腾 token 倒是比什么模板不知高到哪里去。模板不能创造新 token 也是原罪之一。

点评

(´・ω・`) 模板要能支持这个那得省多少事了,复杂点的PP宏展开不顺利的时候一堆莫名其妙的错误,比模板还惨...  发表于 2015-12-31 09:13
回复 支持 反对

使用道具 举报

3

主题

74

帖子

283

积分

中级会员

Rank: 3Rank: 3

威望
9
经验
182
贡献
9
发表于 2015-12-31 20:01:25 | 显示全部楼层

点评

么么哒(๑˃̵ᴗ˂̵)و  发表于 2016-1-6 08:41
#if !idppc
/*
** float q_rsqrt( float number )
*/
float Q_rsqrt( float number )
{
        long i;
        float x2
回复

使用道具 举报

10

主题

108

帖子

465

积分

超级版主

RA2DIY 特别行政区行政长官

Rank: 8Rank: 8

威望
4
经验
346
贡献
3
发表于 2016-1-1 01:27:06 | 显示全部楼层
  1. #define CAR_(h_, ...)  h_
  2. #define CAR(h_, ...)   CAR_(h_, __VA_ARGS__)

  3. #define CDR_(h_, ...)  __VA_ARGS__
  4. #define CDR(h_, ...)   CDR_(h_, __VA_ARGS__)

  5. #define CONS_(h_, ...) h_, __VA_ARGS__
  6. #define CONS(h_, ...)  CONS_(h_, __VA_ARGS__)
复制代码

点评

后来发现还是()()()序列好用,CAR、CDR都改成操作这个的了(๑˃̵ᴗ˂̵)و  发表于 2016-1-6 08:41
We will prevail!! www.lhmouse.com
回复 支持 反对

使用道具 举报

1

主题

21

帖子

80

积分

注册会员

Rank: 2

威望
0
经验
59
贡献
0
发表于 2016-1-5 20:52:31 | 显示全部楼层
这个真不错,学习了。

点评

教科书上是比较少讲这类东西 (๑˃̵ᴗ˂̵)و  发表于 2016-1-6 08:42
回复 支持 反对

使用道具 举报

0

主题

10

帖子

45

积分

新手上路

Rank: 1

威望
1
经验
32
贡献
1
发表于 2016-1-22 21:10:13 | 显示全部楼层
棒棒棒!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|ISO/IEC C++ China Unofficial    

GMT+8, 2017-8-19 07:46 , Processed in 0.065900 second(s), 27 queries , Xcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表