在C ++中实现断言检查的最佳方法是什么?

时间:2008-10-07 18:21:18

标签: c++ debugging assert debugbreak

我的意思是,我需要做些什么才能在我的代码中使用有用的断言?

MFC非常简单,我只使用ASSERT(某事)。

什么是非MFC方式?

编辑:是否可以在assert.c中停止断言,而不是在调用assert()的文件中停止断言?

编辑<assert.h>&amp;之间有什么区别? <cassert>

已接受的答案:在这篇文章中有很多很棒的答案,我希望我能接受不止一个答案(或者有人将它们全部合并)。所以答案会被授予Ferruccio(第一个答案)。

11 个答案:

答案 0 :(得分:29)

#include <cassert>

assert(something);

对于编译时检查,Boost的静态断言非常有用:

#include <boost/static_assert.hpp>

BOOST_STATIC_ASSERT(sizeof(int) == 4);  // compile fails if ints aren't 32-bit

答案 1 :(得分:13)

这取决于您是否正在寻找在Visual C ++之外工作的东西。它还取决于您要查找的断言类型。

有几种类型的断言:

  1. <强>预处理
    这些断言是使用预处理程序指令#error完成的 预处理程序断言仅在预处理阶段进行评估,因此对模板等内容无用。

  2. 执行时间
    这些断言是使用assert()中定义的<cassert>函数完成的 执行时间断言仅在运行时进行评估。正如BoltBait指出的那样,如果已定义NDEBUG宏,则不会编译。

  3. <强>静态
    正如您所说,这些断言是通过使用ASSERT()宏完成的,但前提是您使用的是MFC。我不知道另一种做静态断言的方法是C / C ++标准的一部分,但是,Boost库提供了另一种解决方案:static_assert
    Boost库中的static_assert函数将添加到C++0x standard中。

  4. 作为附加警告,Ferruccio建议的assert()函数与MFC ASSERT()宏的行为不同。前者是执行时断言,而后者是静态断言。

    我希望这有帮助!

答案 2 :(得分:10)

断言(通常)仅调试

“assert”的问题在于它通常在调试二进制文件中,并且一些开发人员使用它们就好像代码仍然在生产中一样。

这本身并不邪恶,因为代码应该进行密集测试,因此,产生断言的错误肯定会被发现并被删除。

但有时(大多数情况下?),测试并不像想要的那样密集。我不会谈论一个旧工作,我们必须编码,直到最后一分钟(不要问......有时,经理只是......咳...... )...断言添加到下一分钟将被编译并作为发布二进制文件传递给客户端的断言是什么意思?

断言(某些)现实生活中的应用

在我们的团队中,我们需要一些东西来检测错误,同时还需要其他东西来处理错误。我们可能会在Release Build上使用它。

Assert将仅在调试版本中检测并处理错误。

所以我们添加了一个XXX_ASSERT宏,以及一个XXX_RAISE_ERROR宏。

XXX_ASSERT宏将与ASSERT宏做同样的事情,但它将在Debug和Release中构建。它的行为(写一个日志,打开一个消息框,什么都不做等)可以用.INI文件控制,然后它会中止/退出应用程序。

这被用作:

bool doSomething(MyObject * p)
{
   // If p is NULL, then the app will abort/exit
   XXX_ASSERT((p != NULL), "Hey ! p is NULL !") ;

   // etc.
}

XXX_RAISE_ERROR宏只会“记录”错误,但不会尝试处理它。这意味着它可以将消息记录在文件中和/或打开带有消息的MessageBox和一个继续按钮,另一个用于启动调试会话(根据.INI文件配置)。这被用作:

bool doSomething(MyObject * p)
{
   if(p == NULL)
   {
      // First, XXX_RAISE_ERROR will alert the user as configured in the INI file
      // perhaps even offering to open a debug session
      XXX_RAISE_ERROR("Hey ! p is NULL !") ;
      // here, you can handle the error as you wish
      // Than means allocating p, or throwing an exception, or
      // returning false, etc.
      // Whereas the XXX_ASSERT could simply crash.
   }

   // etc.
}

在我们的库中引入一年后,只使用了XXX_RAISE_ERROR。当然,它不能用于应用程序的时间关键部分(我们有一个XXX_RAISE_ERROR_DBG),但在其他地方,它是好的。事实上,人们可以使用任何首选的错误处理,并且可以随意激活,无论是在开发人员计算机上,还是在测试人员上,甚至是用户,都非常有用。

答案 3 :(得分:9)

在第二次“编辑”中回答问题:

&LT; ASSERT.H&GT;是C头

&LT;了cassert&GT;是C ++标准库头...它通常包括&lt; ASSERT.H&GT;

答案 4 :(得分:6)

要在调用assert的文件内部进行分解,可以使用引发异常的自定义宏或调用__debugbreak

#define MYASSERT(EXPR, MSG) if (!(EXPR)) throw MSG;

或者:

#define MYASSERT(EXPR) if (!(EXPR)) __debugbreak();

答案 5 :(得分:6)

基本断言用法

#include <cassert>

/* Some code later */
assert( true );

最佳实践笔记

断言用于识别应该为真的运行时状态。因此,它们在发布模式下编译。

如果您希望断言始终命中,则可以将false传递给它。例如:

switch ( someVal ):
{
case 0:
case 1:
  break;
default:
  assert( false ); /* should never happen */
}

也可以通过断言传递消息:

assert( !"This assert will always hit." );

成熟的代码库经常扩展断言功能。一些常见的扩展包括:

  • 在每个模块的基础上切换断言以本地化测试。
  • 创建在大多数调试版本中编译的附加断言宏。对于频繁调用的代码(每秒数百万次)并且不太可能不正确,这是理想的。
  • 允许用户禁用当前命中的断言,编译单元中的所有断言或代码库中的所有断言。这会阻止良性断言被触发,从而产生无法使用的构建。

答案 6 :(得分:5)

特定于Microsoft的CRT断言

#include <crtdbg.h>
#include <sstream>
...
// displays nondescript message box when x <= 42
_ASSERT(x > 42);
// displays message box with "x > 42" message when x <= 42
_ASSERTE(x > 42);
// displays message box with computed message "x is ...!" when x <= 42
_ASSERT_EXPR(
   x > 42, (std::stringstream() << L"x is " << x << L"!").str().c_str());

答案 7 :(得分:3)

有一个名为ModAssert的更高级的开源库,它具有适用于Visual C ++和gcc的断言。可能还有其他编译器,不确定。学习它需要一些时间,但如果你想要不依赖于MFC的良好断言,请看看这些。它位于http://sourceforge.net/projects/modassert/

答案 8 :(得分:1)

使用intellisense在visual studio中打开它(右键单击)

// cassert standard header
#include <yvals.h>
#include <assert.h>

yvals.h是windows的东西。所以,就assert()本身而言,包含它的两种方式是相同的。使用<cxxx>是一种很好的做法,因为它通常不是那么简单(命名空间包装和其他魔术)

这对我来说在来电网站上断了......

这里有article解释为什么你不想自己编写这个宏。

答案 9 :(得分:1)

这是我最近在C ++中使用Assertion工具的迭代:http://pempek.net/articles/2013/11/17/cross-platform-cpp-assertion-library/

这是一个可以轻松添加到项目中的嵌入式2文件库。

答案 10 :(得分:1)

回答提问者的第三个问题: 我们使用“cassert”而不是“assert.h”的第一个原因是,在C ++的情况下,有一个允许C ++编译器可能不会将函数描述存储在代码文件中,而是在dll或在编译器本身。 第二,可能会对功能进行微小的更改,以便促进C和C ++之间的差异,无论是现在还是将来。因为assert.h是一个C库,所以首选是在C ++中使用“cassert”。