Where should assert() be used in C resp. C++? Where should assert() be used in C resp. C++? c c

Where should assert() be used in C resp. C++?


Context: I write server software for a living, the kind that stays up for weeks before the next version is loaded. So my answers may be biaised toward highly defensive code.

The principle.

Before we delve into the specifics of where to use assert, it's important to understand the principle behind it.

assert is an essential tool in Defensive Programming. It helps validating assumptions (assert them actually) and thus catch programming errors (to be distinguished from user errors). The goal of assert is to detect erroneous situations, from which recovery is generally not immediately possible.

Example:

char const* strstr(char const* haystack, char const* needle) {  assert(haystack); assert(needle);  // ...}

Alternatives.

In C ? There is little alternative. Unless your function has been designed to be able to pass an error code or return a sentinel value, and this is duly documented.

In C++, exceptions are a perfectly acceptable alternative. However, an assert can help produce a memory dump so that you can see exactly what state the program is in at the moment the erroneous situation is detected (which helps debugging), while an exception will unwind the stack and thus lose the context (oups...).

Also, an exception might (unfortunately) get caught by a high level handler (or an unsavory catch from a fellow developer (you would not do that, of course)), in which case you could miss completely the error until it's too late.

Where NOT to use it.

First, it should be understood that assert is only ever useful in Debug code. In Release, NDEBUG is defined and no code is generated. As a corollary, in Release assert has the same worth as a comment.

  • Never use it for checks that are necessary to the good behavior of the software. Error conditions should be checked and dealt with. Always.

Second, it should be understood that malformed input is part of your life. Would you want your compiler display an assert message each time you make an error ? Hum! Therefore:

  • Never use it for input data validation. Input data should be validated and errors appropriately reported to the user. Always.

Third, it should be understood that crashes are not appreciated. It is expected of your program that it will run smoothly. Therefore, one should not get tempted to leave asserts on in Release mode: Release code ends up in the end user hands and should never crash, ever. At worst, it should shutdown while displaying an error message. It is expected that no user data is lost during this process, and even better if upon restarting the user is taken back to where she was: that is what modern browsers do, for example.

  • Never leave asserts on in Release.

Note: for server code, upon "hitting" an assertion, we manage to get back in position for treating the next query in most cases.

Where to use it.

assert is on in Debug mode, and so should be used for Debugging. Whenever you test new code, whenever your test suite run, whenever software is in your (or your teammates) hands, whenever software is in you QA department hands. Asserts let you spot errors and gives you the full context of the error so that you can repair.

  • Use it during the development and testing cycles.

Even better. Since you know code will not be executed in Release you can afford to perform expensive checks.

Note: you should also test the Release binary, if only to check the performance.

And in Release ?

Well, in the codebase I work on, we replace the inexpensive asserts (the others are ignored) by specific exceptions that are only caught by a high level handler that will log the issue (with backtrace), return a pre-encoded error response and resume the service. The development team is notified automatically.

In software that is deployed, the best practices I have seen imply to create a memory dump and stream it back to the developers for analysis while attempting not to lose any user data and behave as courteously as possible toward the unfortunate user. I feel really blessed to be working server-side when I contemplate the difficulty of this task ;)


I'm gonna throw out my view of assert(). I can find what assert() does elsewhere, but stackoverflow provides a good forum for suggestions on how and when to use it.

Both assert and static_assert serve similar functions. Let's say you have some function foo. For example, lets say you have a function foo(void*) that assumes its argument is not null:

void foo(void* p) {  assert(p);  ...}

Your function has a couple people that care about it.

First, the developer who calls your function. He might just look at your documentation and maybe he will miss the part about not allowing a null pointer as the argument. He may not ever read the code for the function, but when he runs it in debug mode the assert may catch his inappropriate usage of your function (especially if his test cases are good).

Second (and more important), is the developer who reads your code. To him, your assert says that after this line, p is not null. This is something that is sometimes overlooked, but I believe is the most useful feature of the assert macro. It documents and enforces conditions.

You should use asserts to encode this information whenever it is practical. I like to think of it as saying "at this point in the code, this is true" (and it says this in a way so much stronger than a comment would). Of course, if such a statement doesn't actually convey much/any information then it isn't needed.


I think there's a simple and powerful point to be made:

assert () is for checking internal consistency.

Use it to check preconditions, postconditions, and invariants.

When there may be inconsistency due to external factors, circumstances which the code can't control locally, then throw an exception. Exceptions are for when postconditions cannot be satisfied given the preconditions. Good examples:

  • new int is ok up to its preconditions, so if memory is unavailable, throwing is the only reasonable response. (The postcondition of malloc is "a valid pointer or NULL")
  • The postcondition of a constructor is the existence of an object whose invariants are established. If it can't construct a valid state, throwing is the only reasonable response.

assert should not be used for the above. By contrast,

void sort (int * begin, int * end) {    // assert (begin <= end); // OPTIONAL precondition, possibly want to throw    for (int * i = begin, i < end; ++i) {        assert (is_sorted (begin, i)); // invariant        // insert *i into sorted position ...    }}

Checking is_sorted is checking that the algorithm is behaving correctly given its preconditions. An exception is not a reasonable response.

To cut a long story short: assert is for things which WILL NEVER happen IF the program is LOCALLY correct, exceptions are for things which can go wrong even when the code is correct.

Whether or not invalid inputs trigger exceptions or not is a matter of style.