我需要在编译时在我的代码中生成一系列连续的数字。我以这样的方式尝试了“__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 块中。
您无法使用预处理器执行此操作,因为每个编译单元都是单独预处理的。为此需要一个运行时解决方案。你可以创建一个全局单例,每个需要唯一标识符的地方都可以使用这个单例定义一个静态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__
扩展为超级全局值而不是单位全局值。当然,最困难的部分是管理文件以免引起问题......
您可以在项目中每个源文件的开头声明具有不同值的相同宏名称,然后在表达式中为其添加
__COUNTER__
:
// include.h
#define _MY_COUNTER_ _FILE_COUNTER_+__COUNTER__
// Foo.cpp
#define _FILE_COUNTER_ 1000
#include <include.h>
...
printf("Foo::Foo(): Counter = %d\n", _MY_COUNTER_);
// Bar.cpp
#define _FILE_COUNTER_ 2000
#include <include.h>
...
printf("Bar::Bar()): Counter = %d\n", _MY_COUNTER_);
如果不需要普通宏,直接使用源文件中的表达式即可:
_FILE_COUNTER_+__COUNTER__
当然,这不是顺序的。但至少,唯一的编号是有保证的(确保每个文件中有足够的空间),并且您将能够确定项目宏的确切发布位置。