__COUNTER__ preprocessor object in Visual C ++

I need to create a series of consecutive numbers during my code at compile time. I tried "__COUNTER__" as follows:

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 distribute "__COUNTER__" 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 is:

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

It seems to me that "__COUNTER__" is provided as a variable for each compilation unit .

What I would like to have is a global counter that is efficient in all code.

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

Imagine I have try / catch blocks throughout the code (subsystem or module in multiple .cpp files). At run time, the program runs in a loop, inside each loop, all try blocks will be executed in orders (in which order it does not matter), and I want to check how the code responds to an exception for each attempt / catch, one at a time. For example, the first time in 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 as follows:

 int g_testThrowExceptionIndex = 0; 

In each try / catch:

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

And the macro will be something like this:

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

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

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

And in the header is defined:

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

The problem is that every time you add a try / catch block and want to test, you need to create a new constant via #define and put it in the macro. Worse, what if you remove some of the try / catch blocks from the code? You also need to update the #define list ...

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

Solution: Thanks for the Suma 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 

You can then put "TEST_THROW_EXCEPTION" in any of your try / catch block that you want to check.

+6
source share
2 answers

You cannot do this with a preprocessor, since each compilation unit is pre-processed separately. This requires a run-time solution. You can create a global singleton, and each place requiring 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 initialization order of these static values โ€‹โ€‹(IDs) in several compilation units is not defined. You can be sure that they will always be unique, but you cannot rely on them having certain meanings or order. Therefore, be careful when, for example, saving them to a file - you must translate them into some kind of neutral representation for this.

+5
source

When you see that you are using MSVC, you can always add a pre-build step that parses files and extends __COUNTER__ to a super global value instead of a global unit value. Of course, the hard part is file management so as not to create problems ...

+1
source

Source: https://habr.com/ru/post/894324/


All Articles