I realize that while the question has no language tag, it is probably implicitly talking about “coffee languages”. But just for the sake of completeness, I'd like to mention the somewhat diverging apparent consensus in the C++ world.
There are three things C++ programmers will typically be interested in:
- Will it have zero-overhead in optimized builds? (That is, can it be “compiled out”?)
- Can I use it to trap into a debugger right at the point where the error was detected?
- Can I use it to report problems from functions declared
noexcept
?
In the past, I have approached the first problem by writing code like this
int
factorial(const int n)
{
if (CHECK_ARGS)
{
if (n < 0)
throw std::invalid_argument {"n < 0"};
}
int fac = 1;
for (int i = 2; i <= n; ++i)
fac *= i;
return fac;
}
where CHECK_ARGS
is #define
d to a compile-time constant so the compiler may completely eliminate all the argument checking code in optimized builds. (I'm not saying that compiling the checks out is a good thing in general but I do believe that a user should have the option to compile them out.)
I still like about this solution that the argument checking code is clearly visible grouped together into the if
. However, the second and third issue are not solved by this. Therefore, I'm now leaning again more towards using an assert
macro for argument checking.
The Boost coding standards agree with this:
What About Programmer Errors?
As a developer, if I have violated a precondition of a library I'm using, I don't want stack unwinding. What I want is a core dump or the equivalent - a way to inspect the state of the program at the exact point where the problem was detected. That usually means assert()
or something like it.
There was a very interesting talk given by John Lakos at CppCon'14 titled Defensive Programming Done Right (part 1, part 2). In the first part of his talk, he discusses the theory of contracts and undefined behavior. In the second part, he presents what I consider a very good proposal for systematic argument checking. In essence, he proposes assertion macros that allow the user to select how much of a budget (in terms of CPU utilization) she is willing to donate to the library for argument checking and has the library make wise use of that budget. As an addition, the user can also install a global error handling function that will be called in case a broken contract is detected.
Regarding the aspect that a function is private, I don't think that this means we should never have it check its arguments. We might trust our own code more to not violate an internal function's contract but we also know that we aren't perfect either. Argument checking in internal functions is just as helpful in detecting our own bugs as it is in public functions for detecting bugs in client code.