Preprocessor facility __COUNTER__ in Visual C++
Asked Answered
S

3

9

I need to generate a series of sequential numbers throughout my code at compile time. I tried "__COUNTER__" in a way like this:

void test1()
{
  printf("test1(): Counter = %d\n", __COUNTER__);
}
void test2()
{
  printf("test2(): Counter = %d\n", __COUNTER__);
}
int main()
{
  test1();
  test2();
}

And the result was perfect as I expected:

test1(): Counter = 0
test2(): Counter = 1

Then I spread "__COUNTER__" out in different .cpp files:

In Foo.cpp:
Foo::Foo()
{
  printf("Foo::Foo() with counter = %d\n", __COUNTER__);
}
In Bar.cpp:
Bar::Bar()
{
  printf("Bar::Bar() with counter = %d\n", __COUNTER__);
}

In Main.cpp:
int main()
{
  Foo foo;
  Bar bar;
}

The result was:

Foo::Foo() with counter = 0
Bar::Bar() with counter = 0

It looks to me that "__COUNTER__" is provided as a per compile unit variable.

What I'd like to have is a global counter that's effective throughout the code.

This is used for testing in a debug build where I want to achieve this goal:

Imagine that I have try/catch blocks throughout the code (a subsystem or a module within multiple .cpp files). At run time the program is running in a loop, within each loop all the try blocks will be executed in orders (in which order doesn't matter), and I want to test how the code react to exception for each try/catch, one by one. For example, the first time in the loop, #1 try/catch block throws an exception; second time in the loop, #2 try/catch block throws an exception, etc etc.

I plan to have a global counter like this:

int g_testThrowExceptionIndex = 0;

In each try/catch:

try
{
  TEST_THROW_EXCEPTION(__COUNTER__)
  //My logic is here...
}
catch(...)
{
  //Log or notify...
}

And the Macro would be something like this:

#define TEST_THROW_EXCEPTION(n) \
        if(g_testThrowExceptionIndex == n)\
        {\
          g_testThrowExceptionIndex++;\
          throw g_testThrowExceptionIndex;\
        }\

Without the ability to generate the sequence number at compile time, I have to write the Macro like this:

TEST_THROW_EXCEPTION(THROW_INDEX_1)
......
TEST_THROW_EXCEPTION(THROW_INDEX_N)

And in the header, defines:

#define THROW_INDEX_1 0
#define THROW_INDEX_2 1
......

The problem is, every time you add a try/catch block and you want to test, you have to create a new constant through #define and put that number into the Macro. Worse, what if you remove some of the try/catch blocks from the code? You have to update your #define list too...

==============

Solution: Thanks for Suma's idea, I ended up with something like this:

#if defined(_DEBUG)  && defined(_EXCEPTION_TEST)
  extern int g_testThrowExceptionIndex;
  struct GCounter
  {
    static int counter; // used static to guarantee compile time initialization
    static int NewValue() {return counter++;}
  };
  #define TEST_THROW_EXCEPTION \
      static int myConst = GCounter::NewValue();\
      if(g_testThrowExceptionIndex == myConst)\
      {\
        g_testThrowExceptionIndex++;\
        throw 0;\
      }
#else
  #define TEST_THROW_EXCEPTION 
#endif

In main.cpp:

#if defined(_DEBUG) && defined(_EXCEPTION_TEST)
  int g_testThrowExceptionIndex= 0;
  int GCounter::counter= 0;
#endif

Then you can put "TEST_THROW_EXCEPTION" in any of your try/catch block you want to test out.

Snout answered 4/8, 2011 at 19:41 Comment(0)
L
5

You cannot do this using preprocessor, as each compile unit is preprocessed separately. A run time solution is needed for this. You may create a global singleton and each place which requires a unique identifier can define a static int using this singleton.

struct GCounter
{
  static int counter; // used static to guarantee compile time initialization
  static int NewValue() {return counter++;}
};

int GCounter::counter = 0;

void Foo1()
{
  static int ID1 = GCounter::NewValue();
}

void Foo2()
{
  static int ID2 = GCounter::NewValue();
}

Note: the order of initialization of those static values (IDs) in multiple compilation units is not defined. You can be sure they will be always unique, but you cannot rely upon them having some particular values or ordering. Therefore be careful when e.g. saving them into a file - you should translate them to some neutral representation for that.

Lasko answered 4/8, 2011 at 19:55 Comment(1)
With precompiled header files, __COUNTER__ within those causes even more problems. Suma's +1 solution is nicely portable and sidesteps preprocessing issues.Armrest
J
1

Seeing as you are using MSVC, you can always add a pre-build step that parses over the files and expands __COUNTER__ to a super global value instead of a unit global value. Of course the hard part is managing the files so as not to cause problems...

Johnstone answered 4/8, 2011 at 21:9 Comment(1)
Yeah, that's a way of doing it in your own preprocessor, where you can actually do more thingsSnout
M
0

You can declare the same macro name with different values at the beginning of each source file in your project, and then add a __COUNTER__ to it in your expression:

// include.h
#define _MY_COUNTER_ _FILE_COUNTER_+__COUNTER__

// Foo.cpp
#define _FILE_COUNTER_ 1000
#include <include.h>
...
printf("Foo::Foo(): Counter = %d\n", _MY_COUNTER_);

// Bar.cpp
#define _FILE_COUNTER_ 2000
#include <include.h>
...
printf("Bar::Bar()): Counter = %d\n", _MY_COUNTER_);

If you don't want the common macro, just use the expression in the source file:

_FILE_COUNTER_+__COUNTER__

Of course, it is not sequential. But at least, the unique number is guaranteed (make sure you have enough room within each file), and you will be able to identify from where exactly in your project macro was issued.

Mccarthy answered 5/1 at 0:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.