tags:

views:

1301

answers:

6

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?

+5  A: 

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);
)
Jesse
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...)
Michael Burr
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.
Andres Jaan Tack
A: 

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

Raymond Martineau
+9  A: 

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
derobert
Nice find. Learn something new everyday.
Jesse
Would he need to take care that it is not used in any other compile unit? Otherwise, it might be unexpectedly skipping numbers.
Jesse
@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.
derobert
+4  A: 

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;
}
Josh Kelley
This is a good answer! Thanks for the pointer; I admit I find the Boost.Preprocessor documentation a little dense.
Andres Jaan Tack
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.
Josh Kelley
A: 

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?

+1  A: 

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;
}
Magnus