C 和 C++ 标准都指定了以下内容:
16.3.1 Argument substitution (C++11)
6.10.3.1 Argument substitution (C11)
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.
可以将本段解释为标准要求:
(1) 首先识别宏参数(逗号分隔),然后分别展开每个参数中包含的所有宏,
或
(2) 展开参数列表中包含的所有宏,然后识别每个参数。
为了说明这一点,让我们考虑以下示例代码:
#define CONDITION (0)
#if (CONDITION > 0)
#define FunctionAlias(par_a, par_b, par_opt, par_c) \
FunctionName(par_a, par_b, par_opt, par_c)
#else
#define FunctionAlias(par_a, par_b, par_c) \
FunctionName(par_a, par_b, par_c)
#endif
int global_a, global_b, global_c;
#if (CONDITION > 0)
int global_opt;
#endif
void FunctionName(int a, int b, int c)
{
}
void AnotherFunction()
{
FunctionAlias(
global_a,
global_b,
#if (CONDITION > 0)
global_opt,
#endif
global_c
);
}
(1) 方法一会产生无效代码:
int global_a, global_b, global_c;
void FunctionName(int a, int b, int c)
{
}
void AnotherFunction()
{
FunctionName(global_a, global_b, #if ((0) > 0) global_opt);
}
(2) 方法 2 生成有效的 C 代码:
int global_a, global_b, global_c;
void FunctionName(int a, int b, int c)
{
}
void AnotherFunction()
{
FunctionName(global_a, global_b, global_c);
}
哪些标准的解释是正确的?
最佳答案
首先,您根本不能将预处理指令放入类函数宏的参数中,因为有些文本比您引用的内容略有下降:
If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the behavior is undefined.
[ N1570, §6.10.3 p11 ].
其次,独立于此,标准需要您称为 (1) 的行为。这是由您引用的这部分文本指定的:
Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.
如果在确定参数之间的边界之前扩展类函数宏的参数,这句话就没有任何意义。你也可以通过实验看到这一点,稍微修改你的代码:
#if (CONDITION > 0)
#define FunctionAlias(par_a, par_b, par_opt, par_c) \
FunctionName(par_a, par_b, par_opt, par_c)
#else
#define FunctionAlias(par_a, par_b, par_c) \
FunctionName(par_a, par_b, par_c)
#endif
int global_a, global_b, global_c;
#if (CONDITION > 0)
int global_opt;
#define GLOBAL_OPT global_opt,
#else
#define GLOBAL_OPT /*nothing*/
#endif
void FunctionName(int a, int b,
#if CONDITION > 0
int opt,
#endif
int c)
{
}
void AnotherFunction()
{
FunctionAlias(
global_a,
global_b,
GLOBAL_OPT
global_c
);
}
如果 CONDITION
未定义或为零,这将编译正常,但当 CONDITION
为非零时,您将收到类似以下行的错误
test.c: In function ‘AnotherFunction’:
test.c:28:17: error: macro "FunctionAlias" requires 4 arguments, but only 3 given
28 | );
| ^
证明 GLOBAL_OPT
在寻找 FunctionAlias
的四个参数之前没有展开。
关于c++ - C/C++ : How should preprocessor directive work on macros argument list?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64452244/