类函数宏的扩展创建一个单独的标记
Expansion of function-like macro creates a separate token
我刚刚发现 gcc 似乎将类函数宏的扩展结果视为单独的标记。这是一个显示 gcc 行为的简单示例:
#define f() foo
void f()_bar(void);
void f()bar(void);
void f()-bar(void);
当我执行 gcc -E -P test.c
(运行 只是预处理器)时,我得到以下输出:
void foo _bar(void);
void foo bar(void);
void foo-bar(void);
看起来,在前两个定义中,gcc 在扩展宏之后插入 space 以确保它是一个单独的标记。这真的是这里发生的事情吗?
这是任何标准强制执行的吗(我找不到关于该主题的文档)?
我想使 _bar
成为同一个标记的一部分。有什么办法吗?我可以使用令牌连接运算符 ##
但它需要几个级别的宏(因为在实际代码中 f() 更复杂)。我想知道是否有一个简单(并且可能更易读)的解决方案。
我能想到的唯一方法(如果你不能使用标记连接运算符##)是使用传统的(预标准)C 预处理:
gcc -E -P -traditional-cpp test.c
输出:
void foo_bar(void);
void foobar(void);
void foo-bar(void);
看起来,在前两个定义中,gcc 在扩展宏之后插入 space 以确保它是一个单独的标记。这真的是这里发生的事情吗?
是的。
这是任何标准强制要求的吗(我找不到关于该主题的文档)?
是的,尽管允许实现插入多个白色space来分隔标记。
f()_bar
经过词法分析后,这里有 4 个标记(在这个阶段它们实际上是预处理器标记,但我们称它们为标记):f
、(
、)
和 _bar
.
类函数宏替换语义(如 C11,6.10.3 中所定义)必须将 3 个标记 f
、(
、)
替换为一个新的 foo
。不允许对其他令牌进行操作并更改最后一个 _bar
令牌。为此,实现必须插入至少一个 whitespace 以保留 _bar
标记。否则结果将是 foo_bar
这是一个单一的标记。
gcc
预处理器有点 documents 在这里:
Once the input file is broken into tokens, the token boundaries never change, except when the ‘##’ preprocessing operator is used to paste tokens together. See Concatenation. For example,
#define foo() bar
foo()baz
==> bar baz
not
==> barbaz
在另一种情况下,如 f()-bar
,有 5 个标记:f
、(
、)
、-
和 bar
. (-
是 C 中的标点符号,而 _bar
中的 _
只是标识符标记的一个字符)。该实现不必在此处插入标记分隔符(如白色space),因为在宏替换后 -bar
仍被视为来自 C 语法的两个单独的标记。
gcc
预处理器 (cpp
) 不会在这里插入 whitespace 只是因为它没有必要。在 cpp
documentation 中,在标记间距上写着(在不同的问题上):
However, we would like to keep space insertion to a minimum, both for aesthetic reasons and because it causes problems for people who still try to abuse the preprocessor for things like Fortran source and Makefiles.
我没有在这个答案中解决您的问题,但我认为您必须使用明确指定的运算符来连接标记:##
标记粘贴运算符。
我刚刚发现 gcc 似乎将类函数宏的扩展结果视为单独的标记。这是一个显示 gcc 行为的简单示例:
#define f() foo
void f()_bar(void);
void f()bar(void);
void f()-bar(void);
当我执行 gcc -E -P test.c
(运行 只是预处理器)时,我得到以下输出:
void foo _bar(void);
void foo bar(void);
void foo-bar(void);
看起来,在前两个定义中,gcc 在扩展宏之后插入 space 以确保它是一个单独的标记。这真的是这里发生的事情吗?
这是任何标准强制执行的吗(我找不到关于该主题的文档)?
我想使 _bar
成为同一个标记的一部分。有什么办法吗?我可以使用令牌连接运算符 ##
但它需要几个级别的宏(因为在实际代码中 f() 更复杂)。我想知道是否有一个简单(并且可能更易读)的解决方案。
我能想到的唯一方法(如果你不能使用标记连接运算符##)是使用传统的(预标准)C 预处理:
gcc -E -P -traditional-cpp test.c
输出:
void foo_bar(void);
void foobar(void);
void foo-bar(void);
看起来,在前两个定义中,gcc 在扩展宏之后插入 space 以确保它是一个单独的标记。这真的是这里发生的事情吗?
是的。
这是任何标准强制要求的吗(我找不到关于该主题的文档)?
是的,尽管允许实现插入多个白色space来分隔标记。
f()_bar
经过词法分析后,这里有 4 个标记(在这个阶段它们实际上是预处理器标记,但我们称它们为标记):f
、(
、)
和 _bar
.
类函数宏替换语义(如 C11,6.10.3 中所定义)必须将 3 个标记 f
、(
、)
替换为一个新的 foo
。不允许对其他令牌进行操作并更改最后一个 _bar
令牌。为此,实现必须插入至少一个 whitespace 以保留 _bar
标记。否则结果将是 foo_bar
这是一个单一的标记。
gcc
预处理器有点 documents 在这里:
Once the input file is broken into tokens, the token boundaries never change, except when the ‘##’ preprocessing operator is used to paste tokens together. See Concatenation. For example,
#define foo() bar foo()baz ==> bar baz not ==> barbaz
在另一种情况下,如 f()-bar
,有 5 个标记:f
、(
、)
、-
和 bar
. (-
是 C 中的标点符号,而 _bar
中的 _
只是标识符标记的一个字符)。该实现不必在此处插入标记分隔符(如白色space),因为在宏替换后 -bar
仍被视为来自 C 语法的两个单独的标记。
gcc
预处理器 (cpp
) 不会在这里插入 whitespace 只是因为它没有必要。在 cpp
documentation 中,在标记间距上写着(在不同的问题上):
However, we would like to keep space insertion to a minimum, both for aesthetic reasons and because it causes problems for people who still try to abuse the preprocessor for things like Fortran source and Makefiles.
我没有在这个答案中解决您的问题,但我认为您必须使用明确指定的运算符来连接标记:##
标记粘贴运算符。