5

I want to create a unique variable name on the fly.

This is my code:

int call(int i)
{
    return i;
}
 
#define XCAT3(a, b, c)    a ## b ## c
 
#define CALL_2(arg, place, line) int XCAT3(cl, place, line) = call(arg);
 
#define CALL_1(arg)    CALL_2(arg, __FUNCTION__, __LINE__)
 
int main(int argc, char* argv[])
{
    CALL_1(1); /* this is line 20 */
    return 0;
}

This does work in GCC (http://ideone.com/p4BKQ) but unfortunately not in Visual Studio 2010 or 2012.

The error message is:

test.cpp(20): error C2143: syntax error : missing ';' before 'function_string'

test.cpp(20): error C2143: syntax error : missing ';' before 'constant'

test.cpp(20): error C2106: '=' : left operand must be l-value

How can I create an on-the-fly unique variable name with C++?

Solution:

#define CAT2(a,b) a##b
#define CAT(a,b) CAT2(a,b)
#define UNIQUE_ID CAT(_uid_,__COUNTER__)

int main(int argc, char* argv[])
{
    int UNIQUE_ID = 1;
    int UNIQUE_ID = 2;
    return 0;
}
Community
  • 1
  • 1
Felix Dombek
  • 13,664
  • 17
  • 79
  • 131
  • 2
    http://stackoverflow.com/questions/1082192/how-to-generate-random-variable-names-in-c-using-macros – Derek Oct 03 '12 at 15:15

2 Answers2

6

For a unique identifier, many implementations offer the __COUNTER__ preprocessor macro, that expands to an ever increasing number with every use.

#define CAT(a,b) CAT2(a,b) // force expand
#define CAT2(a,b) a##b // actually concatenate
#define UNIQUE_ID() CAT(_uid_,__COUNTER__)

auto UNIQUE_ID() = call(1); // may be _uid_0
auto UNIQUE_ID() = call(2); // may be _uid_1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • +1, that is good to know, but i'd really like to know what's wrong with my code :| – Felix Dombek Oct 03 '12 at 15:01
  • 1
    @Felix: As was said, `__FUNCTION__` yields a string literal like `"main"`, not something that can be used as part of an identifier. Actually, your code shouldn't compile at all, not even on GCC. – Xeo Oct 03 '12 at 15:08
  • Accepted because this is what I need, but: I started a Visual Studio 2008 default commandline project and another one in VS2012, and in neither one them the __COUNTER__ gets expanded. Complete source: `#define UNIQUE_ID uid##__COUNTER__ int main(int argc, char* argv[]) { int UNIQUE_ID = 1; int UNIQUE_ID = 2; return 0; }` gives the error `error C2374: 'uid__COUNTER__' : redefinition; multiple initialization` have you got an idea why? – Felix Dombek Oct 03 '12 at 16:25
  • @Felix: Good question. Try `#define EXPAND(Arg) Arg` and `#define UNIQUE_ID() _uid_ ## EXPAND(__COUNTER__)`. – Xeo Oct 03 '12 at 16:32
  • That cannot work because the ## paste operator takes its arguments literally. But to EXPAND the counter is a good idea and worked! Now I just have to get it pasted correctly ... – Felix Dombek Oct 03 '12 at 16:40
  • @Felix: Well... `#define CAT(a,b) CAT2(a,b)`, `#define CAT2(a,b) a##b`, `#define UNIQUE_ID() CAT(_uid_,__COUNTER__)` should work in this case. May or may not need `EXPAND` around `__COUNTER__`. – Xeo Oct 03 '12 at 16:43
  • The additional level of indirection worked! Thanks a lot for your help! – Felix Dombek Oct 03 '12 at 16:49
2

You need to defer token pasting until after the parameters place and line are recursively expanded in the macro CALL_LATER2. You do that by moving the ## operations to a separate macro -- as long as ## does not appear in the body of CALL_LATER2, all of its arguments will be prescanned for macros:

#define XCAT3(a, b, c)    a ## b ## c
#define CALL_LATER2(fun, h, place, line) \
    auto XCAT3(calllater, place, line) = \
        call_later((fun), (h));

However, this still won't do what you want, as __FUNCTION__ expands to a string with " characters, not to something that can be pasted into an identifier. You need to instead just base your created name off __LINE__ and ensure that the fact that you can end up with duplicates in different compilation units is not a problem (If they're local to some function, that should be fine, or you can put them in an anonymous namespace.)

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226