Proper Use of Assert and Enforce

Jonathan M Davis jmdavisProg at gmx.com
Wed Mar 14 01:02:53 PDT 2012


On Wednesday, March 14, 2012 06:44:19 Chris Pons wrote:
> I'm new, and trying to incorporate assert and enforce into my
> program properly.
> 
> My question revolves around, the fact that assert is only
> evaluated when using the debug switch.

assert has nothing to do with the debug switch. All the debug switch does is 
enable debug blocks. e.g.

debug
{
    writeln("A debug message.");
}

If you put an assertion in the debug block, then it'll only be there when 
you compile with -debug like any other code, but if you put it outside of 
the debug block, it won't be affected by -debug at all. Rather, assertions 
are compiled in unless you compile with the -release flag.

> I read that assert throws
> a more serious exception than enforce does, is this correct?

Not exactly. Have you used assertions in other languages? In most languages, 
an assertion just outright kills your program if it fails. An assertion 
asserts that a particular condition is true with the idea that if that 
condition is _not_ true, then there is a bug in your program. As such, you 
don't want your program to continue if it fails. It's used to verify the 
integrity of your program. in and out contracts use them as do invariants 
and unittest blocks.

When an assertion fails, an AssertError is thrown, _not_ an Exception. 
Throwable is the base class of all exception types, and Error and Exception 
are derived from it. Errors are for conditions which are normally considered 
fatal and should almost never be caught (for instance, trying to allocate 
memory when you're out of memory results in an OutOfMemoryError being 
thrown). Unlike Exceptions, Errors can be thrown from nothrow functions, and 
the language does not actually guarantee that scope statements, destructors, 
and finally blocks will be run when an Error is thrown (though the current 
implementation currently does run them for Errors and there is some 
discussion of actually guaranteeing that it always will; regardless, 
catching Errors is almost always a bad idea).

Exception and any types derived from Exception cannot be thrown from nothrow 
functions and _are_ guaranteed to trigger scope statements (aside from 
scope(success)), destructors, and finally blocks. They are generally meant to 
be recoverable and catching them is fine. They are generally used to indicate 
that a function is unable to properly perform its function given the current 
state of the program but _not_ because of a logic error. Rather, it's 
because of stuff like bad user input or because a file doesn't exist when it's 
supposed to, so opening it fails. Exceptions are used for reporting error 
conditions to your program. It can then either choose to catch them and 
handle them or let it percolate to the top of the program and kill it. But 
unlike Errors, it _is_ okay to catch them, and it's intended that they not 
indicate an unrecoverable error.

So, you use an assertion when you want to guarantee something about your 
program and kill your program when that condition fails (indicating a bug in 
your program). The checks are then normally compiled out when you compile 
with -release. So, the idea is to use them to catch bugs during development 
but to not have them impede the performance of the program when you release 
it. The only assertions that are always left in are the ones that can be 
determined to be false at compile time (generally assert(0) and 
assert(false), though stuff like assert(1 == 0) also qualify, since their 
result is known at compile time). They're used when you want to guarantee 
that a particular line is never hit (e.g. when a switch statement is never 
supposed to hit its default case statement). The compiler actually inserts 
them at the end of non-void functions so that if the program somehow reaches 
the end without returning, the program will not try and continue.

Exceptions, on the other hand, are _not_ used for bugs in your code, but 
rather error conditions which occur do to circumstances at runtime which are 
not controlled by your program (like bad user input). enforce is simply a 
way to make throwing exceptions look like assertions and save a line of 
code. So, instead of

if(!condition)
    throw new Exception(msg);

you do

enforce(condition, msg);

And if you want to throw a specific exception type, you do either

enforceEx!SpecificException(condition, msg);

or

enforce(condition, new SpecificException(msg));

> I'm trying to use enforce in conjunction with several functions
> that initialize major components of the framework i'm using.
> 
> However, i'm concerned with the fact that my program might
> continue running, while I personally would like for it to crash,
> if the expressions i'm trying to check fail.
> 
> Here is what i'm working on:
> 
> 	void InitSDL()
> 	{
> 		enforce( SDL_Init( SDL_Init_Everything ) > 0, "SDL_Init
> Failed!");
> 
> 		SDL_WN_SetCaption("Test", null);
> 
> 		backGround = SDL_SetVideoMode( xResolution, yResolution,
> bitsPerPixel, SDL_HWSURFACE | SDL_DOUBLEBUF);
> 
> 		enforce( backGround != null, "backGround is null!");
> 
> 		enforce( TTF_Init() != -1, "TTF_Init failed!" );
> 	}
> 
> Is it proper to use in this manner? I understand that I shouldn't
> put anything important in an assert statement, but is this ok?

Shouldn't put anything important in assert statements? I'm afraid that I 
don't follow. Assertions are compiled out when you compile with -release, so 
they cannot be used when you want to guarantee that they fail even with -
release or if you have code in them that must always run. So, for instance, 
if the foo function _had_ to be run, doing

assert(foo() == expected);

would be bad. You'd do

immutable result = foo();
assert(result == expected);

instead. The same concern does not exist with enforce, however, since it's 
never compiled out.

As for whether assert or enforce should be used, does failure mean a bug in 
your program or simply that you've hit an error condition. From the looks of 
your code, it would probably simply be an error condition in this case, 
since your verifying that some initialization functions succeed. If they 
fail, it's not a bug in your program but rather that something else 
(probably outside of your control) went wrong. So, enforce is the correct 
choice. And if nothing calling initSDL (directly or indirectly) catches the 
exception, then the exception will kill your program, but it can be caught 
with a catch block if you choose to. If you want to _guarantee_ that the 
program dies if any of them fail, then you should probably create a subclass 
of Error (e.g. SDLInitError) and throw that, in which case, your first 
enforce would become

enforceEx!SDLInitError(SDL_Init(SDL_Init_Everything) > 0, "SDL_Init 
Failed!");

Hopefully I didn't overload you too thoroughly and this at least gives you a 
better idea of the difference between assertions and exceptions (and 
therefore between assert and enforce). Assertions are for verifying the 
integerity of your program and failure means that your program has a bug, 
whereas exceptions are for reporting error conditions which do _not_ 
indicate a bug in your program.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list