Visual C++中的预处理器设施__COUNTER__

gcuhipw9  于 2024-01-09  发布在  其他
关注(0)|答案(3)|浏览(139)

我需要在编译时在整个代码中生成一系列连续的数字。我尝试了“COUNTER”这样的方法:

  1. void test1()
  2. {
  3. printf("test1(): Counter = %d\n", __COUNTER__);
  4. }
  5. void test2()
  6. {
  7. printf("test2(): Counter = %d\n", __COUNTER__);
  8. }
  9. int main()
  10. {
  11. test1();
  12. test2();
  13. }

字符串
结果正如我所料,非常完美:

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


然后我将“COUNTER”分散到不同的.cpp文件中:

  1. In Foo.cpp:
  2. Foo::Foo()
  3. {
  4. printf("Foo::Foo() with counter = %d\n", __COUNTER__);
  5. }
  6. In Bar.cpp:
  7. Bar::Bar()
  8. {
  9. printf("Bar::Bar() with counter = %d\n", __COUNTER__);
  10. }
  11. In Main.cpp:
  12. int main()
  13. {
  14. Foo foo;
  15. Bar bar;
  16. }


结果是:

  1. Foo::Foo() with counter = 0
  2. Bar::Bar() with counter = 0


在我看来,“COUNTER”是作为每个编译单元的**变量提供的。
我想要的是一个在整个代码中有效的全局计数器。
这用于在调试构建中进行测试,我希望实现以下目标:
假设我在整个代码中都有try/catch块(多个.cpp文件中的子系统或模块)。在运行时,程序在循环中运行,在每个循环中,所有的try块都将按顺序执行(顺序无关紧要),并且我想逐个测试代码对每个try/catch异常的React。例如,第一次在循环中,#1 try/catch块抛出一个异常;第二次在循环中,#2 try/catch块抛出一个异常,依此类推。
我计划有一个这样的全局计数器:

  1. int g_testThrowExceptionIndex = 0;


每次try/catch:

  1. try
  2. {
  3. TEST_THROW_EXCEPTION(__COUNTER__)
  4. //My logic is here...
  5. }
  6. catch(...)
  7. {
  8. //Log or notify...
  9. }


Macro应该是这样的:

  1. #define TEST_THROW_EXCEPTION(n) \
  2. if(g_testThrowExceptionIndex == n)\
  3. {\
  4. g_testThrowExceptionIndex++;\
  5. throw g_testThrowExceptionIndex;\
  6. }\


如果不能在编译时生成序列号,我必须像这样编写宏:

  1. TEST_THROW_EXCEPTION(THROW_INDEX_1)
  2. ......
  3. TEST_THROW_EXCEPTION(THROW_INDEX_N)


在header中,定义:

  1. #define THROW_INDEX_1 0
  2. #define THROW_INDEX_2 1
  3. ......


问题是,每次你添加一个try/catch块,你想测试,你必须通过#define创建一个新的常量,并把这个数字放入宏。更糟糕的是,如果你从代码中删除一些try/catch块怎么办?你还必须更新你的#define列表。

解决方案:感谢Suma的想法,我最终得到了这样的东西:

  1. #if defined(_DEBUG) && defined(_EXCEPTION_TEST)
  2. extern int g_testThrowExceptionIndex;
  3. struct GCounter
  4. {
  5. static int counter; // used static to guarantee compile time initialization
  6. static int NewValue() {return counter++;}
  7. };
  8. #define TEST_THROW_EXCEPTION \
  9. static int myConst = GCounter::NewValue();\
  10. if(g_testThrowExceptionIndex == myConst)\
  11. {\
  12. g_testThrowExceptionIndex++;\
  13. throw 0;\
  14. }
  15. #else
  16. #define TEST_THROW_EXCEPTION
  17. #endif


在main.cpp中:

  1. #if defined(_DEBUG) && defined(_EXCEPTION_TEST)
  2. int g_testThrowExceptionIndex= 0;
  3. int GCounter::counter= 0;
  4. #endif


然后你可以把“TEST_THROW_EXCEPTION”放在任何你想测试的try/catch块中。

o4hqfura

o4hqfura1#

你不能使用预处理器来实现这一点,因为每个编译单元都是单独预处理的。这需要一个运行时解决方案。你可以创建一个全局单例,每个需要唯一标识符的地方都可以使用这个单例定义一个静态int。

  1. struct GCounter
  2. {
  3. static int counter; // used static to guarantee compile time initialization
  4. static int NewValue() {return counter++;}
  5. };
  6. int GCounter::counter = 0;
  7. void Foo1()
  8. {
  9. static int ID1 = GCounter::NewValue();
  10. }
  11. void Foo2()
  12. {
  13. static int ID2 = GCounter::NewValue();
  14. }

字符串
注意事项:在多个编译单元中,这些静态值(ID)的初始化顺序没有定义。你可以确保它们总是唯一的,但是你不能依赖它们具有某些特定的值或顺序。因此,当将它们保存到文件中时要小心-你应该将它们转换为某种中立的表示。

展开查看全部
2ledvvac

2ledvvac2#

如果你使用的是MSVC,你可以添加一个预构建步骤来解析文件,并将__COUNTER__扩展为一个超级全局值,而不是一个单元全局值。当然,困难的部分是管理文件,以免引起问题。

8i9zcol2

8i9zcol23#

您可以在项目中的每个源文件的开头使用不同的值声明相同的宏名称,然后在表达式中为其添加__COUNTER__

  1. // include.h
  2. #define _MY_COUNTER_ _FILE_COUNTER_+__COUNTER__
  3. // Foo.cpp
  4. #define _FILE_COUNTER_ 1000
  5. #include <include.h>
  6. ...
  7. printf("Foo::Foo(): Counter = %d\n", _MY_COUNTER_);
  8. // Bar.cpp
  9. #define _FILE_COUNTER_ 2000
  10. #include <include.h>
  11. ...
  12. printf("Bar::Bar()): Counter = %d\n", _MY_COUNTER_);

字符串
如果你不需要通用宏,只需要使用源文件中的表达式:

  1. _FILE_COUNTER_+__COUNTER__


当然,它不是连续的。但至少,保证了唯一的编号(确保每个文件中有足够的空间),并且您将能够确定宏在项目中的确切位置。

展开查看全部

相关问题