断言什么时候应该留在生产代码中?

在 comp.lang.c + + 上有一个 讨论。在 C + + 中默认只存在于调试构建中的断言是否应该保存在生产代码中。

显然,每个项目都是独一无二的,所以我的问题是 没有这么多的 是否断言应该保留,但在某些情况下这是值得推荐的/不是一个好主意。

我的意思是:

  • 一个运行时检查,用于测试一个条件,如果该条件为 false,则显示软件中的 bug。
  • 程序停止的一种机制(可能在最小的清理工作之后)。

我说的不一定是 C 或 C + + 。

我个人的观点是,如果你是程序员,但不拥有数据(这是大多数商业桌面应用程序的情况) ,你应该保持它们,因为一个失败的断言显示一个错误,你不应该继续与一个错误,有损坏用户的数据的风险。这迫使您在发布之前进行强有力的测试,并使 bug 更加可见,从而更容易发现和修复。

你的意见/经验是什么?

干杯,

卡尔

见相关问题 给你


回应及更新

嘿,格雷厄姆,

断言是错误,纯粹而简单,因此应该像错误一样处理。 因为错误应该在发布模式下处理,所以您实际上不需要断言。

这就是为什么我在谈论断言时更喜欢用“ bug”这个词。这样事情就清楚多了。对我来说,“错误”这个词太含糊了。丢失的文件是一个错误,而不是 bug,程序应该处理它。尝试去引用一个空指针是一个错误,并且程序应该承认某些东西闻起来像坏奶酪。

因此,您应该使用断言测试指针,但使用正常的错误处理代码测试文件的存在。


有点跑题了,但这是讨论的重点。

提醒一下,如果您的断言在失败时闯入调试器,为什么不呢。但是文件不能存在的原因有很多,这些原因完全超出了代码的控制范围: 读/写权限、磁盘已满、 USB 设备已拔出等等。既然你不能控制它,我觉得断言不是处理这个问题的正确方式。

卡尔


托马斯,

是的,我有完整的代码,必须说,我强烈反对这个特别的建议。

假设您的自定义内存分配程序出了问题,并为某个其他对象仍在使用的内存块进行了归零。这个对象有规律地取消引用的指针刚好为零,其中一个不变量是这个指针永远不会为空,并且有几个断言可以确保它保持这种状态。如果指针突然为空,该怎么办。你只是如果()周围,希望它的工作?

记住,我们在这里讨论的是产品代码,因此不需要破解调试器并检查本地状态。这是用户机器上的一个真正的错误。

卡尔

68478 次浏览

除非分析表明断言导致了性能问题,否则我认为它们也应该保留在生产版本中。

但是,我认为这还需要您在某种程度上优雅地处理断言失败。例如,它们应该产生一种通用类型的对话框,可以选择(自动)向开发人员报告问题,而不仅仅是退出或使程序崩溃。另外,应该注意不要对实际允许的条件使用断言,但可能不喜欢或认为不需要这些条件。这些条件应该由代码的其他部分来处理。

断言是错误,纯粹而简单,因此应该像错误一样处理。

因为错误应该在发布模式下处理,所以您实际上不需要断言。

我认为断言的主要好处是有条件的中断——它们比通过 VC 的窗口设置只需要一行代码的东西要容易得多。

除了编译时类型检查之外,我很少使用断言。我会使用 例外而不是断言,因为大多数语言都是为处理它们而构建的。

我举个例子

file = create-some-file();
_throwExceptionIf( file.exists() == false, "FILE DOES NOT EXIST");

反对

file = create-some-file();
ASSERT(file.exists());

应用程序将如何处理断言? 我更喜欢处理致命错误的旧 try catch方法。

大多数情况下,当我在 java 中使用断言(断言关键字)时,我会在后面自动添加一些生产代码。根据情况,它可以是一个日志消息,一个异常... 或者什么都没有。

根据我的观点,所有的断言在开发版本中都是至关重要的,而不是在产品版本中。有些必须保留,有些必须丢弃。

如果像处理其他任何错误一样处理它们,我不认为这有什么问题。请记住,C 语言中的失败断言和其他语言一样,只会退出程序,而这对于生产系统来说通常是不够的。

也有一些例外——例如,PHP 允许您为断言失败创建自定义处理程序,以便您可以显示自定义错误、执行详细的日志记录等,而不仅仅是退出。

如果希望保留它们,请用错误处理替换它们。没有什么比程序消失更糟糕的了。我认为将某些错误视为严重的 bug 并没有什么错,但是它们应该被引导到你程序的某个部分,该部分负责处理这些错误,收集数据,记录数据,并通知用户你的应用程序出现了一些不必要的问题,正在退出。

请允许我引用史蒂夫 · 麦康奈尔的代码完成。断言部分是8.2。

通常,您不希望用户在生产代码中看到断言消息; 断言主要用于开发和维护期间。断言通常在开发时编译到代码中,并从生产代码中编译出来。

然而,在同一部分的后面,给出了以下建议:

对于高度健壮的代码,请先断言,然后再处理错误。

我认为,只要性能不是问题,就将断言保留在其中,而不是显示消息,而是将其写入日志文件。我认为这个建议也在代码完成中,但是我现在找不到它。

我们的数据库服务器软件包含生产和调试断言。调试断言仅仅是——它们在生产代码中被删除。只有在下列情况下才会发生生产断言: (a)存在一些应该 永远不会存在的条件; (b)不可能可靠地从这种条件中恢复。生产断言表明软件中出现了错误或发生了某种数据损坏。

由于这是一个数据库系统,我们存储的是可能对企业至关重要的数据,因此我们尽一切可能避免损坏数据。如果存在 导致我们存储不正确数据的情况,我们将立即断言、回滚所有事务并停止服务器。

话虽如此,我们还试图在性能关键型例程中避免产品断言。

在我的 C + + 中,我定义了 REQUIRE (x) ,它类似于断言(x) ,只是在发布版本中断言失败时抛出一个异常。

由于断言失败表明存在 bug,因此即使在发布版本中也应该认真对待。当我的代码的性能很重要时,我通常会对高级代码使用 REQUIRE () ,对必须快速运行的低级代码使用声明()。如果失败条件可能是由第三方编写的代码传入的数据引起的,或者是由文件损坏引起的,我也使用 REQUIRE 代替断言(最理想的情况是,我会专门设计出在文件损坏的情况下行为良好的代码,但是我们并不总是有时间这样做)

他们说,不应该向最终用户显示这些断言消息,因为他们不会理解它们。那又怎样?最终用户可能会向您发送一封带有屏幕截图或错误消息的文本的电子邮件,这将帮助您进行调试。如果用户只是简单地说“它崩溃了”,你就没有能力修复它。最好是自动发送断言失败的信息给自己,但这只有在以下情况下才能奏效: (1)软件运行在你控制/监视的服务器上,或者(2)用户可以访问互联网,你可以获得他们的许可发送错误报告。

断言是不会过时的评论。它们记录了哪些理论状态是有意的,哪些状态不应该发生。如果代码发生了变化,因此状态允许发生变化,开发人员很快就会得到通知,并需要更新断言。

保持断言在生产代码中处于打开状态,除非您已经测量到关闭断言后程序运行速度明显加快。

如果不值得通过衡量来证明它更有效率,那就不值得为了业绩赌博而牺牲清晰度。”史蒂夫 · 麦康奈尔1993年

Http://c2.com/cgi/wiki

我将断言视为内联单元测试。对于开发过程中的快速测试非常有用,但是最终这些断言应该被重构出来,以便在单元测试中进行外部测试。

断言不是错误,不应作为错误处理。当引发断言时,这意味着在您的代码中存在 bug,或者在调用您的代码的代码中存在 bug。

有几点需要避免在生产代码中启用断言: 1.您不希望最终用户看到类似“ ASSERTION false MyPrivateClass.cpp line 147”的消息。最终用户是 没有的质量保证工程师。 2. 断言可能会影响性能

然而,我们有一个强有力的理由不这么认为: 断言可能会影响性能和时间,遗憾的是,这有时很重要(特别是在嵌入式系统中)。

我倾向于在生产代码中保留断言,但是要确保这些断言不会打印出来暴露给最终用户。

~ Yitzik

我发现最好是处理范围内的所有错误,并使用断言来假设我们断言的是真实的。

也就是说,如果你的程序正在打开/读取/关闭一个文件,那么无法打开该文件就是在范围内——这是一种真实的可能性,换句话说,忽略这种可能性就是疏忽。因此,应该有与之相关联的错误检查代码。

但是,假设 fopen ()文档中总是返回一个有效的、打开的文件句柄。打开该文件,并将其传递给 readfile ()函数。

在这个上下文中,readfile 函数(可能根据其设计规格)可以假设它将获得一个有效的文件 ptr。因此,在这样一个简单的程序中,为否定的情况添加错误处理代码将是浪费。然而,在继续执行之前,它至少应该以某种方式记录这个假设——以某种方式确保——确实如此。它实际上不应该假设它总是有效的,例如,如果调用不正确,或者它被复制/粘贴到其他程序中。

因此,readfile (){ asser(fptr!= 空)。. }在这种情况下是合适的,而全面的错误处理则不合适(忽略了这样一个事实,即实际读取文件无论如何都需要一些错误处理系统)。

是的,这些断言应该保留在生产代码中,除非绝对有必要禁用它们。即使这样,您也应该只在性能关键部分禁用它们。

如果您甚至考虑在生产环境中保留断言,那么您可能认为它们是错误的。断言的全部意义在于您可以在生产中关闭它们,因为它们不是解决方案的一部分。它们是一种开发工具,用于验证您的假设是否正确。但是当你投入生产的时候,你应该已经对你的假设有信心了。

也就是说,有一种情况下我会在生产环境中打开断言: 如果我们在生产环境中遇到一个可重复的 bug,而我们在测试环境中很难再现这个 bug,那么在生产环境中打开断言来重现这个 bug 可能会有帮助,看看它们是否提供了有用的信息。

一个更有趣的问题是: 在测试阶段,什么时候关闭断言?

断言应该保留在生产代码中。如果一个特定的断言看起来在生产代码中可能有用,那么它就不应该是一个断言; 它应该是一个运行时错误检查,也就是像这样的代码: if( condition != expected ) throw exception

术语“断言”已经变成意味着“仅在开发时进行的检查,它将在现场执行 没有。”

如果你开始认为断言可能会成为现实,那么你将不可避免地开始产生其他危险的想法,比如想知道任何给定的断言是否真的值得做。没有不值得作出的断言。你永远不应该问自己“我是否应该坚持这一点?”你应该只问自己“有什么是我忘记断言的吗?”

假设生产环境中有一段代码,它命中了一个通常会被触发的断言。断言发现了一个错误!但事实并非如此,因为断言被关闭了。

那现在怎么办?要么程序会(1)在远离问题根源的地方以无信息的方式崩溃,要么(2)愉快地运行到完成,可能会得到错误的结果。

这两种情况都不受欢迎,即使在生产环境中也要保持断言的活动状态。