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 expand开发者_运维问答ing to:
static void Unique___LINE__(void) {}
Is this possible to do?
The problem is that when you have a macro replacement, the preprocessor will only expand the macros recursively if neither the stringizing operator #
nor the token-pasting operator ##
are applied to it. So, you have to use some extra layers of indirection, you can use the token-pasting operator with a recursively expanded argument:
#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}
Then, __LINE__
gets expanded to the line number during the expansion of UNIQUE
(since it's not involved with either #
or ##
), and then the token pasting happens during the expansion of TOKENPASTE
.
It should also be noted that there is also the __COUNTER__
macro, which expands to a new integer each time it is evaluated, in case you need to have multiple instantiations of the UNIQUE
macro on the same line. Note: __COUNTER__
is supported by MS Visual Studio, GCC (since V4.3), and Clang, but is not standard C.
How to auto-generate unique variable names with the line number in them by using macros
This is a generic answer, not addressing the narrow specifics of the OP's question, since there are already adequate answers for that.
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__)
Example program:
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;
}
SAMPLE OUTPUT:
Notice that the intentionally-produced build errors reveal the autogenerated variable names as counter_56
, counter_55
, and counter_54
, as shown here!:
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]
Full output:
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 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) DOES NOT DO WHITE SPACE CORRECTLY for macro expansion - it adds whitespace (which certainly destroy's the result as being a C Identifier for further pre-processing) **, clang simply doesn't do # or * macro expansion as a C Preprocessor is expected to for decades. The prime example is compiling X11, macro "Concat3" is broken, it's result is now MISNAMED C Identifier, which of course fails to build. and i'm beginning to thing build fails are their profession.
I think the answer here is "new C that breaks standards is bad C", these hacks always choose to (clobber namespaces) they change defaults for no reason but do not really "improve C" (excepting to their own say so: which i say is contraption made to explain why they get away with all the breakage no one yet has made them responsible for).
It's not a problem that the earlier C pre-processors did not support UNIq_()__ because they supported #pragma which allows "compiler brand hackery in code to be flagged as hackery" and also function just as well WITHOUT effecting standards: just as changing defaults is useless wonton breakage, and just as changing what a function does while using the same name (namespace clobbering) is ... malware in my opinion
精彩评论