|
Good old assert. Such a well meaning tool that can cause such a ruckus. Assert gets some bad press because it can be so jarring when it works. The documented behaviour for assert in C++ is:
An assert() evaluates its argument and calls abort() if the result is zero (false).
The C++ Programming Language, Special Edition pg 750
In a typical Windows game, the abort part means the DUNH! system sound and an angry red dialogue box telling you the fun is over. If you're not a programmer, you might wonder why anyone would intentionally put this kind of thing in their code. If you are a programmer, you've probably noticed how similar an assert feels to an unhandled exception (i.e. a real crash). This causes everyone to lump asserts into the same mental category as "somebody broke the code".
To be fair, when an assertion fails it does means something bad has happened. Something that the code isn't built to handle. Something that if le ft unchecked will probably lead to a crash (though not always right away), maybe trashing some files in the process, wiping highscores from the server, or some other unpredictable peril.
When something goes wrong in a program, it's like flying a helicopter as a part comes loose. Assert would be like freezing time right when that bolt falls off to figure out what went wrong. Not using assert is analogous to crashing and burning and then digging through the wreckage to figure out what happened.
You can attach the debugger when your game asserts and take a look at what's going on - inspect variables, look at the call stack, and so on. Compared to tracing back from an often unrelated symptom, this kind of debugging is easy.
The assert macro in C++ does nothing in non-debug builds (i.e. when NDEBUG is defined). While this is good in principle, you can never know for certain that there isn't some obscure scenario that could trigger an assert, so it can be useful to have some around even in optimized builds. For this purpose, you probably want to write your own assert function/template/macro that ties into your error handling system and does something a little more graceful than abort(). Something like submitting a bug report would be ideal.
The catch: Assert is not a substitute for error handling. Asserts in
production code should only be used to detect things that are
impossible, or at least would be impossible if the code was written
correctly. They're a tool for detecting coding errors.
Things that aren't coding errors are things like
missing files, failed network connections, out of memory errors, to
name a few. It's not the program's fault when these things happen. They
are runtime errors that you have to detect and deal with yourself.
Assert is a useful tool for building solid, reliable code.
The more you use it, the closer to the real problem you'll find yourself when something goes wrong. Don't be afraid to use assert - and if the DUNH! sound bothers you, you can always try changing your "Critical Stop" system to something less jarring.
|
I would go one step further and recommend that people build a Kernel which controls their run loop, and allows runtime Logging and Profiling. The one that I have built up over the years is pretty much 100% portable and I use it on OS X, Windows and also on the iPhone.
I am a fan of Singletons for that purpose. I know some people are not. If this was GameDev I would already have been lynched by now.. but still.. it works for me. :)
The issue here is that an assert can crash the editor and leave the user without an opportunity to save work.
For truly catastrophic conditions (out of memory/handles/etc...) assert may be your only hope. But for the average "this condition shouldn't be possible" it might not be a good strategy.
*I'm looking at you asserts that fire off every frame in someone elses code*!