文本替换宏

来自cppreference.com
 
 
C++ 语言
 
 

预处理器支持文本宏替换。亦支持仿函数文本宏替换。

目录

[编辑] 语法

#define 标识符 替换列表(可选) (1)
#define 标识符( 形参 ) 替换列表(可选) (2)
#define 标识符( 形参, ... ) 替换列表(可选) (3) (C++11 起)
#define 标识符( ... ) 替换列表(可选) (4) (C++11 起)
#undef 标识符 (5)

[编辑] 解释

[编辑] #define 指令

#define 指令定义 标识符 为宏,即指示编译器以 替换列表 替换 identifier 的所有后继出现,这能可选地额外处理。若标识符已定义为任何类型的宏,则程序为病式,除非定义等同。

[编辑] 仿对象宏

仿对象宏以 替换列表 替换每次出现的被定义 标识符#define 指令的版本 (1) 准确表现如此。

[编辑] 仿函数宏

仿函数宏以 替换列表 替换每次出现的被定义 标识符 ,可选地接受一定量实参,然后替换 替换列表 中任何 形参 的对应出现。

仿函数宏语法类似函数调用语法:每个后随作为下个预处理记号的 ( 的宏名实例引入为 替换列表 所替换的记号序列。序列以匹配的 ) 记号终止,跳过中间的匹配左右括号对。

对于版本 (2) ,实参数量必须与宏定义中的形参数量相同。对于版本 (3,4) ,实参数量必须多于 (C++20 前)不少于 (C++20 起)形参数量(不计 ... )。否则程序为病式。若标识符不在函数记号中,即它自身后无括号,则它完全不被替换。

#define 指令的版本 (2) 定义简单仿函数宏。

#define 指令的版本 (3) 定义有可变数量实参的仿函数宏。附加实参(是谓可变实参)可用 __VA_ARGS__ 标识符访问,它会被与要被替换的标识符一起提供的实参替换。

#define 指令的版本 (4) 定义有可变数量实参的仿函数宏,但无常规实参。附加实参(是谓可变实参)只能用 __VA_ARGS__ 标识符访问,它会被与要被替换的标识符一起提供的实参替换。

注意:若仿函数宏包含不为匹配的左右括号对所保护的逗号(最常于类型名中找到,如于 assert(std::is_same_v<int, int>);BOOST_FOREACH(std::pair<int,int> p, m) ),则将逗号转译为宏实参分隔符,导致实参数量不匹配所致的编译失败。

[编辑] ### 运算符

仿函数宏中, 替换列表 中标识符前的 # 运算符经通过标识符运行形参替换,并将结果以引号环绕,等效地创建字符串字面量。另外,预处理器添加反斜杠以转义环绕内嵌字符串字面量的引号,若它存在,并按需要双写字符串中的反斜杠。移除所有前导和尾随空白符,并将文本中部(但非内嵌字符串字面量中内)的任何空白符序列缩减成单个空格。此操作被称为“字符串化”,若字符串化的结果不是合法的字符串字面量,则行为未定义。

# 出现于 __VA_ARGS__ 前时,整个展开的 __VA_ARGS__ 被包在引号中:

#define showlist(...) puts(#__VA_ARGS__)
showlist();            // 展开成 puts("")
showlist(1, "x", int); // 展开成 puts("1, \"x\", int")
(C++11 起)

替换列表 中任何二个相继标识符间的 ## 运算符在二个标识符(首先未被宏展开)上运行形参替换,然后连接结果。此操作被称为“连接”或“记号粘贴”。只有一同组成合法记号的记号才可以粘贴:组成更长标识符的标识符、组成数字的数位,或组成 += 的运算符 += 。不能以粘贴 /* 创建注释,因为考虑文本宏替换前,注释就被移除了。若连接的结果不是合法记号,则行为未定义。

注意:一些编译器提供扩展,允许 ## 出现于逗号后及 __VA_ARGS__ 前,此情况下 ## 在存在可变实参时不做任何事,但在不存在可变实参时移除逗号:这使得可以定义如 fprintf (stderr, format, ##__VA_ARGS__) 的宏。

[编辑] #undef 指令

#undef 指令取消定义 标识符 ,即取消 #define 指令所作的 标识符 定义。若标识符未关联到宏,则忽略该指令。

[编辑] 预定义宏

下列宏名已预定义于任何翻译单元中。

__cplusplus
指代所用的 C++ 标准版本,展开成值 199711L(C++11 前) 、 201103L(C++11) 、 201402L(C++14) 或 201703L(C++17)
(宏常量)
__STDC_HOSTED__
(C++11)
若实现有宿主(运行在 OS 下)则展开成整数常量 1 ,若独立(不随 OS 运行)则展开成 0
(宏常量)
__FILE__
展开成当前文件名,作为字符串字面量,能以 #line 指令更改
(宏常量)
__LINE__
展开成源文件行号,整数常量,能以 #line 指令更改
(宏常量)
__DATE__
展开成翻译日期,形式为 "Mmm dd yyyy" 的字符串。若月的日数小于 10 则 "dd" 的首字符为空格。月份名如同以 std::asctime() 生成
(宏常量)
__TIME__
展开成翻译时间,形式为 "hh:mm:ss" 的字符串字面量
(宏常量)
__STDCPP_DEFAULT_NEW_ALIGNMENT__
(C++17)
展开成 std::size_t 字面量,其值为调用不具对齐的 operator new 所保证的对齐(更大的对齐将传递给具对齐重载,如 operator new(std::size_t, std::align_val_t)
(宏常量)

下列附加宏名可能为实现所预定义。

__STDC__
若存在则为实现定义值,典型地用于指示 C 一致性
(宏常量)
__STDC_VERSION__
(C++11)
若存在则为实现定义值
(宏常量)
__STDC_ISO_10646__
(C++11)
wchar_t 使用 Unicode ,则展开成 yyyymmL 形式的整数常量,日期指示受支持 Unicode 的最近版本
(宏常量)
__STDC_MB_MIGHT_NEQ_WC__
(C++11)
若基础字符集的宽字符编码可以不等于其窄编码,则展开成 1 ,如在为 wchar_t 使用 Unicode 的基于 EBCDIC 的系统上。
(宏常量)
__STDCPP_STRICT_POINTER_SAFETY__
(C++11)
若实现支持严格 std::pointer_safety 则展开成 1
(宏常量)
__STDCPP_THREADS__
(C++11)
若程序能拥有多于一个执行线程则展开成 1
(宏常量)

这些宏的值(除了 __FILE____LINE__ )在翻译单元间保持常量。试图重定义或取消定义这些宏导致未定义行为。

注意:在每个函数作用域内部,有个特殊的函数局域预定义变量,名为 __func__(C++11 起) ,定义为保有实现定义格式函数名的静态字符数组。它不是预处理器宏,但它被与 __FILE____LINE__ 一起使用,例如由 assert

[编辑] 示例

#include <iostream>
 
// 制造函数工厂并使用它
#define FUNCTION(name, a) int fun_##name() { return a;}
 
FUNCTION(abcd, 12)
FUNCTION(fff, 2)
FUNCTION(qqq, 23)
 
#undef FUNCTION
#define FUNCTION 34
#define OUTPUT(a) std::cout << #a << '\n'
 
int main()
{
    std::cout << "abcd: " << fun_abcd() << '\n';
    std::cout << "fff: " << fun_fff() << '\n';
    std::cout << "qqq: " << fun_qqq() << '\n';
    std::cout << FUNCTION << '\n';
    OUTPUT(million);               // 注意缺少引号
}

输出:

abcd: 12
fff: 2
qqq: 23
34
million

[编辑] 参阅

文本替换宏C 文档