断言和NDEBUG

时间:2008-12-23 15:35:55

标签: c++ error-handling

在阅读了一些关于异常滥用的线程之后(基本上说,如果函数前置条件不正确,你不想解开堆栈 - 可能表明你的所有内存都已损坏或同样危险)我正在考虑使用assert ()更频繁。以前我只使用assert()作为调试工具,我认为这是很多C ++程序员使用它的方式。我担心我的错误处理部分会被未来某个时候引入运行时构建的NDEBUG #define关闭。有没有办法解决这个问题并让其他人对此有疑问(即我应该担心它吗?)

谢谢, 专利

编辑: 我正在阅读的线程的重点是,如果你的应用程序真的被错误,那么展开堆栈可能会损坏系统,例如,如果析构函数将某些内容写入文件并且文件句柄已损坏。我不建议使用assert进行正常的错误处理。我目前的用例非常弱,但看看你的想法:


  //check later code won't crash the system
  if( buf.length() % 2 )
    return false;
  // do other stuff that shouldn't affect bufs length
  //copy 2 bytes into buf at a time, if length is odd then don't know 
  //what will happen so use assert to make sure it can't damage anything
  assert( !(buf.length() % 2) );
  for( i = 0; i != buf.length(); i += 2 )
    memcpy( buf + i, data, 2 );
 

edit2:讨论在这里: http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/80083ac31a1188da

6 个答案:

答案 0 :(得分:8)

您可以构建自己的断言,而不是使用库存C断言。您的断言不会被禁用。

查看在/usr/include/assert.h(或任何地方)中如何实现assert()。它只是一些预处理器魔术最终称为“断言失败”功能。

在我们的嵌入式环境中,我们一直在替换assert()。

答案 1 :(得分:7)

我喜欢定义自己的断言宏。我总是进行两次ASSERT测试(即使对于优化的构建),DASSERT只对调试版本有影响。您可能希望默认为ASSERT,但是如果测试的内容很昂贵,或者性能敏感区域的内部循环内的断言可以更改为DASSERT。

另外,请记住,断言应仅用于完全无意义的条件,这些条件表明程序中存在逻辑错误,并且无法从中恢复。这是对编程正确性的测试。永远不应该使用断言来代替错误处理,异常或健壮性,并且你永远不应断言与格式错误或不正确的用户输入相关的任何内容 - 这样的事情应该优雅地处理。断言只是一个受控制的崩溃,你有机会输出一些额外的调试信息。

以下是我的宏:

/// ASSERT(condition) checks if the condition is met, and if not, calls
/// ABORT with an error message indicating the module and line where
/// the error occurred.
#ifndef ASSERT
#define ASSERT(x)                                                      \
    if (!(x)) {                                                         \
        char buf[2048];                                                 \
        snprintf (buf, 2048, "Assertion failed in \"%s\", line %d\n"    \
                 "\tProbable bug in software.\n",                       \
                 __FILE__, __LINE__);                                   \
        ABORT (buf);                                                    \
    }                                                                   \
    else   // This 'else' exists to catch the user's following semicolon
#endif


/// DASSERT(condition) is just like ASSERT, except that it only is 
/// functional in DEBUG mode, but does nothing when in a non-DEBUG
/// (optimized, shipping) build.
#ifdef DEBUG
# define DASSERT(x) ASSERT(x)
#else
# define DASSERT(x) /* DASSERT does nothing when not debugging */
#endif

答案 2 :(得分:4)

好吧,a failing assertion is a bug,不多也不少。与取消引用空指针相同,除了你自己给你的软件一棒。勇敢的决定,你值得称赞!

通过异常跳出问题几乎没有帮助,它不能修复bug。所以我建议你实现自己的ASSERT()宏,其中包括:

  • 尝试尽可能多地收集有关失败的数据(断言表达式,堆栈跟踪,用户环境等)
  • 努力让用户尽可能轻松地向您报告,
  • 抛出一个消息框,为造成不便而道歉并粗暴地中止该应用。

如果性能有问题,您可以考虑使用某种SOFT_ASSERT()宏从发布版本中消失。

答案 3 :(得分:1)

除了在调试期间检查假设的随意方式之外,我会避免依赖断言。在发布代码中,您重新定义的断言将由随机崩溃处理,这不是用户友好的

如果在调试期间,假设你的假设,假设某个条件永远不会成立,那么结果是无效的。根据条件更改您的错误处理代码。

否则,有一个更加用户友好的设施来处理假设违规条件。创建一个名为CAssumptionViolated的异常,将其抛出到断言的位置。将其捕获到主例程中,并为用户提供一种提醒您错误的方法。更好的是,提供例外的调试信息。更好的是,以某种方式自动将调试信息转发给您的组织。

答案 4 :(得分:1)

也许我没有正确理解你,但IMO断言()不应该在发布代码中用来做你所描述的,即强制执行不变量。

如果函数的前置条件不正确,您想要做什么?您希望如何将其传达给:

  • 互动用户?
  • 您的支持人员?

在这两种情况下,您都需要调用一些错误处理代码,而且(在我看来)异常就是故障单。

但是,如果您仍想使用此路线,可以将以下内容放在每个源文件的顶部:

#undef NDEBUG

我不推荐这个。请不要这样做。没有人会感谢你: - )

的Seb

答案 5 :(得分:0)

我见过程序实现自己的断言和验证宏。其中asserts用于调试模式,并在调试和释放模式下进行验证。具体来说,验证可用于在检查失败时从程序中优雅地退出(或者更优雅地而不是直接崩溃)。可能是我在Unreal代码中看到的第一个也是最好的用法之一(我相信你仍然可以看到Unreal标题)。