在 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
完成的
预处理器断言仅在预处理阶段进行评估,因此对模板之类的东西没有用处。
执行时间
这些断言是使用<cassert>
中定义的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 头文件
【讨论】:
而且由于 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() 本身而言,包含它的两种方式是相同的。使用<cxxx>
是一种很好的做法,因为它通常不是那么简单(命名空间包装和其他魔法)
这对我来说在呼叫者站点中断...
这里有一个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++ 中实现断言检查的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
在 java 中实现 PriorityQueue 的最佳方法是啥?