How can I assert() without using abort()?
assert, c++, exception
Solution
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.
Problem
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?