I've recently started adding some asserts to my code, and this is how I've been doing it:
I mentally divide my code into boundary code and internal code. Boundary code is code that handles user input, reads files, and gets data from the network. In this code, I request input in a loop that only exits when input is valid (in the case of interactive user input), or throw exceptions in the case of unrecoverable file / network corrupt data.
Internal code is everything else. For instance, a function that sets a variable in my class might be defined as
void Class::f (int value) {
assert (value < end);
member = value;
}
and a function that gets input from a network might read as such:
void Class::g (InMessage & msg) {
int const value = msg.read_int();
if (value >= end)
throw InvalidServerData();
f (value);
}
This gives me two layers of checks. Anything where the data is determined at run-time always gets an exception or immediate error handling. However, that extra check in Class::f
with the assert
statement means that if some internal code ever calls Class::f
, I still have a sanity check. My internal code might not pass a valid argument (because I may have calculated value
from some complex series of functions), so I like having the assertion in the setting function to document that regardless of who is calling the function, value
must not be greater than or equal to end
.
This seems to fit into what I'm reading in a few places, that assertions should be impossible to violate in a well-functioning program, while exceptions should be for exceptional and erroneous cases that are still possible. Because in theory I'm validating all input, it should not be possible for my assertion to be triggered. If it is, my program is wrong.