Stupid C++ Tricks: Adventures in Assert

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.

 

[Update 10 March 2008] Pow2Assert.h and Pow2Assert.cpp are released under MIT License.  -chas

 

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.  An example, if you've never seen it (except, of course, for about a page ago):

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

This can be useful 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 int 3:

#define POW2_HALT() do { __asm int 3 } while(0)

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
{
bool Handle(char const* condition, char const* msg, char const* file, int line);
typedef void (*Handler)(char const*, char const*, char const*, int);
void SetHandler(Handler newHandler);
}}

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(x) \
do { if ((!(x)) && pow2::Assert::Handle(#x, __FILE__, __LINE__)) \
POW2_HALT(); } while(0)
#else
#define POW2_ASSERT(x) \
do { (void)sizeof(x); } while(0)
#endif

We're in the home stretch!  This is actually our final code, but we're not done discussing things yet.

 

 


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:


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:

 

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

 


 

So that's pretty much it for Assert.  We're still tinkering with it every now and then, but it's about as solid as we know how to make it at this point.  It's certainly serving us well in the meantime.  Here's our current implementation as of this article.

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

Warnings

while(0)

causes the compiler to throw "main.cpp(20) : warning C4127: conditional expression is constant" style warnings when using Warning Level 4 in Visual Studio 2005.

My solution is to change this into:

while((x) == !(x))

and to change the POW2_HALT macro to:

POW2_HALT(x)

so you can do the same there.

This seems to generate the same assembler output for me.

Curious...

I'm checking out the "do { (void)sizeof(x); } while ((x) == !(x))".  In the assembler listings i'm looking at (gcc -O3 -Wall --save-temps) and MSVC8 SP1, i'm seeing 'x' get evaluated twice, and this is for the 'stubbed out' assert (i'm not posting it to keep the comment short).  In my case 'x' is a call to a function that is declared extern, so the compiler can't know that there are no side effects.

In visual studio we disable 4127 because we often explicitly write loops with constant termination conditions: "while (1) { DoStuff(); if (exitCondition) break; }" kinds of things.

infinite loop + break

while(1) can be replaced by for(;;) which should normally not trigger a warning.

Compiler warning 4127

I worked around that one by declaring something like

extern const bool g_false;

in the header, and initializing it to, um, false, in the source file.

const bool g_false = false;

Presto, warning goes away, and the compiler should be able to optimize that one to something reasonable. It does cost a global, but it's a const, so if something tries to write to it, the compiler will scream, and the compiler may take the const hint and optimize the storage away in the final assembler? I suppose if the data section of the code gets smashed, g_false may become, er, g_true, but if things have gotten that bad, you're screwed no matter how you look at it.

More on warning 4127

I find that the VS2005 compiler in release mode, completely optimizes away that do { (void)sizeof((exp)); } while(g_false); line, no code gets emitted for it. When building in debug mode, it does emit an extra instruction to read g_false before comparing r with r and then bne'ing, however. But in debug mode, I'd be running the slow hairy version of the ASSERT() macro anyway, so it doesn't matter.

Thanks for all the tips on assert macros. And if you ever work with Windows CE, don't ever set the warning level past 3 - the system headers are horribly broken, and will start warning :P

Block delimiters vs. do...while()

I was about to say that while((x) == !(x)) will evaluate x twice if x is a function, but it seems I'm a bit slow :)

Anyway, here is my real contribution to this post: what's wrong with normal block delimiters? Something like:

#ifdef POW2_ASSERTS_ENABLED
    #define POW2_ASSERT(x) \
        { if ((!(x)) && pow2::Assert::Handle(#x, __FILE__, __LINE__)) \
            POW2_HALT(); }
#else
    #define POW2_ASSERT(x) \
        { (void)sizeof(x); }
#endif

That's perfectly valid, and I don't see any other problem with it (as it's strictly equivalent to a loop which is executed once). At least, that's how I see it. Do I miss something obvious?

do/while

It's a big problem when they're used in if/else blocks.  Check here for more info.

re: do/while

I see the "problem" - however this is not that much a problem: this code doesn't compile:

#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }

if (x > y)
exch(x,y); // Branch 1
else
do_something(); // Branch 2

(because of the empty statement it adds before the else clause).

Moreover, the style is quite awful and proper use of block delimiters would correct everything (but linux hackers are not used to proper use of block delimiters (no offense intended)). So if the reason is to allow the programmer to write code that is ultimately dangerous (by not forcing him to properly use block delimiters in conditionals) then I see why the do/while construct can be of interest. But for production code, the safest construct is not what would make bad code easier to write (imho).

#define MY_ASSERT(condition)

#define MY_ASSERT(condition) \
if (!(condition)) { DoAssertStuff(); } else (void)0

This has the same advantages as the "do { } while (0)" idiom (i.e. unbraced if/else works correctly), without the 'conditional expression is constant' compiler warning and without the possibility of unnecessarily evaluating an expression twice, as in the "do { } while ((x) == !(x))" idiom.

From C++ FAQ Lite

Good solution!

Cool, I didn't know about that idiom. Looks cleaner than the do {} while (0) one. I'll try updating our macros. Thanks!

thanks for information.

thanks for information.

Why not ternary?

Just curious why you use statements for your assert, instead of a ternary macro. The canonical assert is normally something like this

#if ASSERT_ENABLED
#define assert(expr) ((void)((expr)?((void)0) : AssertFail(#expr, __FILE__, __LINE__)))
#else
#define assert(expr) ((void)0)
#endif

which evaluates expr once (important), returns void, and compiles out cleanly.

The bit about wanting to force a reference on the expression when asserts are disabled is actually going down a wrong path, in my opinion. If a function returns a status, you need to do something with it; asserts are not error handling. If a function always returns true, then why is it returning anything? In that case, you put the assert in the function and then you don't need to return anything from the function, which is certainly more efficient in release.

You should also switch to using __debugbreak, at least on MSVC, because you can't do asm { __int 3 } in 64-bit builds. You should also wrap the __int 3 inside a __try block, so that you only break to the debugger if there is actually a debugger present, e.g. something like this

__try { __debugbreak(); } __except(EXCEPTION_EXECUTE_HANDLER) {}

This way, your debug builds running in QA (or a friend's computer, or not in the compiler) don't crash the program.

The handler trick is still cute, and I might adopt it in our own code. I am also at the point where I'm going to junk __FILE__ and __LINE__ everywhere and rely on stack crawls to get that for me. I'm also considering leaving asserts on in release builds (asserts that log, not halt), and use a preprocessor to annotate all asserts with unique numbers so that release asserts don't leak implementation details.

Fun stuff.

OK, responding in order

OK, responding in order here:

1.  We don't use ternary macros because we need more complicated logic than that.  The macro you posted only evaluates the condition- we have a handler function that says whether the program should continue running (really only for running unit tests).  :? format gets really ugly when you start nesting them, so we went with an if statement.

2.  The code you posted doesn't compile cleanly when asserts are disabled, temporary variables that are only used for assert can go initialized but unused.  That was the reason for going down the road of sizeof.  That way the compiler will always be quiet, we don't have to disable an otherwise useful warning, and the assert parameter goes unevaluated.

3.  "If a function returns a status, you need to do something with it":  Almost all DirectX calls return HRESULT for their status.  If our call to DrawIndexedPrimitive fails we know we have a bug that requires our attention ASAP, so we capture the HRESULT in a local const variable, then assert that it succeeded on the next line.  That return code variable isn't used anywhere else.  We don't want to gracefully recover and keep going, we want to stop the show right then and there so we can deal with it.  Similarly, we're not at the point where we want to gracefully handle missing files, so we just assert that our new FILE* is non-null.  You're right in saying that "Asserts are not error handling", they're error sledge-hammering.

4.  We have since switched to using __debugbreak(), thanks to Ignacio and you for the suggestion.  We're not on 64-bit but I'll almost always prefer an intrinsic to inline asm.

5.  __try/__except and "This way, your debug builds [...] don't crash the program":  Crashing the program is exactly why we assert!  If our game is running and an assert fires, it's because there's no meaningful way for the application to continue and who knows what could happen if execution continues.  If a function can fail in an 'ok' way (e.g. a network player disconnected so sendto() no longer works), we don't assert that.  We don't care if we're in the debugger or not, the app should stop dead in its tracks.

6.  The handler trick:  Having a handler is a great way to do strack frame grabs, I have that in my toy engine at home for asserts, memory allocations, log messages, etc...  We'll probably get there eventually but haven't had a need for it yet.  Also, if you omit frame pointers in release build you'll often get totally wrong stack frame captures, so having __FILE__ and __LINE__ can be handy too.

Just add a new macro?

Team's I've been on have always solved the unused variable issue by making a new macro, VERIFY. Ignoring all the do/while stuff above the code is something like

#if DEBUG
#define VERIFY(x) { if (!(x)) __debugbreak(); }
#else
#define VERIFY(x) { x; }
#endif

So instead of this

bool success = DoStuff(); ASSERT(success);

we do this

VERIFY(DoStuff());

Less code, no warning issued, intent clearer

More unused fun

Hey, the sizeof() trick is excellent! I'll offer an extension to it:

extern int DummyHelper(...); // Note, this function's body is NEVER defined because it is never called.
#define MY_UNUSED(...) ((void)sizeof(DummyHelper(__VA_ARGS__)))

Most compilers now implement the __VA_ARGS__ I guess. This lets you put any number of comma-separated expressions as parameters to the macro. This is useful in the sense that it can be more compact. However, the true power comes when people start asking for assert-like logging functions that use printf-style parameters:

#ifndef MY_LOG_ENABLED
#define MY_LOG(...) MY_UNUSED(__VA_ARGS__)
#else
#define MY_LOG(...) fprintf(stderr,__VA_ARGS__) // your choice of printf-style log output function
#endif

Sooner or later, you will :) have some form of ASSERT_MSG(condition, ...) for more readable or detailed assertion messages, and this comes in handy as well.

As a note, the ((void)sizeof(whatever)) expression shouldn't need any do.. while(0) around it; it doesn't hurt, but it doesn't help either.

C++ booleans are nasty

'bool' is an insidiously nasty thing in C++. Yeah, it's handy for many two-state items, but it actually can quickly turn to obfuscation. E.g. I've seen many game engines where game objects have a Reset method which takes a boolean parameter. When you're familiar with the code, you'd know what myObject.Reset(true) means vs. myObject.Reset(false), but the fact is that many won't understand what it means. This only gets compounded when you've got more than one. (Clearly, for cases like, myObject.SetVisibility(true) it's a lot less ambiguous!)

My proposal, then, is to use enums instead of booleans for your assert handler. Sure, the enum only has two values, but that just makes it easy to install!


// choose the naming convention that you like. Sadly the code tag doesn't do indents?
enum AssertHandlerResult
{
ASSERT_CARRY_ON_MERRILY_NOTHING_TO_SEE_HERE,
ASSERT_STOP_DEAD_DO_NOT_PASS_GO
};

...and this has the added benefit of being able to easily and non-destructively extend what your assert handlers can report down the road! However, now your assert handlers aren't returning 'true' or 'false' without context (or worse, with a comment explaining why one or the other was chosen!) but in fact now it has documentation that is executed.

Literate programming FTW! OMG! ;p

Asserts vs. Exceptions

Great bit of code guys, thanks.

What are your thoughts on exceptions and asserts? There seems a lot of opinion on the matter, with the concensus that exceptions handle unexpected but predictably mad user input whereas asserts handle conditions that should definitely never occur (bugs).

If the above statement is true then the average game should almost never use an exception because user input is usually limited to up/down/left/right/fire?

I've heard some people say that exceptions lead to nicer code that avoids reams of return value error checking and therefore it should be used everywhere. The idea being not to return errors anywhere at all, but throw exceptions like mad and just wrap a try block arround the whole application.

I know exceptions were not widely supported on consoles a few years ago, but Win/Linux/Mac/X360 handle them ok. They also cause exe bloat, but again these days it isn't usually seen as an issue unless the platform is restricted. Do you use exceptions?

nope

We use exceptions all over the place in our C# code, but our pure C++ runtime has no exceptions.  The various Herb Sutter books (Exceptional C++, More Exceptional C++, Exceptional C++ Style) explain all of the perils with C++ exception handling, and when we realized that there seemed to be as many special 'gotcha' cases as not, we opted against using them.

That said, the totally sweet C++ unit testing framework UnitTest++ uses Structured Exception Handling on VS2003 and VS2005 to 'catch' crashes like null pointer dereferences and report them as failures.  Charles Bloom (lead programmer on the final Oddworld game) also reported using them in the game runtime to prevent catastrophic failure.  Evidently, enemy update code was wrapped in a try/catch and enemies whose AI took a crash path just 'froze' in place while the rest of the game merrily continued.  Cool stuff, but when you do the equivalent on posix-style consoles you have to get into evil signal handlers, etc... and we found that using TDD has kept our crash bugs to a minimum, or at least easy to diagnose when they do pop up.

the sizeof trick

The sizeof trick is clever but the side effect of using it is that the compiler will no longer warn you when you want to know that a variable is not used except for the assert. And it won't compile away the code to initialise that variable - only the assert code itself eg:

int n = some_lengthy_calculation_only_for_assertion_testing_purposes();
POW2_ASSERT(i < n);

Copyright? License

What is the License and Copyright for this code?

Certainly learned a lot from this posting and the code, but wanting to use it.

Thanks!

Here's our header and cpp

Here's our header and cpp file, released under the MIT license.  Enjoy!  (see the top of the page for the files)