【问题标题】: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++ debugging assert debugbreak


【解决方案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); // 看这个!在发布时不会做任何事情。
  • 有关编译时断言的更多信息,请参见此处:stackoverflow.com/questions/174356/…
【解决方案2】:

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

有几种类型的断言:

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

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

  3. 静态
    正如您所说,这些断言是通过使用 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”。

              【讨论】:

                猜你喜欢
                • 2011-05-24
                • 1970-01-01
                • 2013-02-21
                • 2011-02-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-03-10
                相关资源
                最近更新 更多