繁体   English   中英

断言和NDEBUG

[英]assert and NDEBUG

在阅读了一些关于异常滥用的线程之后(基本上说,如果函数前置条件不正确,你不想解开堆栈 - 可能表示你的所有内存都已损坏或同样危险)我正在考虑使用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

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

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

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

我喜欢定义自己的断言宏。 我总是进行两次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

一个失败的断言是一个错误 ,不多也不少。 与取消引用空指针相同,除了你自己给你的软件一棒。 勇敢的决定,你值得信赖!

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

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

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

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

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

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

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

如果函数的前置条件不正确,您想要做什么? 你想怎么把它传达给:

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

在这两种情况下,您都需要调用一些错误处理代码,并且(在我看来)异常只是票证。

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

#undef NDEBUG

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

勒布

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM