What is the best way of implementing assertion checking in C++?
Asked Answered
V

11

22

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

Valerlan answered 7/10, 2008 at 18:21 Comment(2)
When using C++, it is said that you'd better include c<filename>, where you'd include <filename>.h in C. That it cstdio instead of stdio.h, or as in your case cassert instead of assert.h.Dodson
See also the answers to the related question https://mcmap.net/q/586840/-library-to-facilitate-the-use-of-the-quot-design-by-contract-quot-principle-closed/….Den
P
30
#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
Pomander answered 7/10, 2008 at 18:23 Comment(2)
Be aware that the expression 'something' goes away entirely in non-debug builds, therefore this code: assert(PerformSomeFabulousTrick(kTopeka) != 0); // watch this! will do nothing in release.Ioneionesco
for more information about compile time assertions see here: #174856Thomasinethomason
C
14

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!

Citole answered 7/10, 2008 at 19:3 Comment(0)
R
10

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.

Raiment answered 7/10, 2008 at 21:22 Comment(0)
B
9

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>

Bautzen answered 7/10, 2008 at 18:42 Comment(1)
And since assert() is a macro rather than a function, the C++ header does not put it into the std:: namespace making it a difference only in name of the header.Duffer
O
7

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.
Overshadow answered 7/10, 2008 at 19:6 Comment(3)
You wrote: "[...] identify runtime states that should never be true [...]". Shouldn't it be the other way around? Asserts should always be true.Cypriot
Sometimes, the assert's condition will be false at compile time. It is then very cool to have a static_assert feature to stop compilation.Raiment
Static asserts in Windows: C_ASSERT (msdn.microsoft.com/en-us/library/ms679289.aspx)Dalenedalenna
A
6

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();
Astrogation answered 7/10, 2008 at 18:36 Comment(1)
My favorite solution when the compiler supports it (MSVC).Furness
C
5

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());
Carbaugh answered 7/10, 2008 at 19:56 Comment(0)
V
3

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/

Vaporescence answered 17/11, 2009 at 8:48 Comment(0)
L
1

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.

Lucius answered 7/10, 2008 at 18:55 Comment(0)
P
1

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.

Petronille answered 10/2, 2014 at 20:26 Comment(0)
I
1

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

Incardinate answered 20/11, 2014 at 7:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.