Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preprocessor facility __COUNTER__ in Visual C++

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.

like image 970
Ping Avatar asked Aug 04 '11 19:08

Ping


2 Answers

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.

like image 121
Suma Avatar answered Sep 18 '22 15:09

Suma


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...

like image 20
Necrolis Avatar answered Sep 21 '22 15:09

Necrolis