协助避免断言......永远!

时间:2009-12-04 00:49:58

标签: c++ c api assert

在C和C ++中assert非常重量级例程,向stdout写入错误并终止程序。在我们的应用程序中,我们为assert实现了一个更强大的替换,并给它自己的宏。我们已尽一切努力用我们的宏替换assert,但仍有许多方法assert可以重新引入(例如,来自内部第三方库,天真注入等)

有关如何减少,限制甚至根除assert用途的任何建议?最好的答案是编译器可以为我们捕获的答案,因此我们不必像现在一样照看代码库。

10 个答案:

答案 0 :(得分:14)

实际上,我不确定我是否真的理解这个问题。如果断言,断言只会很昂贵,无论如何这都很好,因为你现在处于异常状态。

assert仅在调试版本中启用,因此请使用第三方库的版本构建。但实际上,断言不应该每时每刻都在消失。

答案 1 :(得分:2)

这取决于(至少部分)你正在改变的东西。假设你不介意它打印出它的正常消息,并且主要想要去掉它abort(),你可以考虑单独留下assert(),而是定义你自己的{{1}版本}}

从理论上讲,这样做是不可移植的 - 但实际上,abort()在标准库中是一个相当普通的函数,如果你链接自己的函数,你会得到它的行为。有时(特别是一些微软链接器)你必须做一些工作才能让链接器合作用你的abort()替换它们,但它很少很难。

答案 2 :(得分:2)

改进内置断言工具(提供堆栈跟踪,核心转储,谁知道)可以很方便。在这种情况下,如果您在让开发人员遵循您所拥有的任何标准(例如“而不是assert()使用SUPER_ASSERT()”或其他任何标准)时遇到问题,您可以自己设置{{1}头文件在编译器的头文件的运行时目录之前的include路径中。

这几乎可以保证使用标准assert.h宏的任何人都会收到编译器错误或获得断言功能(取决于你的assert()标题的内容)。

答案 3 :(得分:2)

我认为你的问题完全有效。如果您已实现自己的错误处理,则可能需要:

  1. 即使在发布版本中也始终触发断言。
  2. 在断言触发的情况下实施更好的错误报告。您可能希望发送错误报告或写入日志文件。
  3. 话虽如此,我认为任何解决办法都不会有效。

    • 如果幸运的话,第三方库使用ASSERT宏,只要定义此宏的文件对多个文件有#pragma once#ifndef __HEADERFILE_H__ #define __HEADERFILE_H__条款,您就可以重新定义自己包容性。单独包含头文件,重新定义ASSERT,你很好。

    • 如果它们直接包含assert.h或cassert,你只能修补我猜的代码。进行最少的代码更改,将更改保存为修补程序文件,并在更新库时希望修补程序仍然有效。将修补程序添加到版本控制。

    如果这不起作用,请重新考虑问题,如果您确实需要第三方库中的内部断言。仅发布发布版本,这将摆脱断言,并添加ASSERT以检查代码中的正确性。检查返回值的有效性。如果触发了这样的ASSERT,您仍然可以深入了解第三方代码以查看导致问题的原因。

答案 4 :(得分:2)

我认为这个问题是有效的。

如果触发了我自己的断言扩展为asm(“int3”),这相当于一个断点。我还发现调试比简单终止更有用。

我简单地称它为“ASSERT()”而不是正常的“assert()”,并且完全避免使用assert()。

答案 5 :(得分:1)

最明显的方法似乎是给自己的断言版本自己的名字,与assert()略有不同。然后你可以搜索文本,查看链接器消息等文字字符串“_assert”,你知道你看到它时会遇到问题。

在我自己的代码中,我总是使用Assert(),它扩展为我自己的执行断言的函数,或者扩展为((void)0)以用于发布版本。编译器会将((void)0)表达式转换为空,但它仍然算作表达式。因此

Assert(3 == x);

将变成

((void)0);

分号有一个地方可以去。

顺便说一下,我曾经在一个GUI应用程序上工作,其中assert是一个特殊的GUI模式弹出对话框。你有三个选择:忽略,永远忽略或者休息。忽略会忽略断言并继续运行。永远忽略会设置一个标志,直到你在调试器中重新启动程序,该断言将不再触发。中断将允许断言进入调试器。

我不记得他们如何保证每个断言都有自己的标志。也许当你编写Assert()调用时,你必须指定一个唯一的整数?如果它比那更自动,那将是很好的。我很确定实际的implmentation是一个有点向量,当你永远选择忽略时它会设置一点。

答案 6 :(得分:1)

assert()通常#define((void)0) for release code#define NDEBUG),因此根本没有开销

使用测试版时,性能开销是否会影响测试的真实性?

答案 7 :(得分:1)

如果源代码在您的控制之下:

#define NDEBUG
// Before
#include <assert.h>
// Or other header that includes assert.h

或使用预编译的头或编译选项来定义NDEBUG

对于第三方二进制文件,请使用它们的发行版本。

答案 8 :(得分:1)

您似乎错过了第三方代码最有可能在“标准”assert行为假设下编写的事实。即代码期望程序在失败的断言时终止。如果断言条件被破坏,则断言后面的代码通常不会也将无法正常工作。在100个中的99个案例中根本不起作用。在100个中的99个案例中,它将简单地崩溃,即程序将终止。

要相信通过覆盖第三方代码中的assert行为,您将以某种方式延长程序的使用时间是最好的。

答案 9 :(得分:0)

在库标题中查找assert(假设它们是您文件系统上的真实文件)并将其替换为无效的文件

// #define assert(condition) ... /* old definition */
#define assert(condition) ((condition) & "PLEASE DO NOT USE ASSERT" = 42)