【问题标题】:Preprocessor facility __COUNTER__ in Visual C++Visual C++ 中的预处理器工具 __COUNTER__
【发布时间】:2011-10-20 07:52:17
【问题描述】:

我需要在编译时在整个代码中生成一系列序列号。我以这样的方式尝试了“__COUNTER__”:

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

结果和我预期的一样完美:

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

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

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

结果是:

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
{
  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)

并在标题中定义:

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

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

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

解决方案: 感谢 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 块中。

【问题讨论】:

    标签: c++ visual-studio counter c-preprocessor


    【解决方案1】:

    您不能使用预处理器来执行此操作,因为每个编译单元都是单独预处理的。为此需要一个运行时解决方案。您可以创建一个全局单例,每个需要唯一标识符的地方都可以使用此单例定义一个静态 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) 的初始化顺序。您可以确定它们将始终是唯一的,但您不能依赖它们具有某些特定的值或顺序。因此,当例如将它们保存到文件中 - 您应该为此将它们转换为某种中性表示。

    【讨论】:

    • 使用预编译的头文件,其中的__COUNTER__ 会导致更多问题。 Suma 的 +1 解决方案非常便携,并且回避了预处理问题。
    【解决方案2】:

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

    【讨论】:

    • 是的,这是在您自己的预处理器中执行此操作的一种方式,您实际上可以在其中做更多的事情
    猜你喜欢
    • 1970-01-01
    • 2011-06-10
    • 2010-11-11
    • 1970-01-01
    • 2010-10-13
    • 1970-01-01
    • 2015-06-24
    • 1970-01-01
    • 2011-06-06
    相关资源
    最近更新 更多