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

2

128

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?

Optimal answered 20/10, 2009 at 20:11 Comment(3)
I think you can get this to work with indirect macro expansion.Pukka
possible duplicate of How to concatenate twice with the C preprocessor and expand a macro as in "arg ## _ ## MACRO"? The same goes for any macro besides __LINE__ (although that is a common use case.Ehman
See also: Preprocessor macros: any way to get a unique variable name and reuse it?Saeger
D
211

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.

Dough answered 20/10, 2009 at 20:33 Comment(10)
I'm afraid that doesn't work with GNU cpp. TOKENPASTE uses LINE as a literal. TOKENPASTE(Unique_, LINE) expands to Unique___LINE__Optimal
@DD: D'oh, fixed now. It needs 2 layers of indirection, not 1.Dough
The __COUNTER__ macro didn't work for me in gcc; although the __LINE__ one did work as advertised.Cravat
Bit of extra information for anyone trying COUNTER, according to msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx it is a macro specific to Microsoft.Autoharp
@Yourdoom: GCC also supports __COUNTER__ (since V4.3). I edited the answer.Alathia
Any explanation of why you need 2 level of indirection? I've tried it with just one, absent of # and ##, and that does not expand it on VS2017. Apparently the same is true for GCC. But if you add a 2nd level of indirection, then it does expand. Magic?Protuberance
@AdamRosenfield can you look at similar question #65799113Ignorance
Can you add some demo usages to this answer please? Also, how do we see the unique names that are generated?Saeger
I found a really good example demo, with output showing the generated variable name. See here: Create an identifier at compile time that has the line number as part of itSaeger
I posted a generic answer with another demo here.Saeger
S
2

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 these lines which suppress the errors in order to
    // force the build errors to now show up so that we can see the
    // variable names in the compile-time error messages. 
    //
    // (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

See also

  1. My answer here where I use my MAKE_UNIQUE_VARIABLE_NAME() macro from above: Printing the size of (sizeof()) a type or variable in an error message at compile time in both C and C++
Saeger answered 17/4, 2022 at 6:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.