I am just wondering when assert() is a better choice to use over simple error handling mechanisms. It seems like handling the error myself would be preferable to just shutting down the program. It would allow for the user/programmer to get some idea as to what happened rather than a vague error message.
However, there has to be a reason otherwise it wouldn't be in here, so I'm looking to be enlightened. :)
First of all, it is debug assertion. It is not included in release build (Which defines NDEBUG macro). So they are only matter to the programmer and help him to catch errors which should not happen during normal use.
As for terminating program, it allows programmer to see stack trace to determine why this situation happened.
So it is not intendedd to be normal error handling mechanism and rather a tool to help programmer to debug application
rather than a vague error message.
Well, there is no need to make error message vague...
Assert is designed for when you want to be sure that something doesn't happen and if it does, there is no point carrying on your program because you've found a problem. It also doesn't run in release mode so the user will never be affected by it, and I'm pretty sure you can pass a string to assert as well as the condition.
C++ (and C) has a well-established tradition of narrow contracts: functions have preconditions and leave the behavior undefined if those preconditions are violated. The prototypical example is std::vector::operator[], where the behavior is undefined if the index is equal or greater the vector's current size.
The opposite, wide contract, is when every possible combination of values that the function arguments can have leads to a well-defined behavior. The prototypical example of a wide-contract function is std::vector::at, which never has undefined behavior (assuming vector's class invariants are valid, of course)
The most common use of assert that I have seen is asserting your preconditions. When writing a narrow-contract function, the first lines are very often asserts, which abort the program in a debug build instead of leaving it undefined if someone calls it with incorrect arguments. There is a C++ proposal for standardization of four levels of such precondition asserts, to be executed at different levels of debugging (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4378.pdf)
What are asserted are invariants. Invariants can be of many different types; these three are common:
1. Preconditions: see Cubbi's post.
2. Postconditions: a function has indeed done what it promised to do. For instance:
1 2 3 4 5 6 7
void remove_whitespace( std::string& str )
{
// code to remove whitespace from str
// invariant: at this point, str does not contain whitespace characters
assert( std::none_of( std::begin(str), std::end(str), []( char c ) { return std::isspace(c) ; } ) ) ;
}
3. Class invariants:
The values of the members and the objects referred to by members are collectively called the state of the object (or simply, its value). A major concern of a class design is to get an object into a well defined state (initialization/construction), to maintain a well defined state as operations are performed, and finally to destroy the object gracefully. The property that makes the state of an object well-defined is called its invariant. - Stroustrup
struct my_class
{
my_class( /* ... */ ) // constructor
{
// initialise (establish the class invariant)
// canonical: assert the class invariant before returning from the constructor:
assert( is_valid() ) ; // the invariant has been established
}
void my_class_mutating_operation( /* ... */ )
{
assert( is_valid() ) ; // precondition: we start with a well-defined object
/*also*/ assert( /* preconditions for the function's arguments */ ) ;
// mutate the object
assert( is_valid() ) ; // postcondition: the object is still in a well-defined state
/*perhaps, also*/ assert( /* the function has done what it promised to do */ ) ;
}
int my_class_non_mutating_operation( /* ... */ )
{
assert( is_valid() ) ; // precondition: we have a well-defined object
return ... ;
}
private:
bool is_valid() const ; // return true if the class invariant holds
// ...
};