宏的“奇妙漂流”

最近因为种种的机缘巧合,在阅读ReactiveCocoa的源码,然后就被它里面对宏的神奇使用所折服了,我想从此将踏上不归之路。。

扬帆起航.jpg

RAC中有一个RACmetamacros.h文件,这个文件里定义了大量的宏,里面对宏的使用到了出神入化的地步,可以在预处理时就得到可变参数个数,可以在书写RAC(obj,x)的时候,就自动提示出obj对象的属性x。今天我们就简单的学习一些关于宏的基础知识,然后举几个例子来具体的分析一下。

首先是关于宏里面各种符号意义的介绍,下面所列出来的是相对比较常见的。

  • \的作用:换行,因为#define只在当前行起作用,为了看的更简单、更直观,可以使用\换行显示。

  • #的作用:它的功能是将其后面的宏参数字符串化,简单地说就是宏变量的左右加上双引号

  • ##的作用:它表示将两个参数连接起来这种运算。注意函数宏必须是有意义的运算,因此你不能直接写AB来连接两个参数,而需要写成例子中的A##B

  • _的作用:用来声明变量(注意在宏里面 _1 也是一个变量,可以看做是变量x1。。)

  • __VA_ARGS__:总体来说就是将左边宏中 ... 的内容原样抄写在右边__VA_ARGS__ 所在的位置。

  • __FILE__:宏在预编译时会替换成当前的源文件名。

  • __LINE__:宏在预编译时会替换成当前的行号。

  • __FUNCTION__:宏在预编译时会替换成当前的函数名称。

有了以上的这些基础知识,我们先来看一个比较简单的例子。(例子来源于孙源大神的一篇文章,可惜没有注释,可能大伙看了也不明白是怎么出来的。。)

[Macro]预处理时计算可变参数个数
#define COUNT_PARMS2(_a1, _a2, _a3, _a4, _a5, RESULT, ...) RESULT
#define COUNT_PARMS(...) COUNT_PARMS2(__VA_ARGS__, 5, 4, 3, 2, 1)
int count = COUNT_PARMS(1,2,3); // 预处理时count==3

下面就来一步步分析一下是怎么在预处理的时候通过宏得到count的个数的。

  • COUNT_PARMS(1,2,3)中的1,2,3是可变参数,将1,2,3当做一个整体参数传入到COUNT_PARMS2(__VA_ARGS__, 5, 4, 3, 2, 1)中的__VA_ARGS__中,对应上面所列出的第5点规则:【 __VA_ARGS__总体来说就是将左边宏中 ... 的内容原样抄写在右边 __VA_ARGS__所在的位置】
    所以此时COUNT_PARMS(1,2,3)就等价于COUNT_PARMS2(1,2,3, 5, 4, 3, 2, 1)
  • 看一下COUNT_PARMS2的定义:COUNT_PARMS2(_a1, _a2, _a3, _a4, _a5, RESULT, ...) RESULT,其中前面的_a1, _a2, _a3, _a4, _a5, 相当于是变量,对应上面的第4条规则_ 的作用:用来声明变量(注意在宏里面 _1 也是一个变量,可以看做是变量x1。。)
    因为宏定义前5个是变量,所以会将COUNT_PARMS2(1,2,3, 5, 4, 3, 2, 1)中的1,2,3,5,4参数依次赋值给_a1, _a2, _a3, _a4, _a5, 此时COUNT_PARMS2(_a1, _a2, _a3, _a4, _a5, RESULT, ...)RESULT对应的就是第6个参数3,后面的那个...可变参数对应的就是最后的两个参数2和1了,我们看到宏定义最后将RESULT返回,而这个RESULT恰好就是我们想要的结果了,这里思考一下如果我们求COUNT_PARMS(1,2,3,4,5,6,7)的可变参数个数的话,RESULT会是多少呢

带着疑问,我们继续看个更复杂一些的----ReactiveCocoa中的metamacro_argcount宏,这也是一个用来在预编译的时候求可变参数个数的宏。

先看一下它的定义
#define metamacro_argcount(...)  metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define metamacro_at(N, ...)  metamacro_concat(metamacro_at, N)(__VA_ARGS__)
#define metamacro_concat(A, B) metamacro_concat_(A, B)
#define metamacro_concat_(A, B) A ## B
#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)
#define metamacro_head(...)  metamacro_head_(__VA_ARGS__, 0)
#define metamacro_head_(FIRST, ...) FIRST

我们假设int a = metamacro_argcount(1,2, 3);//a = 3
看这个是怎么一步一步的计算出来的。步骤跟上面的那个简单例子基本一致。

注意我们为了书写方便,在此用X替代20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1

  • 1,2,3当参数传入宏里面
    metamacro_argcount(1,2,3)就相当于metamacro_at(20,1,2,3,X)

  • 看下metamacro_at宏的定义metamacro_at(N, ...),所以第一步中的20相当于这里的N,而1,2,3,X则对应宏中的可变参数 ...

  • 继续往下走,看metamacro_concat_(A, B) A ## B这个宏,对应上述的规则3,此处会将传入的A和B做一个拼接。而从第二步对应过来的话,那么metamacro_at对应的是A,而20对应的是B,将它们做一个拼接得到metamacro_at20,所以此时第二步中metamacro_concat(metamacro_at, N)(__VA_ARGS__),就等价于metamacro_at20(1,2,3,X)因为要将__VA_ARGS__替换成前面的可变参数1,2,3,X。此时我们将X展开,就会得到metamacro_at20(1,2,3,20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

  • 查看metamacro_at20宏发现里面20个变量,将上一步中的前20个变量依次代入即可。此时metamacro_at20宏中的...可变参数就是剩下的3,2,1了,这时候我们就得到得到metamacro_head(3,2,1)

  • 继续往下查看metamacro_head_(__VA_ARGS__, 0)的实现,将3,2,1代入后得到metamacro_head_(3,2,1, 0)。这个宏返回的是第一个元素,而这个3正好是可变参数的个数了。

需要注意的一点是,我们所输入的可变参数的个数是有最大值限制的,比如刚刚RAC中的最大可变参数数目是20,如果我们输入的超过了20的话,那么就会出现错误。

如果看到这里,你没有蒙圈的话,那么恭喜你已经初步进入“宏”的奇妙漂流中了,之后会加入对它的数学分析。。

要相信这只是一扇打开更广阔世界的门而已。。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容