摆脱#ifndef NDEBUG

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个问题我无法解决:

1.它只适用于int,float等.

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

2."空类"是1个字符宽!

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

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

BЈо*_*вић 8

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

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

  • @zeuxcg,断言对业务逻辑很重要的事情是好的.引入新的仅测试变量以便您可以对它们进行断言是不好的.这是一个微妙但重要的进口 (5认同)
  • +1用于单元测试而不是混合测试和业务逻辑 (2认同)
  • 根据相同的逻辑,断言是黑客 - 你应该正确地对代码进行单元测试......对吗? (2认同)
  • @Glen,有时会有一些复杂的假设需要额外的数据来验证.在其他时候,调试数据有助于调试过程 - 例如,在发布时冗余但在调试时非常有用的对象名称. (2认同)

asc*_*ler 8

怎么样:

#ifndef NDEBUG
#define DEBUG_VAR(T) static nullclass<T>
#endif
Run Code Online (Sandbox Code Playgroud)

现在没有额外的存储添加到DEBUG_VAR(T)用作成员的类中,但仍可以使用声明的标识符,就像它是成员一样.


zeu*_*xcg 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
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 */ };)