GCC 预处理:如何正确地将宏展开的结果转成字符串

  2022 年 02 月 21 日   C++  

众所周知,在 C/C++ 预处理的过程中,不会处理字符串字面量中的宏,比如:

#define PI 3.14

int main() {
  std::cout << "pi=PI\n";
}

这只会输出 pi=PI,而非 pi=3.14

那如果要把宏展开的结果放到字符串中,该怎么做呢?

直接使用 # 操作符?

众所周知,C/C++ 预处理有一个字符串化操作符(#),可以将宏的参数进行转化为字符串。但直接使用它,将无法完成我们想要的效果:

#define PI 3.14
#define STR(x) #x

int main() {
  std::cout << "pi=" STR(PI) "\n";
}

输出结果:

pi=PI

为什么宏参数 PI 没有被展开?原因是 GCC 中的字符串化操作符 # 不会先展开它的参数,而是直接把它的参数用双引号(")包裹起来。关于这个规则可以参考 Stringification - The C Preprocessor

解决方法:间接地使用 # 操作符

至于解决方法,在上面的参考链接也有给出,即多定义一个宏,间接地使用 # 操作符。比如:

#define PI 3.14
#define _STR(x) #x
#define STR(x) _STR(x)

int main() {
  std::cout << "pi=" STR(PI) "\n";
}

输出结果:

pi=3.14

解释:

这个规则同样适用于 ## 操作符

虽然上面的文档没有说,但这种规则也适用于连接操作符##),因此也可以通过:

#define _CONCAT(a, b) a##b
#define CONCAT(a, b) _CONCAT(a, b)

使得宏参数在连接的时候也先展开其内容。