Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I assert() without using abort()?

If I use assert() and the assertion fails then assert() will call abort(), ending the running program abruptly. I can't afford that in my production code. Is there a way to assert in runtime yet be able to catch failed assertions so I have the chance to handle them gracefully?

like image 649
wilhelmtell Avatar asked Sep 01 '08 05:09

wilhelmtell


People also ask

How do you properly use assert?

To properly use assertions as a debugging tool, you shouldn't use try … except blocks that catch and handle AssertionError exceptions. If an assertion fails, then your program should crash because a condition that was supposed to be true became false.

Does assert terminate the program?

If expression is false (i.e., compares equal to zero), assert() prints an error message to standard error and terminates the program by calling abort(3).

What is the difference between assert () and Static_assert ()?

static_assert is meant to make compilation fail with the specified message, while traditional assert is meant to end the execution of your program.

How does assert function work?

The assert() function tests the condition parameter. If it is false, it prints a message to standard error, using the string parameter to describe the failed condition. It then sets the variable _assert_exit to one and executes the exit statement. The exit statement jumps to the END rule.


2 Answers

Yes, as a matter of fact there is. You will need to write a custom assert function yourself, as C++'s assert() is exactly C's assert(), with the abort() "feature" bundled in. Fortunately, this is surprisingly straightforward.

Assert.hh

template <typename X, typename A> inline void Assert(A assertion) {     if( !assertion ) throw X(); } 

The above function will throw an exception if a predicate doesn't hold. You will then have the chance to catch the exception. If you don't catch the exception, terminate() will be called, which will end the program similarly to abort().

You may wonder what about optimizing away the assertion when we're building for production. In this case, you can define constants that will signify that you're building for production and then refer to the constant when you Assert().

debug.hh

#ifdef NDEBUG     const bool CHECK_WRONG = false; #else     const bool CHECK_WRONG = true; #endif 

main.cc

#include<iostream>  struct Wrong { };  int main() {     try {         Assert<Wrong>(!CHECK_WRONG || 2 + 2 == 5);         std::cout << "I can go to sleep now.\n";     }     catch( Wrong e ) {         std::cerr << "Someone is wrong on the internet!\n";     }      return 0; } 

If CHECK_WRONG is a constant then the call to Assert() will be compiled away in production, even if the assertion is not a constant expression. There is a slight disadvantage in that by referring to CHECK_WRONG we type a little more. But in exchange we gain an advantage in that we can classify various groups of assertions and enable and disable each of them as we see fit. So, for example we could define a group of assertions that we want enabled even in production code, and then define a group of assertions that we only want to see in development builds.

The Assert() function is equivalent to typing

if( !assertion ) throw X(); 

but it clearly indicates the intent of the programmer: make an assertion. Assertions are also easier to grep for with this approach, just like plain assert()s.

For more details on this technique see Bjarne Stroustrup's The C++ Programming Language 3e, section 24.3.7.2.

like image 62
wilhelmtell Avatar answered Sep 27 '22 22:09

wilhelmtell


glib's error reporting functions take the approach of continuing after an assert. glib is the underlying platform independence library that Gnome (via GTK) uses. Here's a macro that checks a precondition and prints a stack trace if the precondition fails.

#define RETURN_IF_FAIL(expr)      do {                  \  if (!(expr))                                           \  {                                                      \          fprintf(stderr,                                \                 "file %s: line %d (%s): precondition `%s' failed.", \                 __FILE__,                                           \                 __LINE__,                                           \                 __PRETTY_FUNCTION__,                                \                 #expr);                                             \          print_stack_trace(2);                                      \          return;                                                    \  };               } while(0) #define RETURN_VAL_IF_FAIL(expr, val)  do {                         \  if (!(expr))                                                       \  {                                                                  \         fprintf(stderr,                                             \                 "file %s: line %d (%s): precondition `%s' failed.",     \                 __FILE__,                                               \                 __LINE__,                                               \                 __PRETTY_FUNCTION__,                                    \                 #expr);                                                 \          print_stack_trace(2);                                          \          return val;                                                    \  };               } while(0) 

Here's the function that prints the stack trace, written for an environment that uses the gnu toolchain (gcc):

void print_stack_trace(int fd) {     void *array[256];     size_t size;      size = backtrace (array, 256);     backtrace_symbols_fd(array, size, fd); } 

This is how you'd use the macros:

char *doSomething(char *ptr) {     RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.      if( ptr != NULL )        // Necessary if you want to define the macro only for debug builds     {        ...     }      return ptr; }  void doSomethingElse(char *ptr) {     RETURN_IF_FAIL(ptr != NULL); } 
like image 25
indiv Avatar answered Sep 27 '22 22:09

indiv