nju-pa摸鱼记3-NEMU中宏的源码阅读

一、前言

有关于如何使用宏定义检测某个宏是否存在在前两篇专栏中已经讨论得差不多了,本篇将目光转回NEMU的代码框架,探讨include/macro.h中的有关宏的原理。

二、macro.h

NEMU代码框架中的include/macro.h文件中定义了一些列宏

1
2
3
4
5
6
7
8
// keep the code if a boolean macro is defined
#define IFDEF(macro, ...) MUXDEF(macro, __KEEP, __IGNORE)(__VA_ARGS__)
// keep the code if a boolean macro is undefined
#define IFNDEF(macro, ...) MUXNDEF(macro, __KEEP, __IGNORE)(__VA_ARGS__)
// keep the code if a boolean macro is defined to 1
#define IFONE(macro, ...) MUXONE(macro, __KEEP, __IGNORE)(__VA_ARGS__)
// keep the code if a boolean macro is defined to 0
#define IFZERO(macro, ...) MUXZERO(macro, __KEEP, __IGNORE)(__VA_ARGS__)

从注释中可以看出,这些宏都是根据布尔宏(即若被定义则只有可能被定义为01)是否被定义或者定义为什么值而决定后面变长参数表中的代码在预编译时是否被保留。由于它们的原理是相通的,所以这里只分析宏IFDEF(macro, ...),并使用自顶向下的方法,从顶层宏定义一直追踪到最深层的定义。

IFDEF(macro, ...)的第一个参数是一个布尔宏,第二个参数是变长列表,可以传入语句或代码块。若宏macro被定义则保留代码,反之什么都不保留。在它的顶层定义中使用了宏MUXDEF(macro, a, b),它是一个广义的选择器,不管ab的类型是什么。在这里,__KEEP__IGNORE是两个宏函数,它们的定义如下:

1
2
#define __IGNORE(...)
#define __KEEP(...) __VA_ARGS__

它们的功能和它们的名字相同,__KEEP将保留所有的输入,而__IGNORE将所有输入舍弃。所以,在这里宏IFDEF使用宏MUXDEF选择__KEEP宏函数或__IGNORE宏函数中的一个,然后再将选出来的宏函数作用到后面的代码上,决定这段代码的去留。

举个例子:

1
2
3
4
5
6
7
8
9
10
#include "macro.h"
#define FOO 1

int main()
{
IFDEF(FOO, printf("Hello, World!\n");)
// => __KEEP(printf("Hello, World!\n");)
// => printf("Hello, World!\n");
return 0;
}

在上面的例子中,因为宏FOO已经被定义了,所以再MUXDEF的作用下,宏函数__KEEP被选择出来作用在printf语句上,最后这个语句被保留了下来。

接下来看宏MUXDEF是怎么实现的:

1
2
3
4
5
6
7
8
#define CHOOSE2nd(a, b, ...) b
#define MUX_WITH_COMMA(contain_comma, a, b) CHOOSE2nd(contain_comma a, b)
#define MUX_MACRO_PROPERTY(p, macro, a, b) MUX_WITH_COMMA(concat(p, macro), a, b)

#define __P_DEF_0 X,
#define __P_DEF_1 X,

#define MUXDEF(macro, X, Y) MUX_MACRO_PROPERTY(__P_DEF_, macro, X, Y)

MUXDEF又调用了宏MUX_MACRO_PROPERTY,它的作用是使用宏concat为宏macro添加一个前缀__P_DEF_,然后将处理过的宏传给宏MUX_WITH_COMMA。上一篇所介绍的技术在这里体现了出来:如果布尔宏被定义了,那么加上前缀后将得到__P_DEF_0__P_DEF_1,然后再定义这两个宏,通过逗号控制最后传入CHOOSE2nd宏的第二个参数为a,最终选择结果为a;如果宏没有被定义,那么加上前缀后的宏不会再进行解释,那么“contain_comma a”部分将作为传入CHOOSE2nd的第一个参数,最终选择结果为b


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!