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

发布于 2024-07-06 11:24:04 字数 404 浏览 8 评论 0原文

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

MFC很简单,我只是使用ASSERT(一些东西)。

非MFC的方式是什么?

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

编辑: 之间有什么区别?

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

By that I mean, what do I need to do to have useful assertions in my code?

MFC is quite easy, i just use ASSERT(something).

What's the non-MFC way?

Edit: Is it possible to stop assert breaking in assert.c rather than than my file which called assert()?

Edit: What's the difference between <assert.h> & <cassert>?

Accepted Answer: Loads of great answers in this post, I wish I could accept more than one answer (or someone would combine them all). So answer gets awarded to Ferruccio (for first answer).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

那伤。 2024-07-13 11:24:04
#include <cassert>

assert(something);

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

#include <boost/static_assert.hpp>

BOOST_STATIC_ASSERT(sizeof(int) == 4);  // compile fails if ints aren't 32-bit
#include <cassert>

assert(something);

and for compile-time checking, Boost's static asserts are pretty useful:

#include <boost/static_assert.hpp>

BOOST_STATIC_ASSERT(sizeof(int) == 4);  // compile fails if ints aren't 32-bit
念﹏祤嫣 2024-07-13 11:24:04

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

有几种类型的断言:

  1. 预处理器
    这些断言是使用预处理器指令#error
    完成的
    预处理器断言仅在预处理阶段进行评估,因此对于模板等内容没有用处。

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

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

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

我希望这有帮助!

It depends on whether or not you are looking for something that works outside of Visual C++. It also depends on what type of assertion you are looking for.

There are a few types of assertions:

  1. Preprocessor
    These assertions are done using the preprocessor directive #error
    Preprocessor assertions are only evaluated during the preprocessing phase, and therefore are not useful for things such as templates.

  2. Execute Time
    These assertions are done using the assert() function defined in <cassert>
    Execute time assertions are only evaluated at run-time. And as BoltBait pointed out, are not compiled in if the NDEBUG macro has been defined.

  3. Static
    These assertions are done, as you said, by using the ASSERT() macro, but only if you are using MFC. I do not know of another way to do static assertions that is part of the C/C++ standard, however, the Boost library offers another solution: static_assert.
    The static_assert function from the Boost library is something that is going to be added in the C++0x standard.

As an additional warning, the assert() function that Ferruccio suggested does not have the same behavior as the MFC ASSERT() macro. The former is an execute time assertion, while the later is a static assertion.

I hope this helps!

梦明 2024-07-13 11:24:04

断言(通常)仅

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

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

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

在(某些)现实生活应用程序中进行断言

在我们的团队中,我们需要一些东西来检测错误,同时需要一些东西来处理错误。 我们可能在发布版本中需要它。

断言只会在调试构建时检测和处理错误。

因此我们添加了一个 XXX_ASSERT 宏以及一个 XXX_RAISE_ERROR 宏。

XXX_ASSERT 宏将执行与 ASSERT 宏相同的操作,但它将在调试和发布中构建。 它的行为(写入日志、打开消息框、不执行任何操作等)可以由 .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),但在其他地方,它都很好。 事实上,人们可以使用任何首选的错误处理,并且可以在开发人员计算机、测试人员甚至用户上随意激活它,这一事实非常有用。

Assert is (usually) Debug Only

The problem with "assert" is that it is usually in debug binaries, and that some developers use them as if the code would still be in production.

This is not evil per se, as the code is supposed to be intensively tested, and thus, the bugs producing the assert will surely be discovered, and removed.

But sometimes (most of the times?), the tests are not as intensive as wanted. I won't speak about an old job where we had to code until the very last minute (don't asks... Sometimes, managers are just... Ahem...)... What's the point of an assert you adding to a code that will be compiled and delivered as a Release Binary to the client the next minute?

Assert in (some) real life applications

In our team, we needed something to detect the error, and at the same time something else to handle the error. And we needed it, potentially, on Release Build.

Assert will both detect and handle the error only on debug build.

So we added instead a XXX_ASSERT macro, as well as a XXX_RAISE_ERROR macro.

The XXX_ASSERT macro would do the same thing as the ASSERT macro, but it would be built both in Debug and in Release. Its behaviour (write a log, open a messagebox, do nothing, etc.) could be controlled by a .INI file, and THEN, it would abort/exit the application.

This was used as:

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 macro would only "log" the error but would not try to handle it. This means that it could log the message in a file and/or open a MessageBox with the message , and a button to continue, and another to launch a debug session (as per .INI file config). This was used as:

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.
}

One year after their introduction in our libs, only XXX_RAISE_ERROR is being used. Of course, it can't be used on time-critical parts of the app (we have a XXX_RAISE_ERROR_DBG for that), but everywhere else, it is good. And the facts that one can use whatever prefered error handling, and that it can be activated at will, either on the developer computer, or the tester, or even the user, is quite useful.

∞觅青森が 2024-07-13 11:24:04

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

< 断言.h> 是 C 标头

< 卡塞特> 是 C++ 标准库头文件...它通常包括 < 断言.h>

To answer the question in your second "edit":

< assert.h> is the C header

< cassert> is the C++ Standard Library header ... it typically includes < assert.h>

苏大泽ㄣ 2024-07-13 11:24:04

基本断言使用

#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." );

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

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

Basic Assert Usage

#include <cassert>

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

Best Practice Notes

Asserts are used to identify runtime states that should be true. As a result, they are compiled out in release mode.

If you have a situation where you want an assert to always hit, you can pass false to it. For example:

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

It is also possible to pass a message through assert:

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

Mature codebases frequently extend the assert functionality. Some of the common extensions include:

  • Toggling asserts on a per-module basis to localize testing.
  • Creating an additional assert macro that is compiled out in most debug builds. This is desirable for code that is called very frequently (millions of times per second) and is unlikely to be incorrect.
  • Allowing users to disable the currently hit assert, all asserts in the compilation unit or all asserts in the codebase. This stops benign asserts from being triggered, creating unusable builds.
旧情勿念 2024-07-13 11:24:04

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

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

或者:

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

To break inside the file that called the assert, you can use a custom macro that throws an exception or calls __debugbreak:

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

Or:

#define MYASSERT(EXPR) if (!(EXPR)) __debugbreak();
路还长,别太狂 2024-07-13 11:24:04

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

Microsoft-specific CRT asserts

#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());
流云如水 2024-07-13 11:24:04

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

There is a more advanced open source library called ModAssert, that has assertions that work on both Visual C++ and gcc. Probably also on other compilers, don't know for sure. It takes some time to learn it, but if you want good assertions that don't depend on MFC, look at these. It's at http://sourceforge.net/projects/modassert/

相权↑美人 2024-07-13 11:24:04

使用智能感知在 Visual Studio 中打开它(右键单击)

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

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

这对我来说在调用者站点会中断......

这是一个 文章 解释为什么您不想自己编写此宏。

use intellisense to open it in visual studio (right click)

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

yvals.h is windows stuff. so, as far as assert() itself is concerned, the two ways to include it are identical. it's good practice to use the <cxxx> because often it isn't that simple (namespace wrapping and maybe other magic)

This breaks at caller site for me...

here's an article explaining why you don't want to write this macro yourself.

鱼窥荷 2024-07-13 11:24:04

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

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

Here is my most recent iteration of an Assertion facility in C++: http://pempek.net/articles/2013/11/17/cross-platform-cpp-assertion-library/

It's a drop-in 2 files lib you can easily add to your project.

北方的巷 2024-07-13 11:24:04

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

To answer the asker's third question:
the first reason we use "cassert" instead of "assert.h" is because, in the case of C++, there's an allowance made for the fact that the C++ compiler may not store the function descriptions in code files, but in a dll or in the compiler itself.
The second is that there may be minor changes made to functions in order to facilitate the differences between C and C++, either present or in the future. Because assert.h is a C library, the preference is to use "cassert" while in C++.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文