C预处理器,递归宏

imr*_*mre 14 macros c-preprocessor

为什么M(0)和N(0)有不同的结果?

#define CAT_I(a, b) a ## b
#define CAT(a, b) CAT_I(a, b)

#define M_0 CAT(x, y)
#define M_1 whatever_else
#define M(a) CAT(M_, a)
M(0);       //  expands to CAT(x, y)

#define N_0() CAT(x, y)
#define N_1() whatever_else
#define N(a) CAT(N_, a)()
N(0);       //  expands to xy
Run Code Online (Sandbox Code Playgroud)

Jam*_*lis 17

实际上,这取决于您对语言标准的解释.例如,在mcpp下,一个严格符合语言标准文本的预处理器实现,第二个产生CAT(x, y);[从结果中删除了额外的换行符]:

C:\dev>mcpp -W0 stubby.cpp
#line 1 "C:/dev/stubby.cpp"
        CAT(x, y) ;
        CAT(x, y) ;
C:\dev>
Run Code Online (Sandbox Code Playgroud)

一个已知的不一致在C++语言规范(同样矛盾是存在于C规范,虽然我不知道在哪里的缺陷列表是C).规范声明最终CAT(x, y)不应该被宏替换.意图可能是应该进行宏观替换.

引用链接的缺陷报告:

早在20世纪80年代,一些WG14人员就明白,"非替代"措辞与制作伪代码的尝试之间存在细微差别.

该委员会的决定是,"野外"的实际计划不会冒险进入这一领域,并且试图减少不确定性并不值得改变实施或计划的一致性状态的风险.


那么,为什么我们得到不同的行为M(0)N(0)与最常见的预处理程序实现?在替换中M,第二次调用CAT完全由第一次调用产生的标记组成CAT:

M(0) 
CAT(M_, 0)
CAT_I(M_, 0)
M_0
CAT(x, y)
Run Code Online (Sandbox Code Playgroud)

如果M_0被定义为替换为CAT(M, 0),则替换将无限地递归.预处理器规范通过停止宏替换明确禁止这种"严格递归"替换,因此CAT(x, y)不替换宏.

但是,在替换中N,第二次调用仅部分由第一次调用产生的标记CAT组成:CAT

N(0)
CAT(N_, 0)       ()
CAT_I(N_, 0)     ()
N_0              ()
CAT(x, y)
CAT_I(x, y)
xy
Run Code Online (Sandbox Code Playgroud)

这里第二次调用CAT部分地由第一次调用CAT和部分来自其他标记的标记形成,即()来自替换列表N.替换不是严格递归的,因此当CAT替换第二次调用时,它不能产生无限递归.