将引号传递给宏

Passing quote symbol to macro

#include <iostream>

#define DEF(A) #A

int main()
{
    std::cout << DEF(qwer) << std::endl; //prints: qwer
    std::cout << DEF("qwer") << std::endl; //prints: "qwer"
    std::cout << DEF("qwer) << std::endl; //error, but I want to print "qwer without a second quote
}

如何将只有一个引号的参数传递给宏?

你不能那样做。

预处理器对语法的要求比编译器少很多,但是你给它提供的仍然必须是一系列有效的标记,而未终止的字符串文字不是有效的标记。

如果您的系统使用 ASCII / ISO 8859-k 作为其单字节编码——这不是完全可移植的,但现在异常情况很少见——那么您可以写:

std::cout << DEF(\x22qwer) << std::endl;

注意明显相似

std::cout << DEF(\u0022qwer) << std::endl;

将不起作用。为了理解为什么不,重要的是要理解第一个为什么(或如何)起作用,因为该机制并不像它看起来那么明显。

编译器的输入必须由一系列的token和white组成space,DEF函数的参数也不例外。当编译器最初对包含 DEF 函数的行进行标记时,它不知道其参数将被字符串化,因此参数必须可分解为标记。

幸运的是,预处理器在它认为的标记方面非常自由。对于 C++,可能的预处理器标记是:(§2.4 [lex.pptoken])

header-name
identifier
pp-number
character-literal
user-defined-character-literal
string-literal
user-defined-string-literal
preprocessing-op-or-punc
each non-white-space character that cannot be one of the above

\x22qwer 不符合上述任何条件,但 \ 是一个非白色-space 字符,不能是其他标记之一,并且 x22qwer 是一个 identifier。因此 DEF 的参数包含两个标记而没有干预 whitespace,并且 stringify 运算符尽职地将其转换为字符串文字,然后根据字符串文字规则重新解释,其中连续的字符 \x22 替换为双引号(或执行字符编码中对应于 0x22 的任何字符)。

另一方面,DEF(\u0022qwer) 将不起作用。原因是 \u0022 是一个 universal-character-name,所以 \u0022qwer 满足 identifier 的词汇产生式.这使它成为一个单一的令牌。但它不是有效的标识符标记,因为 \u0022 不在作为有效标识符字符的通用字符名称列表中(根据 §2.10 [lex.name] Table 2)。

如果您略读了 stringify 运算符的详细描述,您可能会惊讶地发现反斜杠在 stringify 操作中被原样传递。需要仔细阅读。 §16.3.2 [cpp.stringize]:

the original spelling of each preprocessing token in the argument is retained in the character string literal, except for special handling for producing the spelling of string literals and character literals: a \ character is inserted before each " and \ character of a character literal or string literal (including the delimiting " characters).

那里有一个重要的条件:只有引号和反斜杠 是字符或字符串文字 的一部分会被重新转义。 DEF(\x22qwer) 中的反斜杠不是字符或字符串文字的一部分,因此它会原封不动地通过。