Welcome to my website. Here you can learn about me and the stuff I work on. Currently, I'm writing some C++ tutorials, since honestly, there's so much bad teaching material and teachers out there on the Interwebs that I need to counteract the effect. I also write some articles.
One of the common flaws of software is bad error-handling. We're all used to using exceptions. But there are some common places where they are not used, but should be.
The principle issue here is that there are only three ways to handle an error. The first is a return code. The second is an exception. The third is an error-handling function, CPS style. Unfortunately, people have been increasingly shunning exceptions. Whilst I'm all in favour of flexibility, the complete and utter unusability of return codes makes using them a non-starter.
string a = ...;
int val;
if (Int32.TryParse(a, out val)) {
// Success!
} else {
// Fail!
}
This seems all well and good- I mean, the error is clearly signalled, there's no exceptions, it's all good. But actually, the approach is really rather deficient. If the parse fails, val is still in scope and could be used by anybody erroneously. Secondly, there's no means here by which to make people actually check the return value. Experience from C, where this was basically the only error-handling strategy used, suggests that in fact, many, many calls to functions which return error codes are unchecked, because every call involves duplicating the same error-handling code. In fact, return codes are so bad, it's questionable as to whether a gratuitous exception is the worse choice, even with all it's downsides.
But really, we need to head to the core of this particular matter. Namely, if Parse() fails, what are you going to do? There are two ways in which to handle an error. The first is to abort the operation, because it cannot be continued. The second is to change things so that the operation can continue- probably, for example, a default value. After all, either the user's operation can continue, or it can't. When we're aborting the operation, an exception is a fine choice. It propagates for free, and automatic cleanup is involved. When not, then perhaps a default value would be fine, or some other operation-specific actions. But, since the user declared an integer "val", if the function is to continue, that val must contain a useful value.
The key is that the caller, must make this choice- for every operation that could fail. TryParse allows you to make a non-choice, and that's bad. The easiest way to force the user to make a choice is to take a function object which, when invoked, executes the action of their choice. If they don't pass one, then semantic analysis of their code fails and their program will not compile. You could, as a reasonable "default", throw an exception. But, if you're writing Parse(), then you don't know what the user's operation is, so there's no way you can possibly provide any default code that makes it safe for that operation to continue. The guidelines are: