Stupid C++ Tricks: Adventures in Assert

February 10, 2009

This is a re-hosting of the original Assert article I wrote in April 2007,  during the epic rise and fall of Power of Two Games.  I’m reprinting it here on my personal site now that Pow2 is defunct and expired. Of the few reasons we failed, the lack of a good assert macro was not one of them!

Grab Pow2Assert.h and Pow2Assert.cpp, now under the MIT license!

The simple joys of hack-and-slash prototyping are over!  We’re convinced the gameplay is fun, so it’s time to buckle down and start coding “for real.”  We’re saying goodbye to the guilty pleasures of undisciplined C++ hacking and hello to solid, well-engineered code.  I recently had the pleasure of writing our assert macros and came across some surprisingly challenging problems.  This article is about how to make your assert macro as bulletproof as possible when you want it, and totally disappear into nothingness when you don’t.

We’re very aggressive about assert.  Noel even cared enough a couple years ago to stand up to the haters!  We assert everything from pointer function parameters to Direct3D return codes.  We use it everywhere so it’s got to be absolutely rock solid.  “No problem,” you think, “assert is some beginner C++ stuff.  I’m a C++ animal!  I can write assert macros in my sleep!”  Most assert code looks something like this (Assert::Fail is a function that does about what you’d expect, report the assert and terminate the program):

#define POW2_ASSERT(x) \
    if (!x) { pow2::Assert::Fail(#x, __FILE__, __LINE__); }

Take a minute right now and think of everything you’d fix about this assert macro.  If this code makes you run screaming for the hills (and scheduling a heart-to-heart with your lead programmer), good for you!  If you look at this code and think “yeah, that’s pretty cool chas, I dig that code!” then unhook the paint can from your face, keep reading, and promise me you won’t write any assert macros until you’re done.

OK we’ll go step by step here, in order of severity (and effect on sanity!).  Let’s start with the basics:

1.  Always wrap your macro parameters in parentheses.

Would you expect the following code to trigger an assert?

POW2_ASSERT(true || false);

The correct answer, by the way, is “absolutely not.”  But look at what it expands into when subjected to our awful assert macro:

if (!true || false) { pow2::Assert::Fail(...); }

Yeah, oops. “!true || false” of course collapses into “false”, which causes the assert to fire. Not what we want at all.  The standard fix is to always wrap your macro parameters in parentheses.  The corrected macro thus far now reads:

#define POW2_ASSERT(x) \
    if (!(x)) { pow2::Assert::Fail(#x, __FILE__, __LINE__); }

Not great, but slightly better.

2.  Wrap your macros in do { … } while(0).

What would you expect to have happen here?

if (x == 3)
    POW2_ASSERT(y == 4);
else
    DoSomethingReallyImportant();

The actual code is much different:

if (x == 3)
    if (!(y == 4))
        pow2::Assert::Fail(...);
    else
        DoSomethingReallyImportant();

Not so great. There are even scarier cases, too!  Anyway, the ‘canonical’ and ugly-as-hell solution is to wrap your macros in a “do { … } while(0)” line.  Note the lack of semicolon at the end there, it’s very important.  It’s a surprisingly involved discussion that I won’t rehash because it’s in the comp.lang.c FAQ and you’re starting to get a little bored by now.  Don’t worry, the best fun is yet to come! 

Here’s our best stab at assert yet:

#define POW2_ASSERT(x) \
    do { if (!(x)) { pow2::Assert::Fail(#x, __FILE__, __LINE__); } } while(0)

 

3.  Go away!  Sit in the corner, face the wall; I don’t want to even know you’re here.

Asserts are great!  Let’s assert everywhere that our vectors are normalized!  Let’s assert that our strings are all exactly 12,592 bytes long by scanning for the first ‘\0’!  Let’s assert that every time we clear the back buffer, each pixel is set to burnt umber!  Let’s figure out why we’re only rendering at 1fps in release build!  Hmm.

A very nice thing to be able to do with assert is compile it out of existence.  The standard form looks something like this:

#ifdef POW2_ASSERTS_ENABLED
    #define POW2_ASSERT(x) \
        do { if (!(x)) { pow2::Assert::Fail(#x, __FILE__, __LINE__); } } while(0)
#else
    #define POW2_ASSERT(x)
#endif

OK, now it compiles out, and things are well and good, right? Sure, if you like spurious compiler warnings.

const bool success = DoStuff();  POW2_ASSERT(success);

Gives us this output (in MSVC8 but gcc gives an almost identical one):

main.cpp(7) : warning C4189: 'success' : local variable is initialized but not referenced

Not great.  These warnings only show up on MSVC’s (level 4) and gcc’s (-Wall) highest warning levels, but they can be valuable in other situations so we don’t want to disable them.  We can try the standard cast-to-void trick:

#define POW2_ASSERT(x) do { (void)(x); } while(0)

But then x is still evaluated.  Both gcc and MSVC are smart enough to optimize out the evaluation of x, but only if they can determine if  there are no side effects associated with the evaluation.  Unfortunately, this can only be done if the body of x is known entirely to the compiler.  If x is a function call to another module MSVC can still make it go away with Link-Time Code Generation (via cross-module inlining), but poor gcc is dead in the water and emits the call.  Either way, we’re relying on compiler and linker optimizations to make this code go away.

This code:

int main(int, char*[])
{
    bool DoStuff(); // comes from another .cpp file
    POW2_ASSERT(DoStuff());
    return 0;
}

Causes gcc (-O3 -pedantic -Wall –save-temps) to emit the following assembly:

_main:
pushl %ebp
movl $16, %eax
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
call __alloca
call ___main
call __Z7DoStuffv
leave
xorl %eax, %eax
ret

We’re not so crazy about the whole “call __Z7DoStuffv” line, but at least the compiler shut up.  We’re halfway there, I suppose.  The standard “UNUSED” macro has the same problem:

#define POW2_UNUSED(x) do { (void)(x); } while(0)

This can be useful for shutting up warnings during deep spelunking in the bowels of template code, but not here. The parameter is still evaluated and now we’ve also got this extra little clingy macro that follows our assert around everywhere. Nope, unused won’t do the trick here.

The best optimization you can ever do is to completely remove the code in question.  Both gcc and MSVC have pretty decent optimizing compilers, but why bother requiring them to be good at their jobs?  If there were a way we could force assert to boil down to nothing when we want and not emit any spurious warnings, we’d totally be money. 

Boy, if only there were some C++ keyword that could syntactically accept almost anything and be guaranteed not to emit any code.

Enter the ever-humble sizeof keyword.  This little guy must be the absolute bane of C++ compiler writers.  Alexandrescu thoroughly exploits it in Modern C++ Design, and we can steal a page from his playbook here.  Let’s check the C++ Standard, (5.3.3), shall we?

The operand is either an expression, which is not evaluated, or a parenthesized type-id.

Now I’m nowhere near as tricky as Alexandrescu or the slew of C++ luminaries who have discovered new and insane things to do with this awful language, but I know a useful keyword when I see it!  Baby, you had me at “which is not evaluated”.  Let’s rig it up and take it for a spin.  Note that gcc will correctly warn about the sizeof statement “having no effect,” but we can shut that up easily enough using the cast-to-void trick mentioned earlier:

#define POW2_ASSERT(x) do { (void)sizeof(x); } while(0)

Our old test case now emits the following assembly in gcc:

_main:
pushl %ebp
movl $16, %eax
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
call __alloca
call ___main
leave
xorl %eax, %eax
ret

And on MSVC8:

_main PROC
xor eax, eax
ret 0
_main ENDP

Our warnings are now silenced on both gcc and MSVC, too!  Looks pretty good to me. Let’s look at our final code, which is getting much closer to our quality bar:

#ifdef POW2_ASSERTS_ENABLED
    #define POW2_ASSERT(x) \
        do { if (!(x)) { pow2::Assert::Fail(#x, __FILE__, __LINE__); } } while(0)
#else
    #define POW2_ASSERT(x) \
        do { (void)sizeof(x); } while(0)
#endif

 

4.  Assert should always halt execution of the program, except for when it shouldn’t.

The standard trick on Windows under MSVC to halt the program and break into the debugger is to use __debugbreak():

#define POW2_HALT() __debugbreak()

This is always what you want to do when your assert fires, right?  I mean, it’s an assert!  You can’t really continue gracefully, even if you wanted to.  Things are too insane.  That’s why you asserted in the first place!

Except maybe if you’re running unit tests.  If an assert fires inside a unit test, you probably don’t want a “friendly” modal dialog box to pop up and require you to hit abort, retry, or cancel.  You certainly don’t want that modal dialog box popping up on your poor automated build server.  You want to terminate that particular unit test and keep going.

That makes the requirements of your assert code a little more demanding.  Does it have to know if it’s running inside tests?  How can it possibly know these things even if you wanted to couple your code to your tests?  It can’t.  Not in any sort of clean way, anyway.  Not in any way that doesn’t couple your code too tightly for our comfort.

Assert::Fail hasn’t really been doing much of interest to date.  You’ve probably assumed that it prints the failed assert to the screen in a compiler-friendly format and then halt the program using a macro like POW2_HALT() above.  If it trampolined directly into a user-provided handler, it could report whether or not to terminate the program.  Let’s change it from:

namespace pow2 { namespace Assert
{
    void Fail(char const* condition, char const* msg, char const* file, int line);
}}

to this:

namespace pow2 { namespace Assert
{
	enum FailBehavior
	{
		Halt,
		Continue,
	};

	typedef FailBehavior (*Handler)(const char* condition, 
                                    const char* msg,
                                    const char* file, 
                                    int line);
	Handler GetHandler();
	void SetHandler(Handler newHandler);

	FailBehavior ReportFailure(const char* condition,
                               const char* file,
                               int line,
                               const char* msg, ...);
}}

We hide the current handler and default handler in Assert.cpp so as to keep our header file as simple as possible.  Now we can set any handler we want, and the app will only halt if the handler returns true. For our unit tests, our assert handler reports the assert to our testing framework, and the test fails gracefully.  Let’s beef up our macro yet again:

#ifdef POW2_ASSERTS_ENABLED
    #define POW2_ASSERT(cond) \
        do \
        { \
            if (!(cond)) \
            { \
                if (pow2::Assert::ReportFailure(#cond, __FILE__, __LINE__, 0) == \
                    pow2::Assert::Halt) \
                    POW2_HALT(); \
            } \
        } while(0)
#else
    #define POW2_ASSERT(condition) \
        do { POW2_UNUSED(condition); } while(0)
#endif

We’re in the home stretch!  This is actually our final code, but we’re not done discussing things yet. Note also in the real Pow2Assert.h there are overloaded forms of these macros that support variadic parameters for printf-style formatting. I’ve omitted them here for brevity.
 

5.  Hooray!  My assert fired!  Where the hell am I?!

The final common problem with assert macros is that they often leave you alone and lost in the wilderness, about 3 stack frames away from the code that actually triggered the assert.  I hope to inflict a violent emotional reaction by showing you this next image:

Useless callstack from inside VS80 CRT "assert" function

Ahh yes.  Thanks a ton MSVC for taking me to…  _NMSG_WRITE(int rterrnum=10).  It’s at line 198 of crt0msg.c, if that helps you.  But it doesn’t.  Know why? Because it’s completely useless!  _NMSG_WRITE doesn’t have a damn thing to do with why our program is now dead.

The nicest side effect of item 4 (moving the POW2_HALT() back to the assert macro itself) is that the fired assert lands you in the debugger AT THE SITE OF THE FAILING ASSERT.  Sorry to yell, but it’s important.  Look at this call stack and tell me it’s not nicer:

wonderful-callstack

And we got it as a free side effect of making our assert macro cooler!

So that’s pretty much it for Assert.  I’m still tinkering with it every now and then, but it’s about as solid as I know how to make it at this point.  It’s certainly serving me well in the meantime. 

Credit goes to Steve Rabin for his chapter in Game Programming Gems 1, it was the inspiration for item 5.  Thanks as always to Andrei Alexandrescu for opening my eyes to the sheer blinding madness of sizeof.  Tom Plunket was good enough to beat the do/while(0) stuff into me.

18 Responses to “Stupid C++ Tricks: Adventures in Assert”

  1. […] I give you for this is outlined in a post about Assert from they guys at Power of Two Games: Stupid C++ Tricks: Adventures in Assert). Briefly, you need to test that an assertion thrown you will need to add some additional hooks […]

  2. […] nice little quickie:  I briefly discuss in my assert ramblings why it’s important to wrap all of your multi-line macros in do/while(0) blocks.  An […]

  3. awesome article charles, upgrading ASSERT macro in 3-2-1 … :)

  4. Sad to hear about Pow2, but I was just searching for this awesome macro… I love it. :)

  5. One feature that’s missing: you can’t use an assert() as an expression. Specifically, you can’t use it in a ternary operator:

    condition ? POW2_ASSERT(something) : somethingElse();

    Granted, this isn’t a terribly common or useful construct. But it is the reason why the ANSI C assert() macro is specified to be an expression of type void. You can implement this feature by replacing the if() in the definition of POW2_ASSERT() with a ?: and judicious use of parentheses and the comma operator.

  6. [quote][i]Not great. These warnings only show up on MSVC’s (level 4) and gcc’s (-Wall) highest warning levels[/i][/quote]

    I use the standard assert provided in C/C++ STL. I don’t get that warning (or any other) even with ‘-Wall’ in my compiler’s paramerets. I use Dev-C++ 4.9.9.2, which is build on gcc and g++.

    [quote][i]We can try the standard cast-to-void trick:
    #define POW2_ASSERT(x) do { (void)(x); } while(0)[/i][/quote]

    Standard C/C++ STL define’s null version of assert as follows:
    #define assert(x) ((void)0)

    No tricks, no hassle, so why bother writing your own when STL contains equally good (or better?) solution? Or did you forget to mention something, Charles Nicholson?

    Having your own handler for errors is not an excuse: you can use the signals.h (which is also provided in C/C++ STL) to set a custom handler for SIGABRT signal, which is raised when STL’s assert() detects failure, and thus either default signal handler for that signal or your custom handler is called.

    I did it with gcc and C/C++ STL, i get zero warnings wether or not NDEBUG is defined and i have my own, custom error handler (log, cleanup, shutdown).

    So, my point is: Why see all that trouble?

  7. 1. There is no such thing as the “C/C++ STL”. The STL is the Standard Template Library, and it’s a set of generic and robust containers and algorithms for C++. It is not available for C, because it is built heavily upon C++ templates. You happen to be using one implementation of the STL from one specific vendor that may or may not publish an assert macro. I suspect you’re confused, intended to write “The C++ Standard Library” instead of “C/C++ STL”, and are really using the one in the cassert system header. I’m not sure, however, because you didn’t post a complete program anywhere.

    2. Using signals is significantly less useful than having your own macro, because your signal handler doesn’t have any callsite information that you can report to the user. __LINE__, __FILE__, and __FUNCTION__ will only tell you that you’re in your signal handler, and they won’t tell you where the assert happened. You absolutely need to write your own macro if you want to use a custom error handler AND know about the callsite.

    I guess my answer to your final question “Why see all that trouble” is simply “Because there’s no easier way to do it.” You’ve presented an inferior solution, and if you’re happy with it, then by all means use it.

  8. The following code

    #define NDEBUG 1

    #include

    int main()
    {
    bool failed = false;
    assert(failed);
    }

    shows ‘unused variable’ warning for both GCC and MSVC…

  9. I’ve found that the code below does a decent job on several compilers at silencing that pesky constant conditional expression warning.

    // This is simply not enough on some compilers.
    do {} while(__LINE__ == -1)

    Instead I use these macros to surround multi-line macros.
    #define GorkMacroOpen do {
    #define GorkMacroClose } while(__LINE__ == -1, false)

    Looking forward to more articles Charles :)
    It’s time to update

  10. Just a note that (at least under the version I’m using) __builtin_trap() in GCC works like __debugbreak(). I’m finding this real handy with Xilinx’s GDB-based debugger for the PowerPC405 on their Virtex-4 FPGAs. Thanks!

  11. THANK YOU SO MUCH! I was in the middle of banging my head down to a few thousand lines of code when I stumbled upon your page. The problem:

    #define ASSERT( n )\
    if ( !n )\
    {\
    return;\
    }

    and eventually got a hint when this code wasn’t returning from the block:

    ASSERT( 2 > 2 )

    Thank you >.<

  12. Thanks everyone for the article and comments. This is how my C/C++ assert macros look like now:

    #include

    typedef int (*AssertHandler)(char const*, char const*, int);

    int default_assert_handler(char const* expr, char const* file, int line)
    { fprintf(stderr, “Assertion (%s) failed in %s (%d)\n”, expr, file, line);
    return 1;
    }

    AssertHandler assert_handler = default_assert_handler;

    #if defined(_MSC_VER)
    #define ASSERT_HALT() __debugbreak()
    #else
    #include
    #define ASSERT_HALT() exit(__LINE__)
    #endif

    #ifdef DEBUG
    #define assert(x) ((void)(!(x) && assert_handler(#x, __FILE__, __LINE__) && (ASSERT_HALT(), 1)))
    #else
    #define assert(x) ((void)sizeof(x))
    #endif

  13. […] articolo di JP sulle nuove assert statiche del C++0x. riferimenti: http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ http://msinilo.pl/blog/?p=212 Share and […]

  14. This page is a great source of general information not just about asserts. I reference it often.

    So when I saw this i thought I should return the favour:

    http://www.jaggersoft.com/pubs/CVu11_3.html

    : D

  15. […] I give you for this is outlined in a post about Assert from they guys at Power of Two Games: Stupid C++ Tricks: Adventures in Assert). Briefly, you need to test that an assertion thrown you will need to add some additional hooks […]

  16. The “(void)sizeof(x)” trick does no longer work in VS2010, which is a pity because I use it all of the time.
    Does anyone know a replacement that accomplishes the same?

  17. […] such as a tracing heap allocator, static and run-time assertions (thanks Niklas Frykholm, Charles Nicholson and Tom Forsyth) as well as logging of various debug-streams to […]

  18. […] stumbled upon an informative article: http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ which pointed out a great number of problems that exist in my current suite of debugging […]

Leave a Reply