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

Posted

技术标签:

【中文标题】在 C++ 中实现断言检查的最佳方法是啥?【英文标题】:What is the best way of implementing assertion checking in C++?在 C++ 中实现断言检查的最佳方法是什么? 【发布时间】:2008-10-07 18:21:18 【问题描述】:

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

MFC 很简单,我只使用 ASSERT(something)。

什么是非MFC方式?

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

编辑:<assert.h><cassert>有什么区别?

接受的答案:这篇文章中有很多很棒的答案,我希望我可以接受多个答案(或者有人会将它们全部结合起来)。因此,答案将授予 Ferruccio(第一个答案)。

【问题讨论】:

在使用 C++ 时,据说最好包含c<filename>,在 C 中包含<filename>.h。它是cstdio 而不是stdio.h,或者如你的情况是cassert 而不是assert.h 另见相关问题***.com/questions/1176131/design-by-contract-in-c/…的答案。 【参考方案1】:
#include <cassert>

assert(something);

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

#include <boost/static_assert.hpp>

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

【讨论】:

请注意,'something' 表达式在非调试版本中完全消失,因此此代码:assert(PerformSomeFabulousTrick(kTopeka) != 0); // 看这个!在发布时不会做任何事情。 有关编译时断言的更多信息,请参见此处:***.com/questions/174356/…【参考方案2】:

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

有几种类型的断言:

    预处理器 这些断言是使用预处理器指令#error完成的 预处理器断言仅在预处理阶段进行评估,因此对模板之类的东西没有用处。

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

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

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

我希望这会有所帮助!

【讨论】:

【参考方案3】:

断言(通常)仅用于调试

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

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

但有时(大多数时候?),测试并不像想要的那样密集。我不会谈论直到最后一刻我们必须编码的旧工作(不要问......有时,经理只是......嗯......)......您添加到代码中的断言有什么意义,该代码将在下一分钟被编译并作为发布二进制文件交付给客户端?

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

在我们的团队中,我们需要一些东西来检测错误,同时需要其他东西来处理错误。我们可能在 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),但在其他任何地方,它都很好。并且可以使用任何首选的错误处理,并且可以在开发人员计算机、测试人员甚至用户上随意激活它,这一点非常有用。

【讨论】:

【参考方案4】:

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

是 C 头文件

是 C++ 标准库头文件...它通常包括

【讨论】:

而且由于 assert() 是一个宏而不是一个函数,所以 C++ 标头不会将其放入 std:: 命名空间,因此仅在标头名称上有所不同。【参考方案5】:

基本断言用法

#include <cassert>

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

最佳实践说明

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

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

switch ( someVal ):

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

也可以通过assert传递消息:

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

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

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

【讨论】:

您写道:“[...] 识别不应该为真的运行时状态 [...]”。不应该反过来吗?断言应该总是为真。 有时,断言的条件在编译时会为假。那么有一个 static_assert 特性来停止编译是非常酷的。 Windows 中的静态断言:C_ASSERT (msdn.microsoft.com/en-us/library/ms679289.aspx)【参考方案6】:

要中断调用断言的文件,您可以使用引发异常或调用 __debugbreak 的自定义宏:

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

或者:

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

【讨论】:

编译器支持时我最喜欢的解决方案 (MSVC)。【参考方案7】:

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());

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

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

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

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

这对我来说在呼叫者站点中断...

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

【讨论】:

【参考方案10】:

这是我最近一次迭代的 C++ 断言工具:http://pempek.net/articles/2013/11/17/cross-platform-cpp-assertion-library/

这是一个包含 2 个文件的插件库,您可以轻松地将其添加到您的项目中。

【讨论】:

【参考方案11】:

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

【讨论】:

以上是关于在 C++ 中实现断言检查的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

最佳多线程实现 C++

在 SwiftUI 中实现昂贵的派生属性的最佳方法是啥?

在控制台应用程序 C# 中实现单例的最佳方法?

在 java 中实现 PriorityQueue 的最佳方法是啥?

在 ViewPager 中实现弹性/反弹动画效果的最佳方法是啥?

在 asp.net 中锁定缓存的最佳方法是啥?