摆脱#ifndef NDEBUG

时间:2011-02-16 13:36:12

标签: c++ debugging metaprogramming

我的大多数类都有调试变量,这使它们看起来像这样:

class A
{
    // stuff
#ifndef NDEBUG
    int check = 0;
#endif
};

和方法可能如下所示:

for (/* big loop */) {
    // code
#ifndef NDEBUG
    check += x;
#endif
}

assert(check == 100);

几乎没有比#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

因此在调试模式下,DEBUG_VAR(T)只生成一个T.否则它会生成一个只有no-ops的“null类”。我的代码看起来像这样:

class A {
   // stuff
   DEBUG_VAR(int) check;
};

然后我可以使用检查,就像它是一个普通变量一样!真棒!但是,仍有两个问题我无法解决:

1。它只适用于int,float等

“null class”没有push_back()等没什么大不了的。无论如何,大多数调试变量都是整数。

2。 “null class”是1 char wide !!

C ++中的每个类至少有1个字符宽。因此,即使在发布模式下,使用N个调试变量的类至少会有N个字符太大。这在我看来是不可接受的。它违背了零开销原则,我尽可能地瞄准它。

那么,如何解决第二个问题?甚至可以摆脱#ifndef NDEBUG而不会在非调试模式下损害性能?我接受任何好的解决方案,即使它是你最黑暗的C ++魔法或C ++ 0x。

4 个答案:

答案 0 :(得分:8)

你无法解决第二个问题,因为c ++标准要求类或对象的sizeof至少为一个字节。

最简单的解决方案是不要引入这样的黑客攻击,并对代码进行适当的单元测试。

答案 1 :(得分:8)

怎么样:

#ifndef NDEBUG
#define DEBUG_VAR(T) static nullclass<T>
#endif

现在没有额外的存储空间被添加到使用DEBUG_VAR(T)作为成员的类中,但声明的标识符仍然可以像使用成员一样使用。

答案 2 :(得分:5)

这样的事情可行:

#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

用法示例:

struct Foo
{
    DEBUG_VAR(int, count)
};

void bar(Foo* f)
{
    DEBUG_VAR_OP(f->count = 45)
}

但是,请注意,一般来说,在程序的不同配置之间存储器布局方面存在的差异越大,越多的硬错误(“它在调试中工作,但在发布中随机崩溃”)你将会得到。因此,如果您经常使用其他调试数据,则应重新设计数据结构。当有大量调试数据时,更喜欢在发布模式下留下指向调试数据的指针(即struct Foo { ... ; struct FooDebug* debugData; /* NULL in Release */ };

答案 3 :(得分:0)

如何在调试模式下声明成员对象静态:

#define DEBUG_VAR(T) static nullclass<T>

您必须在某处定义每个对象的实例。

(顺便说一下,空类的对象必须占用空间的原因是它们可以具有唯一的指针。)

编辑:删除了第二个想法 - 将无效。