How can I generate unique values in the C preprocessor?
Asked Answered
M

6

23

I'm writing a bunch of related preprocessor macros, one of which generates labels which the other one jumps to. I use them in this fashion:

MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL();
  MAKE_LABEL();
)

I need some way to generate unique labels, one for each inner MAKE_LABEL call, with the preprocessor. I've tried using __LINE__, but since I call MAKE_LABEL inside another macro, they all have the same line and the labels collide.

What I'd like this to expand to is something like:

MAKE_FUNNY_JUMPING_LOOP(
  my_cool_label_1:  // from first inner macro
  ...
  my_cool_label_2:  // from second inner macro
  ...
)

Is there a way to generate hashes or auto-incrementing integers with the preprocessor?

Margalit answered 15/7, 2009 at 17:22 Comment(2)
I am using ICC (the STM prototype 3.0; based on v11, I think).Margalit
For C++: #9950032Glynnis
C
16

As others noted, __COUNTER__ is the easy but nonstandard way of doing this.

If you need extra portability, or for other cool preprocessor tricks, the Boost Preprocessor library (which works for C as well as C++) will work. For example, the following header file will output a unique label wherever it's included.

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/slot/slot.hpp>

#if !defined(UNIQUE_LABEL)
#define UNIQUE_LABEL
#define BOOST_PP_VALUE 1
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#else
#define BOOST_PP_VALUE BOOST_PP_INC(BOOST_PP_SLOT(1))
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#endif


BOOST_PP_CAT(my_cool_label_, BOOST_PP_SLOT(1)):

Sample:

int main(int argc, char *argv[]) {
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    return 0;
}

preprocesses to

int main(int argc, char *argv[]) {
    my_cool_label_1:
    printf("%x\n", 1234);
    my_cool_label_2:
    printf("%x\n", 1234);
    my_cool_label_3:
    return 0;
}
Clubby answered 15/7, 2009 at 17:47 Comment(2)
This is a good answer! Thanks for the pointer; I admit I find the Boost.Preprocessor documentation a little dense.Margalit
I find it dense too. I usually have to fiddle a bit with the syntax to find something that works, but it's cool when it does.Clubby
N
22

If you're using GCC or MSVC, there is __COUNTER__.

Other than that, you could do something vomit-worthy, like:

#ifndef USED_1
#define USED_1
1
#else
#ifndef USED_2
#define USED_2
2
/* many many more */
#endif
#endif
Nailbrush answered 15/7, 2009 at 17:39 Comment(5)
Nice find. Learn something new everyday.Noman
Would he need to take care that it is not used in any other compile unit? Otherwise, it might be unexpectedly skipping numbers.Noman
@Jesse: Each compile unit gets its own preprocessor run, so COUNTER should always start at 0. However, it is quite possible that someone else may user COUNTER in the same compilation unit, leading to holes in the sequence.Nailbrush
.... holes in the sequence, which could be a good thing if you want unique values (simple ones), (but you really should use UUIDs to do that), but of course thats not what you want here...Unwisdom
__COUNTER__ takes on a different value each time it is accessed. How do you plan to use it to define a an identifier and then refer to that same identifier?Baronet
F
20

I use this:

#define MERGE_(a,b)  a##b
#define LABEL_(a) MERGE_(unique_name_, a)
#define UNIQUE_NAME LABEL_(__LINE__)

int main()
{
    int UNIQUE_NAME = 1;
    return 0;
}

... and get the following:

int main()
{
    int unique_name_8 = 1;
    return 0;
}
Farandole answered 3/11, 2009 at 14:14 Comment(3)
And how do you actually use the variable? You can't.Affair
.... you would need to add a non unique name in the macro to access that unique name, which puts you back to square one... it seems that we get too crazy with the preprocessor and start thinking of ways it can do ALL the work for us, no matter how unrealistic.. not to mention time consuming. (me, i type something unique and move on.. heh) but i think the COUNTER solution for MSVC/GCC is good, and if you are using something else then use boost stuff... first two answers are goodUnwisdom
@Unwisdom A unique name would be used entirely within a single macro invocation. That's useful.Baronet
C
16

As others noted, __COUNTER__ is the easy but nonstandard way of doing this.

If you need extra portability, or for other cool preprocessor tricks, the Boost Preprocessor library (which works for C as well as C++) will work. For example, the following header file will output a unique label wherever it's included.

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/slot/slot.hpp>

#if !defined(UNIQUE_LABEL)
#define UNIQUE_LABEL
#define BOOST_PP_VALUE 1
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#else
#define BOOST_PP_VALUE BOOST_PP_INC(BOOST_PP_SLOT(1))
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#endif


BOOST_PP_CAT(my_cool_label_, BOOST_PP_SLOT(1)):

Sample:

int main(int argc, char *argv[]) {
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    return 0;
}

preprocesses to

int main(int argc, char *argv[]) {
    my_cool_label_1:
    printf("%x\n", 1234);
    my_cool_label_2:
    printf("%x\n", 1234);
    my_cool_label_3:
    return 0;
}
Clubby answered 15/7, 2009 at 17:47 Comment(2)
This is a good answer! Thanks for the pointer; I admit I find the Boost.Preprocessor documentation a little dense.Margalit
I find it dense too. I usually have to fiddle a bit with the syntax to find something that works, but it's cool when it does.Clubby
N
6

I can't think of a way to automatically generate them but you could pass a parameter to MAKE_LABEL:

#define MAKE_LABEL(n) my_cool_label_##n:

Then...

MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL(0);
  MAKE_LABEL(1);
)
Noman answered 15/7, 2009 at 17:35 Comment(2)
Of course it can also include the LINE macro as part of the MAKE_LABEL() definition so your other macros can still use it (as long as you don't use those macros more than once in yet some other macro...)Waggon
Good call. I'm actually already doing this for the MAKE_FUNNY_JUMPING_LOOP macro, since there are relatively few of those and they're easy to describe. I figured if it was just one, I could deal with that. Spreading the manual method to every macro like this, though, is too much for me.Margalit
T
0

You could do this:

#define MAKE_LABEL() \
do {                 \   
my_cool_label:       \
/* some stuff */;    \
goto my_cool_label;  \
/* other stuff */;   \
} while (0) 

This keeps the scope of the label local, allowing any number of them inside the primary macro.

If you want the labels to be accessed more globally, it's not clear how your macro "MAKE_FUNNY_JUMPING_LOOP" references these labels. Can you explain?

Trepidation answered 16/7, 2009 at 12:32 Comment(0)
K
-2

It doesn't seem possible with a standard preprocessor, although you could fake it out by putting parameters within MAKE_LABEL or MAKE_FUNNY_JUMPING_LOOP, and use token pasting to create the label.

There's nothing preventing you from making your own preprocessing script that does the automatic increment for you. However, it won't be a standard C/C++ file in that case.

A list of commands available: http://www.cppreference.com/wiki/preprocessor/start

Kablesh answered 15/7, 2009 at 17:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.