【发布时间】:2011-02-16 13:36:11
【问题描述】:
我的大多数类都有调试变量,这使得它们通常看起来像这样:
class A
{
// stuff
#ifndef NDEBUG
int check = 0;
#endif
};
方法可能如下所示:
for (/* big loop */) {
// code
#ifndef NDEBUG
check += x;
#endif
}
assert(check == 100);
没有什么比#ifndef NDEBUG 更丑的了。不幸的是,我知道没有编译器可以在没有这些 #ifndef 的情况下优化 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。否则它会生成一个只有无操作的“空类”。我的代码看起来像这样:
class A {
// stuff
DEBUG_VAR(int) check;
};
然后我可以像使用普通变量一样使用 check !惊人的!但是,我仍然无法解决 2 个问题:
1。它仅适用于 int、float 等。
“空类”没有 push_back() 等。没什么大不了的。无论如何,大多数调试变量都是整数。
2。 “空类”是 1 个字符宽!!
C++ 中的每个类至少有 1 个字符宽。因此,即使在发布模式下,使用 N 个调试变量的类至少也会有 N 个字符太大。这在我眼里简直是不能接受的。这违反了我尽可能追求的零开销原则。
那么,我该如何解决第二个问题? 是否有可能在不影响非调试模式下的性能的情况下摆脱#ifndef NDEBUG?我接受任何好的解决方案,即使它是你最黑暗的 C++ 魔法或 C++0x。
【问题讨论】:
-
欢迎来到 SO!我对第一个措辞很好且有趣的问题表示赞赏:D
-
您应该在这些变量的名称中添加一个调试前缀(例如
debug_或简短的dbg_),否则您将无法分辨哪个变量是调试变量,哪个不是调试变量正在查看源文件,现在您没有#ifndef NDEBUG行了。 -
在调试/发布模式下拥有不同的对象布局是一个等待调试的艰难时期。我发现在发布模式下保持调试检查和断言的好习惯,并在分析和全面调试后禁用它们。如果您的发布模式与调试模式仅在优化设置上有所不同,那您就可以了。特别要注意 Visual Studio 中讨厌的 _SECURE_SCL 和 _HAS_ITERATOR_DEBUGGING,除非我真的需要它们,否则我总是禁用它们。
标签: c++ debugging metaprogramming