What are the advantages or implications of using assert vs exit or vice versa?

Viewed 720

As far as I understand, a call to assert(e), where e is a boolean expression, executes something roughly like if (!e) { printf("%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, e); abort(); } which terminates the program abruptly if the given expression is not true.

On the other hand, instead of using assert, I could probably write something like if (!e) { fprintf(stderr, "custom error message.\n"); exit(1); } which naively feels like a more cleaner, nicer thing to do.

Ignoring that assert can be turned off globally using the NDEBUG flag, what other advantage do you think one has over the other? Have I got the distinction right, or are there conceptual differences between the two which I am unaware of that let the two ways of conditional program termination have their own niche use cases? If the latter, please explain. Thank you.

3 Answers

The biggest advantage of assert is that it makes one's intentions clear. If you see assert(some_condition) then you know what the author's intent was (i.e., some_condition is always true). With your inlined version, I can't assume intention until I actually read your if block, and realize you're going to display an error message and terminate the program.

Less important reasons include that assert reduces copy/paste errors, some_condition is turned into a string automatically (including preservation of variable names), and that tooling can understand it.

what other advantage do you think one has over the other?

A macro is used because you want to be able to remove it via conditional compilation. In other words, you don't want the code to even appear in the binary.

Have I got the distinction right, or are there conceptual differences between the two which I am unaware of that let the two ways of conditional program termination have their own niche use cases?

Well, exit() and abort() don't behave the same way even if you use 1 as a "unsuccessful" exit code. The latter is intended to kill the program right away without further work and possibly trigger a debugging prompt or save an image of the process space (although exactly what it does depends on the vendor providing it). The former calls the registered functions by atexit(). There are other ways of stopping, too, see quick_exit() and _Exit().

For C++, there are way more considerations on the behavioral difference, e.g. whether destructors for variables in the stack frame(s) are run, whether global destructors are run, what happens if an exception is thrown while doing that, etc.

'assert' is a code autotesting tool. Sometimes the program should not stop its work with on client side (release version) even if the condition leading to the execution of the assert was met. For example:

   switch(color)
   {
      case red:
        //...
      break;
      
      case green:
        //...
      break;
      
      default:
        assert(false && "unexpected color value. 'enum color' was modified?");
   }

another example:

   if ( OkOrFailEnum::OK != result )
   {
      assert(false && "data access fail");
      throw std::runtime_error("Can not get index " + std::to_string(index));
   }    

At the same time, 'assert' is a code commenting tool.

   inline unsigned Sum(unsigned* pPos, unsigned* pOffset)
   {
     assert(nullptr != pPos);                             // \
     assert(nullptr != pOffset);                          // | preconditions
     assert(*pPos + *pOffset < *pOffset && "Overflow?");  // /
     
     return *pPos + *pOffset;
   }

The assert:

 assert(*pPos + *pOffset < *pOffset && "Overflow?");

means, that the Sum(..) function does not works correctly with big sums and it have to do some check before call the function.

Related