tags:

views:

145

answers:

9

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?

+13  A: 

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.

+1 you are right sir.
frast
Note my answer doesn't *explicitly* address the notion of whether or not assertions should be included in production releases (they shouldn't), as it is beyond the scope of the question.
A: 

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

Ignacio Vazquez-Abrams
A: 

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.

Mark Ransom
So do I need to remove the assertions in release mode if they are thoroughly tested? Or just leave them there in case of future change by another developer who maintains the code?
Eric
@Eric, usually you use an `assert` macro that automatically gets compiled to an empty block of code when you build it for release. Then there's no reason to ever get rid of them, they'll be useful to anybody who modifies the code. They act as documentation too.
Mark Ransom
A: 

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.

OmnipotentEntity
A: 

BOOST_VERIFY in http://www.boost.org/doc/libs/1_43_0/libs/utility/assert.html

aaa
+2  A: 

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).

EboMike
Up. I agree with your idea that sometimes crashing a program with good UI notification is better than letting it run in bad state without notice of it.
Eric
@Eric: If you can determine the program is in a "bad state", you can handle it, accordingly (*e.g.* with a UI notification). An *assert* is __not__ required for this case, as throwing an exception or including error-handling code will do just fine. Moreover, if you don't 100% believe that it is *impossible* for the program to be in such a state, *assert* is not the correct construct--you should, again, just test the state and either throw an exception or handle the error. *Assert* doesn't say "I *hope* this is true", it says, "I *assert* this is (necessarily) true".
You can detect a bad state, but you can't necessarily recover from it. If a variable has a value it shouldn't possibly have, how do you recover from that? Guess the right value? It's an indication that something else might be completely broken that lead to this variable being bad. Maybe even trashed memory. You cannot recover from that. Also, for performance reasons, you don't want to spend TOO much time error-checking in a release build. (My requirements are probably different than yours, I'm mostly doing games - every branch counts in some core functions there.)
EboMike
@user359996: If assert won't happen 100%, which benefit of keeping it there in release build?
Eric
@EboMike: I agree with most of your points, but I'm not 100% sure what position you're advocating. Are you suggesting leaving assertions in production code or not?
@user359996: I mean if we are 100% sure that asserts won't actually happen in release build, can we just remove them before release?
Eric
@Eric: Excellent question. Most (all?) compilers have a setting (typically enabled, by default) to disable assertion logic for production releases. That is, you don't need to explicitly remove them from the source code.
@Eric: And to be perfectly clear, I do indeed recommend disabling assertions for production releases.
@user359996: A lesson learned is I should always use debug version of binaries in dev phase which I almost forget to do before, otherwise, the assert just lose its usage...
Eric
@user\d+: I think one thing we can agree on that asserts have to be compiled out in production builds (most APIs are already set up for that). I don't mind quick NULL if checks for sanity if something being NULL doesn't have consequences, but other than that, I would eat the exception and have a global uncaught exception handler that brings up a GUI.
EboMike
A: 

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.

H. den Breejen
+1  A: 

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.

JP19
A: 

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].

JP19