freitass
freitass

Reputation: 6694

How to generate random variable names in C++ using macros?

I'm creating a macro in C++ that declares a variable and assigns some value to it. Depending on how the macro is used, the second occurrence of the macro can override the value of the first variable. For instance:

#define MY_MACRO int my_variable_[random-number-here] = getCurrentTime();

The other motivation to use that is to avoid selecting certain name to the variable so that it be the same as a name eventually chosen by the developer using the macro.

Is there a way to generate random variable names inside a macro in C++?

-- Edit --

I mean unique but also random once I can use my macro twice in a block and in this case it will generate something like:

int unique_variable_name;
...
int unique_variable_name;

In this case, to be unique both variable names have to be random generated.

Upvotes: 31

Views: 40573

Answers (9)

uptime
uptime

Reputation: 61

Although not specifically requested by OP, most programmers want a unique name in a macro so as to define some unique temporary variable, and thus be able to refer to that unique name more than once. The __COUNTER__ technique shown in answers is a great tool toward that. Another level of indirection is required to actually make use of it. For example, building on Dave Dopson's answer:

#define CONCAT(a, b) CONCAT_INNER(a, b)
#define CONCAT_INNER(a, b) a ## b
#define UNIQUE_NAME(base) CONCAT(base, __COUNTER__)

#define MY_MACRO(x, y, z) MY_MACRO_(UNIQUE_NAME(__t), x, y, z)
#define MY_MACRO_(tmpvar, x, y, z) do{ auto tmpvar = (x); \
                                       foobar(tmpvar, y); \
                                       foobar(tmpvar, z); }while(0)

Upvotes: 0

Dave Dopson
Dave Dopson

Reputation: 42704

Try the following:

// One level of macro indirection is required in order to resolve __COUNTER__,
// and get varname1 instead of varname__COUNTER__.
#define CONCAT(a, b) CONCAT_INNER(a, b)
#define CONCAT_INNER(a, b) a ## b

#define UNIQUE_NAME(base) CONCAT(base, __COUNTER__)

void main() {
  int UNIQUE_NAME(foo) = 123;  // int foo0 = 123;
  std::cout << foo0;           // prints "123"
}

__COUNTER__ may have portability issues. If this is a problem, you can use __LINE__ instead and as long as you aren't calling the macro more than once per line or sharing the names across compilation units, you will be just fine.

Upvotes: 49

benoit
benoit

Reputation: 199

use __COUNTER__ (works on gcc4.8, clang 3.5 and Intel icc v13, MSVC 2015)

#define CONCAT_(x,y) x##y
#define CONCAT(x,y) CONCAT_(x,y)
#define uniquename static bool CONCAT(sb_, __COUNTER__) = false

Upvotes: 19

bbrame
bbrame

Reputation: 18544

Here is a succinct macro definition to generate the singleton pattern above.

#define SINGLETON_IMPLIMENTATION(CLASS_NAME) static CLASS_NAME *g##CLASS_NAME = nil; + (CLASS_NAME *)instance { @synchronized(self) { if (g##CLASS_NAME == nil) g##CLASS_NAME = [self new]; } return g##CLASS_NAME; }

#define SINGLETON_DECLARATION(CLASS_NAME) + (CLASS_NAME *)instance;

Upvotes: 1

Mike Patnode
Mike Patnode

Reputation: 471

I needed something similar for a case where I didn't have any profiling tools, but I wanted to count how many threads were inside a particular block of code as well as the amount of time (ticks) spent in that block of code by each thread, In this case every block needed a unique static variable accessible to all threads, and I needed to later reference that variable to incr (I used a logging API rather than printf in the actual code, but this works as well). At first I thought I was very clever by doing the following:

#define PROF_START { \
    static volatile int entry_count##___FUNCTION__##__LINE__ = 0; int *ptc = &entry_count##___FUNCTION__##__LINE__; \
    clock_t start, end; \
    start = times(0); \
    (*ptc)++;

But then I realized this is just silly and the C compiler will simply do this for you, as long as each "static" declaration is its own block:

#include <stdio.h>
#include <sys/times.h>

#define PROF_START { \
    static int entry_count = 0; \
    clock_t start, end; \
    start = times(0); \
    entry_count++;


#define PROF_END \
    end = times(0); \
    printf("[%s:%d] TIMER: %ld:%d\n" , __FUNCTION__, __LINE__, end-start, entry_count); \
    entry_count--; \
    }

Note the open/close brackets in each macro. This isn't strictly thread-safe, but for my profiling purposes I could assume the incr and decr operations were atomic. Here's a recursion sample which uses the macros

#define ITEM_COUNT 5

struct node {
   int data;
   struct node *next;
 };

revsort(struct node **head)
{
  struct node *current = *head;
  struct node *next_item;

  while (current->next)
  {
PROF_START
    next_item = current->next;
    current->next = next_item->next;
    next_item->next = *head;
    *head = next_item;
PROF_END
  }
}

rrevsort(struct node **head)
{
  struct node *current = *head;
  struct node *next_item = current->next;

PROF_START
  current->next = 0;
  if (next_item)
  {
   *head = next_item;
    rrevsort(head);
    next_item->next = current;
  }
PROF_END

}

printnode(struct node *head)
{
  if (head)
  {
    printf("%d ", head->data);
    printnode(head->next);
  }
  else
    printf("\n");

}

main()
{

  struct node node_list[ITEM_COUNT];
  struct node *head = &node_list[0];
  int i;

  for (i=0; i < ITEM_COUNT - 1; i++)
  {
PROF_START
      node_list[i].data = i;
      node_list[i].next = &node_list[i+1];
PROF_END
  }
  node_list[i].data = i;
  node_list[i].next = 0;

  printf("before\n");
  printnode(head);
  revsort(&head);
  printf("after\n");
  printnode(head);
  rrevsort(&head);
  printf("before\n");
  printnode(head);
}

Extra hint, the above program is a common interview question. Excerpt from "nm -A":

macro:0804a034 b entry_count.1715
macro:0804a030 b entry_count.1739
macro:0804a028 b entry_count.1768
macro:0804a02c b entry_count.1775

Upvotes: 2

SingleNegationElimination
SingleNegationElimination

Reputation: 156158

Instead of having the preprocesser create a name, you could possibly let the macro user give you a name.

#define MY_MACRO(varname) int varname = getCurrentTime();

Upvotes: 3

D.Shawley
D.Shawley

Reputation: 59563

Generating unique names in the preprocessor is difficult. The closest you can get is to mangle __FILE__ and __LINE__ into the symbol as popcnt suggests. If you really need to generate unique global symbol names, then I would follow his suggestion about using something like M4 or a Perl script in your build system instead.

You might not need unique names. If your macro can impose a new scope, then you can use the same name since it will simply shadow other definitions. I usually follow the common advice of wrapping macros in do { ... } while (0) loops. This only works for macros which are statements - not expressions. The macro can update variables using output parameters. For example:

#define CALC_TIME_SINCE(t0, OUT) do { \
     std::time_t _tNow = std::time(NULL); \
     (OUT) = _tNow - (t0); \
} while (0)

If you follow a few rules, you are usually pretty safe:

  1. Use leading underscores or similar naming conventions for symbols defined within the macro. This will prevent problems associated with a parameter using the same symbol from occurring.
  2. Only use the input parameters once and always surround them with parentheses. This is the only way to make macros work with expressions as input.
  3. Use the do { ... } while (0) idiom to ensure that the macro is only used as a statement and to avoid other textual replacement problems.

Upvotes: 8

popcnt
popcnt

Reputation: 4645

Add M4 to your build flow? This macro language has some stateful capabilities, and can successfully be intermingled with CPP macros. This is probably not a standard way to generate unique names in a C environment, though I've been able to sucessfully use it in such a manner.

You probably do not not want random, BTW, based on the way you posed your question. You want unique.

You could use __FILE__ and __LINE__ in the macro expansion to get you the uniqueness you seem to be going for... those metavariables get defined within the source file context, so be careful to make sure you get what you are looking for (e.g., perils of more than one macro on the same line).

Upvotes: 16

Mizipzor
Mizipzor

Reputation: 52351

While I don't think its even possible, you should seriously consider making a class out of this.

If you want a random element in a random array to hold a certain value, you can do this:

std::vector< std::vector<int> > m_vec;

Then wrap it in a class, so the developer can only set a number:

void set(int foo)
{
    m_vec[random()][random()] = foo;
}

Is there any reason why you want it a macro? Random variable name sounds dangerous, what if it picks something already defined somewhere else in the code?

Upvotes: -1

Related Questions