Question

Assertion is used to check whether a condition is met(precondition, postcondition, invariants) and help programmers find holes during debugging phase.

For example,

void f(int *p)
{
  assert(p);
  p->do();
}

My question is do we need to assume the condition could not be met in release mode and handle the case accordingly?

void f(int *p)
{
  assert(p);

  if (p)
  {
    p->do();
  }
}

After all, assertion means that the condition it tests should NEVER be false. But if, if we don't check it and it fails, program crashes. Sounds like a dilemma. How do you guys deal with it?

Was it helpful?

Solution

If the assertion fails, the program should crash.

An assertion failing means the programmer made a fundamental mistake in their understanding of how it is possible for the program flow to proceed. This is a development aid, not a production aid. In production, one might handle exceptions, as they "might" occur, whereas assertions should "never" fail.

If you're in the camp that says, "Oh, but what if assertions fail in production? I need to catch them!" then you're missing the point. Ask yourself, in such a case, why aren't you just throwing an exception (or otherwise handling the error)?

Generally speaking, assert is not just a shorthand for "if condition not met, throw exception" (well, sometimes that's the operational semantics, but it's not the denotational semantics). Rather, an assertion failing means the application is in a state the developer does not believe is even possible. Do you really want the code to continue executing in such a case? Clearly (I would say), No.

OTHER TIPS

Defensive programming is always best. You should always assume that despite all your testing, your application will ship with bugs. As such, it is in your best interests to add NULL checks in situations where you can avoid a NULL pointer deference and simply move on.

However, there are situations where there is simply no easy way to avoid a crash, and in those cases, the assert is your only way of detecting the problem during your development cycle.

One important point though - asserts are also often used to detect major problems with the integrity of your data. If you continue past those asserts, you might risk corrupting data. In those cases, it may be better to crash rather than destroying your data. (Obviously, any sort of crash handler that at least brings up a reasonable UI with an error description would be preferable).

Strictly speaking, the second code has redundancy.

void f(int *p)
{
  assert(p);
  if (p)    // Beats the purpose of assertion
  {
    p->do();
  }
}

Assertion means error has occurred. Something which is unexpected/unhandled. In above code, either

1) You are properly handling the case where p is null. (by not calling p->do())- which supposedly is the right/expected thing to do. However, then the assertion is a false alarm.

2) On the other hand, if by not calling p->do(), something will go wrong (maybe further in the code or in the output), then the assertion is right, but there should be no point in continuing anyways.

In the above code the programmer is working extra hard to handle cases which are erroneous anyways.

That said, some people like to treat asserts as something has gone wrong, but lets see if we still get correct output. IMO, that is bad strategy and creates confusions during bug fixing.

Assertions are debugging code, not operating code. Do not use them to catch input errors.

Assertions are used to catch bugs in testing. The theory is that you've tested well enough to know that it will work once you've released it.

If there's any possibility that the condition might arise in real life operation, don't rely on assertions - use exceptions or some other error mechanism.

Asserts are useful for debugging as you mentioned. They should never make it into production code (as compiled, it's ok to wrap them in #ifdefs of course)

If you're running into an issue where it's beyond your control to rectify and you need the check in your production code I'd do something like:

void f(int *p)
{

  if (!p)
  {
    do_error("FATAL, P is null.");
  }

  p->do();
}

Where do_error is a function that logs an error and cleanly exits.

I say leave them in in à release build. There are bound to be bugs in any build. Having asserts in your product means you can pinpoint the problem more easily when you receive à problem report from à uwer.

Do not put much effort in handling the exceptions. Simply make sure you can get hold of the complete exception, includigg stacktrace. That applies to à release build in particular.

Since many people are commenting on putting assertions in release mode:

In what I work on, efficiency is very important (sometimes, execution on large data sets takes dozens of hours to few days to complete). Hence, we have special macros that assert only in debug code (run during QA etc). As an example, an assert inside a for loop is definitely an overhead and you may want to avoid it in release code. After all, if all iz well, asserts are not supposed to fail.

One example where release code asserts are good is -- if logic is not supposed to hit a particular branch of code at all. In this case, assert(0) is fine [thus any kind of assert(0) can always be left in release code].

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top