visualc++中的预处理器设施__COUNTER__

Preprocessor facility __COUNTER__ in Visual C++

本文关键字:设施 COUNTER 处理器 预处理 visualc++      更新时间:2023-10-16

我需要在编译时在整个代码中生成一系列连续的数字。我用如下方式尝试了"__COUNTER__":

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

结果如我所料是完美的:

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

然后我把"__COUNTER__"分散到不同的。cpp文件中:

In Foo.cpp:
Foo::Foo()
{
  printf("Foo::Foo() with counter = %dn", __COUNTER__);
}
In Bar.cpp:
Bar::Bar()
{
  printf("Bar::Bar() with counter = %dn", __COUNTER__);
}
In Main.cpp:
int main()
{
  Foo foo;
  Bar bar;
}

结果是:

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

在我看来,"__COUNTER__"是作为每个编译单元变量提供的。

我想要的是一个在整个代码中有效的全局计数器。

这是用于在调试构建中测试,我想达到这个目标:

假设我在整个代码中都有try/catch块(多个.cpp文件中的子系统或模块)。在运行时,程序在循环中运行,在每个循环中,所有的try块将按顺序执行(顺序无关紧要),我想测试代码对每个try/catch的异常的反应,一个接一个。例如,第一次在循环中,#1 try/catch块抛出一个异常;第二次在循环中,#2 try/catch块抛出一个异常,等等等等。

我计划有一个这样的全局计数器:

int g_testThrowExceptionIndex = 0;

在每个try/catch中:

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

宏应该是这样的:

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

由于无法在编译时生成序列号,我必须这样编写宏:

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

在header中定义:

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

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

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

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

#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
在main.cpp:

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

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

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

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();
}

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

看到您正在使用MSVC,您总是可以添加一个预构建步骤来解析文件并将__COUNTER__扩展为超全局值而不是单位全局值。当然,困难的部分是管理文件,以免引起问题……