Creating C macro with ## and __LINE__ (token concatenation with positioning macro)

I want to create a C macro that creates a function with a name based on the line number. I thought I could do something like (the real function would have statements within the braces):

#define UNIQUE static void Unique_##__LINE__(void) {}

Which I hoped would expand to something like:

static void Unique_23(void) {}

That doesn't work. With token concatenation, the positioning macros are treated literally, ending up expanding to:

static void Unique___LINE__(void) {}

Is this possible to do?

69001 次浏览

问题是,当您有一个宏替换时,如果字符串化操作符 #和令牌粘贴操作符 ##都没有应用到它,那么预处理器只会递归地展开宏。因此,您必须使用一些额外的间接层,您可以使用带有递归扩展参数的令牌粘贴操作符:

#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宏。注意: MS Visual Studio、 GCC (自 V4.3以来)和 Clang 支持 __COUNTER__,但不是标准 C。

GCC doesn't require "wrapping" (or realizing) unless the result needs to be "stringified". Gcc has features but ALL can be done with plain C version 1 (and some argue Berkeley 4.3 C is so much faster it's worth learning how to use).

Clang (llvm)不能正确地进行宏扩展——它增加了空格(这当然会破坏作为 C 标识符进行进一步预处理的结果) ,Clang 只是不能像 C 预处理器预期的那样进行 # 或 * 宏扩展。主要的例子是编译 X11,宏“ Concat3”被破坏,它的结果现在是 MISNAMED C 标识符,当然无法构建。我开始觉得建筑失败是他们的职业。

我认为这里的答案是“违反标准的新 C 是不好的 C”,这些黑客总是选择(重击名称空间)他们无缘无故地改变默认值,但并没有真正“改善 C”(除了他们自己这么说: 我说这是一个奇妙的装置,用来解释为什么他们逃脱了所有的破坏,没有人让他们负责)。


早期的 C 预处理器不支持 UNIq_() _ _ 不是问题,因为它们支持 # 杂注,它允许“代码中的编译器品牌骇客被标记为骇客”,而且在不影响标准的情况下也能正常工作: 就像更改默认值是无用的馄饨破坏,就像更改一个函数在使用相同名称(名称空间重击)时的功能是... ... 在我看来是恶意软件

如何使用宏自动生成带行号的唯一变量名

这是一个通用的答案,不涉及 OP 问题的狭隘细节,因为已经有了足够的答案。

I learned this primarily from @ Jarod42 here, but also from @Adam.Rosenfield here.

#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__)

示例程序:

宏 _ make _ only _ 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_56counter_55counter_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