c++ - C/C++ : How should preprocessor directive work on macros argument list?

标签 c++ c c-preprocessor standards preprocessor

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/

相关文章:

c++ - 将 Image*[3] 传递给 const Image*[3] 不能用 const_cast<Image*[3]> 完成,替代方案?

c - 为什么需要 DLL 才能使用 sqlite3?

c++ - 比较vector <string>并使用C++打印是或否

c++ - 如何使用 'C' 或 'C++' 创建独立的 Windows 程序或应用程序

c++ - 如何强制执行额外的预处理器宏扫描

c - 宏等价物(C/C++)?

c++ - 用于定义数字的数字常量之前的预期 unqualified-id

c - C预处理器语句是C语言的一部分吗?

c++ - fstream 'outfile' 没有命名类型

c++ - G++ -Wshadow 不警告静态成员阴影