« A very short technical report | Main | Rational points on elliptic curves »

Tuesday, 05 January 2010

Why I Don't Like Assertions

***Note that this is not a Luther Martin post. It's the first Steve Burnett blog post in months.***

In the C and C++ programming languages there is something called an "assertion". It can look like this.

int Subroutine (int arg1, Pointer arg2) {
  ...
  assert (arg2 != NULL);
  ...
}

The idea is that when your code is compiled in debug mode, it will assert that that arg2 is not NULL. If arg2 is NULL, the program will either break (if in the middle of a debug session) or will crash. In this way, during the development and test phase, you can more easily find bugs.

The most common use of assertions is to test incoming arguments and return values from subroutine calls, however, you can use them for anything. Also, these tests are made only when the code is compiled in debug. The assertions go away in the released version of the code.

It sounds as if assertions are such a great feature, how could anyone possibly object to them? Here are my reasons for not using them.

1. It's system or C runtime code. Here at Voltage we write portable code. Our code has to run on Windows, Linux, Solaris, mainframe, HSM, POS, and other platforms. The biggest porting problem is system or C runtime calls. These are the basic memory allocation, file manipulation, printing, networking, and other such functions. If your code is to be ported to other platforms, you should avoid making such calls (there are often ways to get around them), or if you must call them, you should call them through a wrapper (don't call malloc, call Z3Malloc, for example). Why this is the case would be the subject of a series of blog posts in itself, but based on my 15+ years of porting experience, if you can avoid making a system or C runtime call, avoid it on principle alone.

2. If a check is worth making in debug mode, it's probably worth making in release mode. Let's say your assertion is testing input arguments or possibly the return value of another subroutine. So you test to make sure something is not NULL or that an int arg is not negative (for instance). Why not test those conditions in release mode? The idea is that testing variables takes time. Take that time in debug mode, but save a few cycles and some code size in the release version. Here at Voltage, execution time and space is so dominated by elliptic curve and pairing operations, that 10, 20, or even 100,000 cycles (or 50 to 100 extra bytes of code size) spent on variable testing would be unmeasurable. It would be less than a blip on the total program. Release code is more important that debug code, so wouldn't it be more important to make the checks there? For example, if you don't want to divide by zero, you especially don't want to divide by zero in the release version, so why check for that condition using an assertion when that means only the debug version will make the check?

3. Testing will find these problems anyway. Suppose you are the developer and are running through the code in a unit test. Also, let's say you did not put in an assertion to check a particular input arg for NULL. And let's go further and say that somehow that arg was NULL. When you try to use that arg, you'll get a NULL pointer exception or crash, or the debugger will stop at this point in the code (isn't that pretty much functionally equivalent to what would have happened if you had used an assertion?). That is, even without an assertion, you'll find that NULL pointer. And if your unit test didn't find it, then the regular testing (QA) had better find it.

4. What do you do when an assertion finds a problem? You either make sure the calling subroutine passes in the correct arg or a called subroutine never returns a bad result (see number 3 above). Or you realize that the calling or called routine can't make those guarantees and so you have to replace the assertion with a "real" check (one that will make it to the release code) (see number 2 above). What does the assertion buy you that testing or a real check does not?

5. It's one more place that release code can go bad. Suppose the assertion somehow makes it into release code (low probability, but possible). Now your release code might crash. Wouldn't it be preferable for release code to handle errors gracefully (return a NULL Pointer error rather than crashing)? That brings us back to #2 above, but it's also about writing safe code. If there's code that is not supposed to be in the release version, don't put it into the source file.

Those are reasons to simply avoid assertions. Think of it this way, the assertion is a tool that can be used, but it comes with some drawbacks. Do the pluses outweigh the minuses? In my opinion they do not. It's a tool that can be replaced with other tools. Because there are alternatives and those alternatives don't come with the same problems assertions have, use the other tools.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00e55375ef1c88330120a7a3371e970b

Listed below are links to weblogs that reference Why I Don't Like Assertions:

Comments

Mark

I agree that checks should not be removed when you release code.

You make a distinction between an assertion and a real check. I suggest that there is a distinction, but not the one you think.

Some classes of error are infrequent or unlikely but anticipated, such as running out of disc space. Assertions are subtly different in that they represent errors that cannot occur unless the programmer has made a mistake.

A proper error check should explain the error then recover gracefully. However a failed assertion presents evidence that the program cannot be in a state that the programmer expected. It is probably best to stop that program as soon as possible so as to preserve evidence that can be used to diagnose the error.

I would have some sympathy if you had said that you would like the assert() macro to require a more meaningful error message

assert(arg2 != NULL, "Subroutine() requires a string but was passed a NULL pointer");

However, even this can be problematic. Imagine you are the programmer responsible for the I/O library and want to report a memory leak in the code that handles stderr.

Post a comment

If you have a TypeKey or TypePad account, please Sign In.

Voltage Data Breach Index

  • Grab the Voltage Data Breach Index

February 2012

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29