组合宏会产生错误

Combining macros generate errors

我想用 C 预处理器和给定的接口名称生成 AVR 中断向量的名称。所以我有这个配置文件(Config.h),它包含在另一个头文件中。

#define INTERFACE                   C, 0
#define BAUD                        19200

和这个获取 ISR 名称的宏

#include "Config.h"
#define ISR_NAME(Name, Vector)      USART ## C0 ## _ ## Vector ## _vect

我的代码中使用了这个宏

#include "Config.h"

ISR(ISR_NAME(INTERFACE, RXC))
{
    // Some stuff
}

而且效果很好。但是接口的名称应该根据给定的 INTERFACE 设置,为此我将宏 ISR_NAME 更改为以下

#include "Config.h"
#define CATENATE(Prefix, Index)             Prefix ## Index
#define ISR_NAME(Name, Vector)              USART ## CATENATE(Name) ## _ ## Vector ## _vect

像以前一样使用宏

#include "Config.h"

ISR(ISR_NAME(INTERFACE, RXC))
{
    // Some code
}

此解决方案会产生大量警告和错误

Severity    Code    Description Project File                                Line
Message     in expansion of macro 'ISR_NAME'                                ...
Message     in expansion of macro 'ISR_NAME'                                ...
Message     in expansion of macro 'ISR_NAME'                                ...
Message     in expansion of macro 'INTERFACE'                               ...
Message     in expansion of macro 'INTERFACE'                               ...
Error       expected ')' before numeric constant    File                    ...
Error       expected ')' before numeric constant    File                    ...
Error       recipe for target 'File.o' failed   File                        ...
Error       pasting ")" and "_" does not give a valid preprocessing token   ...

这里出了什么问题?为什么这个额外的宏会产生这个错误?

此错误消息 "clearly" 描述的问题

Error       pasting ")" and "_" does not give a valid preprocessing token

适用于此扩展:

USART ## CATENATE(Name) ## _ ## Vector ## _vect

("Clearly"这里开个玩笑,错误描述的很清楚,但是你需要翻一大堆垃圾才能找到信息丰富的错误信息。)

当您查看该行时,您会看到 CATENATE(Name) 连接到 _。但这不是预处理器看到的,因为标记粘贴运算符 ## 之前应用 重新扫描替换文本以进行进一步的宏扩展。预处理器看到的是 ) ## _,正如它指出的那样,它不会产生合法令牌。 (预处理器还加入了 USART ## CATENATE 生成合法但未定义的令牌 USARTCATENATE,它没有费心警告你。但这可能会产生其他后果,从而产生其他错误。)

如果你想标记粘贴宏展开的结果,无论是简单的宏展开还是类似函数的宏展开,你都需要使用间接标记粘贴宏,比如你的CATENATE:

#define ISR_NAME(Name, Vector) \
  CATENATE(USART, CATENATE(CATENATE(Name), CATENATE(_, CATENATE(Vector, _vect))))

当然,这将完全宏扩展 NameVector,这可能不是您想要的。为了得到你想要的,你需要清楚使用 ### 运算符的类函数宏的宏扩展顺序:

  1. 宏参数将替换为调用中的参数,但作为 ### 运算符的参数出现的那些参数除外。参数在此替换之前已完全展开。

  2. 应用了 ### 运算符。

    • # 必须后接宏参数名;相应的参数被转换为字符串文字,没有对参数执行任何宏扩展。
    • 如果参数出现在 ## 之前或之后,该参数将被其相应的参数替换,同样不会执行任何宏扩展。 (如果替换参数对应于一个空参数,则插入一个特殊的空占位符。)然后每次出现的两个由 ## 分隔的标记(或占位符)都被替换为一个新标记,表示两个原始标记的串联令牌。
  3. 最后,重新扫描生成的替换文本以进行宏扩展。

在(重新)扫描期间识别类似函数的宏参数,这就是为什么 CATENATE(INTERFACE) 在宏定义中按预期工作(不兼容的 C 预处理器除外,例如历史 MSVC 实现) , 但不在顶层。