仅使用预处理器进行字符串连接?

Using the only the preprocessor for string concatenation?

是否可以在语言(本例中为 C++)之外连接带引号的字符串文字?

也就是说,我可以定义MY_MACRO(a,b,c)并这样使用它吗:

MY_MACRO("one", "two", "three")

并将其扩展为:"onetwothree"?

用例是将属性及其消息应用于函数签名,例如:

MY_ATTRIBUTE_MACRO("this", "is", "the reason") int foo() { return 99; }

它会导致:

[[nodiscard("thisisthe reason")]] int foo() { return 99; }

该语言已经进行了字符串连接!

这个:

"hi" "James"

变成一个字符串文字。

这意味着您根本不需要任何预处理器技巧。


您只需要在宏的输出中使用它:

#define MY_ATTRIBUTE_MACRO(x,y,z) [[nodiscard(x y z)]]

现在这个:

MY_ATTRIBUTE_MACRO("this", "is", "the reason") int foo() { return 99; }

这是:

[[nodiscard("this" "is" "the reason")]] int foo() { return 99; }

这实际上已经是你想要的了,因为隐式字符串连接(发生在 宏扩展之后):

[[nodiscard("thisisthe reason")]] int foo() { return 99; }

Translation phase 4:

[lex.phases]/4: Preprocessing directives are executed, macro invocations are expanded, and _­Pragma unary operator expressions are executed. If a character sequence that matches the syntax of a universal-character-name is produced by token concatenation, the behavior is undefined. A #include preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted.

Translation phase 6:

[lex.phases]/6: Adjacent string literal tokens are concatenated.

我不确定您所说的“语言之外”是什么意思,但是在 C++ 中,任何由空格 分隔的字符串文字都会隐式连接成一个。因此,您的 MY_MACRO 定义实际上 非常 简单:

#include <iostream>

#define MY_MACRO(a, b, c) a b c

int main()
{
    std::cout << MY_MACRO("one", "two", "three") << std::endl;
    return 0;
}

这个短程序的输出就是你所要求的:onetwothree


注意:关于 curiosity/interest,通常建议在定义部分将宏参数括在圆括号中,以避免计算的不良副作用。但是,在这种情况下,使用这样的括号将不起作用,并且会破坏隐式连接:

#define MY_MACRO(a, b, c) (a) (b) (c) // Broken!