我想创建一个基于行号命名的函数的 C 宏。 我认为可以像这样实现(实际函数将在大括号内包含语句):
#define UNIQUE static void Unique_##__LINE__(void) {}
我希望这将扩展为类似于以下内容:static void Unique_23(void) {}
那样行不通。使用标记连接时,定位宏被视为字面量,最终展开为:
static void Unique___LINE__(void) {}
这个能做到吗?
我想创建一个基于行号命名的函数的 C 宏。 我认为可以像这样实现(实际函数将在大括号内包含语句):
#define UNIQUE static void Unique_##__LINE__(void) {}
我希望这将扩展为类似于以下内容:static void Unique_23(void) {}
那样行不通。使用标记连接时,定位宏被视为字面量,最终展开为:
static void Unique___LINE__(void) {}
这个能做到吗?
问题在于当你有一个宏替换时,如果没有应用字符串化运算符#
或标记粘贴运算符##
,预处理器只会递归展开宏。因此,你必须使用一些额外的间接层,可以使用带有递归展开参数的标记粘贴运算符:
#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}
然后,__LINE__
在UNIQUE
展开时被扩展为该行的行号(因为它与#
或##
没有关联),然后在TOKENPASTE
展开过程中进行记号粘贴。
还应该注意到还有__COUNTER__
宏,每次被评估时都会扩展为一个新的整数,以防需要在同一行上具有多个UNIQUE
宏的实例化。注意:__COUNTER__
受到MS Visual Studio、GCC(自V4.3起)和Clang支持,但不是标准的C语言。
__COUNTER__
(自V4.3起)。我编辑了答案。 - sleske这是一个通用的答案,不涉及到 OP 问题的具体细节,因为已经有足够的答案了。
我主要从 @Jarod42 这里 学到了这个方法,也从 @Adam.Rosenfield 这里 学到了一些。
#define CONCAT_(prefix, suffix) prefix##suffix
/// Concatenate `prefix, suffix` into `prefixsuffix`
#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)
/// Make a unique variable name containing the line number at the end of the
/// name. Ex: `uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0;` would
/// produce `uint64_t counter_7 = 0` if the call is on line 7!
#define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)
示例程序:
macro_make_unique_variable_name_with_line_number.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#define CONCAT_(prefix, suffix) prefix##suffix
/// Concatenate `prefix, suffix` into `prefixsuffix`
#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)
/// Make a unique variable name containing the line number at the end of the
/// name. Ex: `uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0;` would
/// produce `uint64_t counter_7 = 0` if the call is on line 7!
#define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Autogenerate unique variable names containing the line number "
"in them.\n\n");
uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_54 = 0;
uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_55 = 0;
uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_56 = 0;
// Uncomment this to suppress the errors.
// (void)counter_54;
// (void)counter_55;
// (void)counter_56;
return 0;
}
样例输出:
请注意,故意产生的构建错误揭示了自动生成的变量名为counter_56
、counter_55
和counter_54
,如下所示!:
macro_make_unique_variable_name_with_line_number.c:56:40: error: unused variable ‘counter_56’ [-Werror=unused-variable]
macro_make_unique_variable_name_with_line_number.c:55:40: error: unused variable ‘counter_55’ [-Werror=unused-variable]
macro_make_unique_variable_name_with_line_number.c:54:40: error: unused variable ‘counter_54’ [-Werror=unused-variable]
完整输出:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 macro_make_unique_variable_name_with_line_number.c -o bin/a -lm && bin/a
macro_make_unique_variable_name_with_line_number.c: In function ‘main’:
macro_make_unique_variable_name_with_line_number.c:56:40: error: unused variable ‘counter_56’ [-Werror=unused-variable]
56 | uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_56 = 0;
| ^~~~~~~
macro_make_unique_variable_name_with_line_number.c:39:33: note: in definition of macro ‘CONCAT_’
39 | #define CONCAT_(prefix, suffix) prefix##suffix
| ^~~~~~
macro_make_unique_variable_name_with_line_number.c:45:43: note: in expansion of macro ‘CONCAT’
45 | #define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)
| ^~~~~~
macro_make_unique_variable_name_with_line_number.c:56:14: note: in expansion of macro ‘MAKE_UNIQUE_VARIABLE_NAME’
56 | uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_56 = 0;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
macro_make_unique_variable_name_with_line_number.c:55:40: error: unused variable ‘counter_55’ [-Werror=unused-variable]
55 | uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_55 = 0;
| ^~~~~~~
macro_make_unique_variable_name_with_line_number.c:39:33: note: in definition of macro ‘CONCAT_’
39 | #define CONCAT_(prefix, suffix) prefix##suffix
| ^~~~~~
macro_make_unique_variable_name_with_line_number.c:45:43: note: in expansion of macro ‘CONCAT’
45 | #define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)
| ^~~~~~
macro_make_unique_variable_name_with_line_number.c:55:14: note: in expansion of macro ‘MAKE_UNIQUE_VARIABLE_NAME’
55 | uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_55 = 0;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
macro_make_unique_variable_name_with_line_number.c:54:40: error: unused variable ‘counter_54’ [-Werror=unused-variable]
54 | uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_54 = 0;
| ^~~~~~~
macro_make_unique_variable_name_with_line_number.c:39:33: note: in definition of macro ‘CONCAT_’
39 | #define CONCAT_(prefix, suffix) prefix##suffix
| ^~~~~~
macro_make_unique_variable_name_with_line_number.c:45:43: note: in expansion of macro ‘CONCAT’
45 | #define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)
| ^~~~~~
macro_make_unique_variable_name_with_line_number.c:54:14: note: in expansion of macro ‘MAKE_UNIQUE_VARIABLE_NAME’
54 | uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0; // `uint64_t counter_54 = 0;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
GCC不需要“包装”(或实现),除非结果需要“字符串化”。GCC具有功能,但所有功能都可以使用普通的C版本1完成(有人认为伯克利4.3 C更快,值得学习如何使用)。
** Clang(llvm)对于宏展开无法正确处理空格-它添加了空格(这肯定会破坏结果作为C标识符进行进一步预处理),clang只是不做#或*宏展开,因为C预处理器预期已经做了几十年。最主要的例子是编译X11,宏“Concat3”已经损坏,它的结果现在被错误地命名为C标识符,当然无法构建。我开始认为构建失败是他们的职业。
我认为答案在于“打破标准的新C是不好的C”,这些黑客总是选择(破坏命名空间),他们没有理由改变默认设置,但并没有真正“改进C”(除了他们自己说的话:我认为这是一个构想,用来解释为什么他们能够逃脱所有违规行为,而没有人让他们负责)。
早期的C预处理器不支持UNIq_()__并不是问题,因为它们支持#pragma,允许在代码中标记“编译器品牌hackery”,而且同样可以很好地工作,而不会影响标准:就像更改默认值是无用的破坏一样,使用相同名称更改函数的操作(命名空间污染)也是...在我看来是恶意软件。
__LINE__
之外的任何宏都适用(尽管后者是常见用例)。 - Ciro Santilli OurBigBook.com