Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better alternatives to assert(false) in C/C++

Currently, I write

assert(false);

at places that my code is never supposed to reach. One example, in a very C-ish style, is:

int findzero( int length, int * array ) {
  for( int i = 0; i < length; i++ )
    if( array[i] == 0 )
      return i;
  assert(false);
}

My compiler recognizes that the program finishes once assert(false) has been reached. However, whenever I compile with -DNDEBUG for performance reasons, the last assertion vanishes and the compiler warns that the execution finishes the function without a return statement.

What are better alternatives of finishing off a program if a supposedly unreachable part of the code has been reached? The solution should

  • be recognized by the compiler and not produce warnings (like the ones above or others)
  • perhaps even allow for a custom error message.

I am explicitly interested in solutions no matter whether it's modern C++ or like 90s C.

like image 945
shuhalo Avatar asked Sep 12 '19 14:09

shuhalo


People also ask

Should I use assert in C?

The main rule of thumb: an assertion failure is always a bug in the program. Use an assert to check function parameters if you expect the caller to ensure that the argument is correct and you want to indicate that any other behavior is a bug in the caller. Dividing by zero is, IMO, a very good example.

What happens if assert fails in C?

The C language provides an <assert. h> header file and corresponding assert() macro that a programmer can use to make assertions. If an assertion fails, the assert() macro arranges to print a diagnostic message describing the condition that should have been true but was not, and then it kills the program.

Is assert good practice C++?

Assertions are entirely appropriate in C++ code. Exceptions and other error handling mechanisms aren't really intended for the same thing as assertions. Error handling is for when there's a potential for recovering or reporting an error nicely to the user.


2 Answers

I like to use

assert(!"This should never happen.");

...which can also be used with a condition, as in

assert(!vector.empty() || !"Cannot take element from empty container." );

What's nice about this is that the string shows up in the error message in case an assertion does not hold.

like image 73
Frerich Raabe Avatar answered Nov 15 '22 17:11

Frerich Raabe


Replacing your assert(false) is exactly what "unreachable" built-ins are for.

They are a semantic equivalent to your use of assert(false). In fact, VS's is spelt very similarly.

GCC/Clang/Intel:

__builtin_unreachable()

MSVS:

 __assume(false)

These have effect regardless of NDEBUG (unlike assert) or optimisation levels.

Your compiler, particularly with the above built-ins but also possibly with your assert(false), nods its head in understanding that you're promising that part of the function will never be reached. It can use this to perform some optimisations on certain code paths, and it will silence warnings about missing returns because you've already promised that it was deliberate.

The trade-off is that the statement itself has undefined behaviour (much like going forth and flowing off the end of the function was already). In some situations, you may instead wish to consider throwing an exception (or returning some "error code" value instead), or calling std::abort() (in C++) if you want to just terminate the program.


There's a proposal (P0627R0), to add this to C++ as a standard attribute.


From the GCC docs on Builtins:

If control flow reaches the point of the __builtin_unreachable, the program is undefined. It is useful in situations where the compiler cannot deduce the unreachability of the code. [..]

like image 39
Lightness Races in Orbit Avatar answered Nov 15 '22 19:11

Lightness Races in Orbit