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

有一个关于 comp.lang.c ++的讨论 ,主要是关于C ++中是否只存在于debugging版本中的断言是否应该保存在生产代码中。

显然,每个项目都是独一无二的,所以我的问题 在于是否应该保留断言, 而是在哪种情况下这是可取的/不是一个好主意。

我的意思是说:

  • 运行时检查testing一个条件,如果发现错误,则会显示软件中的错误。
  • 程序停止的一种机制(也许在真正的最小的清理工作之后)。

我不一定在谈论C或C ++。

我个人的观点是,如果你是程序员,但不拥有数据(这是大多数商业桌面应用程序的情况),你应该保持他们,因为一个失败的asssertion显示一个错误,你不应该去带有一个错误,可能会损坏用户的数据。 这会迫使您在发货之前强烈testing,并使错误更加明显,从而更容易发现并修复。

你的意见/经验是什么?

干杯,

卡尔

在这里查看相关问题


响应和更新

嘿格雷厄姆,

一个断言是错误的,纯粹而简单,因此应该像一个一样处理。 由于应该在发布模式下处理错误,所以你并不需要断言。

这就是为什么我在讨论断言时更喜欢“错误”这个词。 它使事情更清晰。 对我来说,“错误”这个词太模糊了。 缺less的文件是一个错误,而不是一个错误,程序应该处理它。 试图解引用一个空指针是一个错误,程序应该承认有些东西闻起来像坏的奶酪。

因此,你应该用一个断言来testing指针,但是用正常的error handling代码来存在这个文件。


轻微的话题,但在讨论中的重要一点。

作为提醒,如果你的断言在失败时进入debugging器,为什么不呢? 但是有很多原因,一个文件不能完全存在于你的代码的控制之外:读/写权限,磁盘已满,拔掉USB设备等。由于你没有控制权,所以我觉得断言是不是正确的方式来处理这个问题。

卡尔


托马斯,

是的,我有Code Complete,必须说我强烈反对那个特别的build议。

说你的自定义内存分配器拧紧,并清零一些其他对象仍在使用的内存。 我碰巧把这个对象解除引用的指针置零,其中一个不variables是这个指针永远不为空,并且你有几个断言来确保它保持这种状态。 如果指针突然变为null,你会怎么做? 你只是如果()围绕它,希望它的作品?

请记住,我们在这里讨论的是产品代码,所以没有进入debugging器并检查本地状态。 这是用户机器上的一个真正的bug。

卡尔

断言是不会过时的评论。 它们logging哪些理论状态是有意的,哪些状态不应该发生。 如果代码被改变,所以状态允许改变,开发者很快被通知并且需要更新断言。

请允许我引用Steve McConnell的Code Complete。 断言部分是8.2。

通常情况下,您不希望用户在生产代码中看到断言消息; 断言主要用于开发和维护过程中。 断言通常在开发时编译到代码中,并编译成生产代码。

然而,在同一部分的后面,给出了这样的build议:

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

我认为,只要性能不是问题,断言,而不是显示一条消息,让它写入日志文件。 我认为这个build议也在代码完成,但我现在还没有find。

在生产代码中保持断言,除非你已经testing过程序运行速度明显快于closures它们。

如果不值得去certificate它更有效率,那么为赌博而牺牲清晰度是不值得的。“ – Steve McConnell 1993

http://c2.com/cgi/wiki?ShipWithAssertionsOn

如果你甚至想在生产中留下断言,你可能会想到他们错了。 断言的整个观点是,你可以在生产中closures它们,因为它们不是你解决scheme的一部分。 他们是一个开发工具,用来validation你的假设是正确的。 但是当你投入生产时,你应该已经对你的假设有信心了。

也就是说,有一种情况我会在生产中转向断言:如果我们在生产中遇到一个可重现的错误,那么我们在testing环境中难以再生产,那么在断言打开的情况下重新生成错误可能会有帮助在生产中,看看他们是否提供有用的信息。

一个更有趣的问题是:在testing阶段,什么时候closures断言?

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

“断言”这个术语的意思是“只在开发时检查, 而不在现场执行”。

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

除非分析表明这些断言导致了性能问题,否则我认为他们应该留在产品发布中。

不过,我认为这也要求你有条不紊地处理断言失败。 例如,他们应该产生一个通用types的对话框,可以select(自动)向开发人员报告问题,而不仅仅是退出或崩溃程序。 此外,你应该小心不要使用你确实允许的条件断言,但可能不喜欢或考虑不需要的。 这些条件应该由代码的其他部分来处理。

在我的C ++中,我定义了REQUIRE(x),就像assert(x)一样,除了在发布版本中断言失败的时候它会引发一个exception。

由于失败的断言指示了一个错误,因此即使在发布构build中也应该认真对待它。 当我的代码的性能很重要时,我会经常使用REQUIRE()来获得更高级别的代码,并使用assert()来获得必须快速运行的底层代码。 我也使用REQUIRE而不是断言,如果失败的条件可能是由第三方写的代码传入的数据或文件损坏引起的(最好我会devise代码特别是在文件损坏的情况下performance良好,但我们并不总是有时间这样做。)

他们说你不应该把这些断言信息显示给最终用户,因为他们不了解它们。 所以? 最终用户可能会向您发送带有屏幕截图的电子邮件或错误消息的一些文本,从而帮助您进行debugging。 如果用户只是说“崩溃”,那么修复它的能力就会降低。 最好是通过互联网自动发送断言失败消息给自己,但只有当用户访问互联网并获得他们的许可时才有效。

如果你想让他们replaceerror handling。 没有什么比一个正在消失的程序更糟。 我认为将某些错误视为严重的错误没有任何问题,但是应该将其引导至您的程序的一部分,该部分通过收集数据,logging日志并通知用户应用程序出现了一些不需要的情况正在退出。

如果像其他任何错误一样处理,我不会看到任何问题。 不要忘记,与其他语言一样,C中失败的断言只会退出程序,这通常不足以满足生产系统的要求。

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

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

由于这是一个数据库系统,我们正在存储潜在的企业关键数据,所以我们尽我们所能来避免损坏的数据。 如果存在可能导致我们存储不正确数据的情况,则立即断言,回滚所有事务并停止服务器。

话虽如此,我们也试图避免在性能关键的例程中产生断言。

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

即如果你的程序正在打开/读取/closures一个文件,那么打开文件不在范围内 – 这是一个真正的可能性,换句话说,这是可以疏忽的。 所以,应该有与之相关的错误检查代码。

然而,让我们说你的fopen()被logging为总是返回一个有效的,打开的文件句柄。 您打开该文件,并将其传递给您的readfile()函数。

在这种情况下,可能根据其devise规范,readfile函数几乎可以假定它将得到一个有效的文件ptr。 因此,在这样一个简单的程序中,为负面情况添加error handling代码将是浪费的。 但是,至less应该logging下这个假设,不pipe怎样 – 在某种程度上确保 – 实际上是这样,在继续执行之前。 例如,它不应该实际上假定它总是有效的,以防错误地调用,或者被复制/粘贴到其他程序中。

所以,readfile(){assert(fptr!= NULL); ..}在这种情况下是合适的,而全面的error handling不是(忽略实际上读取文件需要一些error handling系统的事实)。

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

我从来没有把它们放在性能关键的代码中。

我很less使用断言来编译时间types检查。 我会使用exception而不是断言,因为大多数语言都是为了处理它们而构build的。

我举一个例子

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

反对

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

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

大多数情况下,当我在java中使用assertion(assert关键字)时,我会自动添加一些生产代码。 根据情况,它可以是一个日志消息,一个例外…或者什么都不是。

根据我的观点,你所有的说法对于开发者来说都是至关重要的,而不是在生产方面。 其中一些必须保留,其他必须丢弃。

我看到断言是在线unit testing。 在开发时用于快速testing,但最终这些断言应该被重构,在unit testing中进行外部testing。

ASSERTIONS不是错误,不应该作为error handling。 当一个断言被抛出时,这意味着在你的代码中或者在调用你的代码的代码中存在一个错误。

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

然而,有一个强有力的理由可以断言:ASSERTION可能影响性能和时间,可悲的是这有时很重要(特别是在embedded式系统中)。

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

〜Yitzik

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

由于错误应该在发布模式下处理,所以你并不需要断言。

我所看到的断言的主要好处是有条件的突破 – 它们比通过VC的窗口钻取要设置一行代码要容易得多。