Mig*_*igi 9 c++ debugging metaprogramming
我的大多数类都有调试变量,这使它们看起来像这样:
class A
{
// stuff
#ifndef NDEBUG
int check = 0;
#endif
};
Run Code Online (Sandbox Code Playgroud)
和方法可能如下所示:
for (/* big loop */) {
// code
#ifndef NDEBUG
check += x;
#endif
}
assert(check == 100);
Run Code Online (Sandbox Code Playgroud)
几乎没有比#ifndef NDEBUG所有的东西更丑陋.不幸的是,我知道没有编译器可以在没有这些#ifndefs的情况下优化check变量(我不知道是否允许这样做).
所以我试图提出一个让我的生活更轻松的解决方案.以下是它现在的样子:
#ifndef NDEBUG
#define DEBUG_VAR(T) T
#else
template <typename T>
struct nullclass {
inline void operator+=(const T&) const {}
inline const nullclass<T>& operator+(const T&) const { return *this; }
// more no-op operators...
};
#define DEBUG_VAR(T) nullclass<T>
#endif
Run Code Online (Sandbox Code Playgroud)
因此在调试模式下,DEBUG_VAR(T)只生成T.否则它只生成一个"null类",只有no-ops.我的代码看起来像这样:
class A {
// stuff
DEBUG_VAR(int) check;
};
Run Code Online (Sandbox Code Playgroud)
然后,我可以使用检查,就好像它是一个正常的变量!真棒!但是,仍有2个问题我无法解决:
"null class"没有push_back()等等.没什么大不了的.无论如何,大多数调试变量都是整数.
C++中的每个类至少有1个字符宽.因此,即使在发布模式下,使用N个调试变量的类至少会有N个字符太大.这在我看来是不可接受的.它违背了零开销原则,我尽可能地瞄准它.
那么,我该如何解决第二个问题呢?是否有可能摆脱#ifndef NDEBUG而不会在非调试模式下损害性能?我接受任何好的解决方案,即使它是你最黑暗的C++魔法或C++ 0x.
您无法解决第二个问题,因为c ++标准要求类或对象的sizeof至少为一个字节.
最简单的解决方案是不要引入这样的黑客攻击,并对代码进行适当的单元测试.
怎么样:
#ifndef NDEBUG
#define DEBUG_VAR(T) static nullclass<T>
#endif
Run Code Online (Sandbox Code Playgroud)
现在没有额外的存储添加到DEBUG_VAR(T)用作成员的类中,但仍可以使用声明的标识符,就像它是成员一样.
像这样的东西可以工作:
#ifdef NDEBUG
#define DEBUG_VAR(type, name)
#define DEBUG_VAR_OP(code)
#else
#define DEBUG_VAR(type, name) type name;
#define DEBUG_VAR_OP(code) code;
#endif
Run Code Online (Sandbox Code Playgroud)
用法示例:
struct Foo
{
DEBUG_VAR(int, count)
};
void bar(Foo* f)
{
DEBUG_VAR_OP(f->count = 45)
}
Run Code Online (Sandbox Code Playgroud)
但请注意,一般来说,程序的不同配置之间在内存布局方面存在的差异越多,您将获得的硬错误("它在调试中工作,但在发布中随机崩溃")就越多.因此,如果您经常使用其他调试数据,则应重新设计数据结构.当有大量调试数据时,更喜欢在发布模式下留下指向调试数据的指针(即struct Foo { ... ; struct FooDebug* debugData; /* NULL in Release */ };)