The Right Approach to Exceptions
jimhewes at gmail.com
Sun Feb 19 10:41:13 PST 2012
On 2/18/2012 10:21 PM, Robert Jacques wrote:
> Not to jump on you in particular :) but bad user parameters should never
> be treated as exceptional. Even bad 'internal' parameters that are
> passed via the external API aren't exceptional. Programmers being lazy
> about input parameter checking is how hackers make their money.
I imagine that you would disagree with the design of the .NET Framework
then, which uses ArgumentException all over.
There's also a similar exception defined in the C++ Std Lib.
So what do you do when a bad parameter is passed? Return an error code
in a return value? So you use both exception handling and return codes?
How about bad parameters to constructors (in C++)?
You may say that bad parameters are not “exceptional”. OK. But my whole
point of that paragraph there was that if you're going to declare one
particular error is “exceptional” and another is not in a seemingly
arbitrary way, then you need to tell us, for all errors, which are
“exceptional” and which are not. You need a more strict definition of
“exceptional” so that people know when to use an exception and when not
to. Then in the cases they are not supposed to use an exception, what do
> Yes, the USB stack has a high-level layer that can recover from a
> connection loss, but the rest of the protocol stack above and below it
> can't understand it and presumably ignore it. And you have just
> introduced a fairly long range dependency between the lowest level of
> your code and middle layer. (To say nothing of the implicit dependences
> implied with the intervening layers). This might be the best/only
> solution to the problem, but it also makes the code base larger and more
> fragile. I wasn't speaking in absolutes, but in relative difficulties.
I guess I don't understand what you're saying here. It sounds like
you're arguing against exceptions in general, which I'm sure you're not.
My stack *does* know what a connection loss is at any level. Just like
it would know what an out-of-memory error is at any level. It just
doesn't want to deal with it everywhere. By not handling this exception
in the intermediate layers and rather just making them exception safe,
the code is smaller not larger. Even at the topmost level it's known
what a connection loss is, and the corrective action is to close the
interface and reopen when the device is plugged in again.
> Traditional error codes are enums and so what your describe as
> BadParameter is an exception wrapping an error code. I really hope no
> one has been proposing to map error code values to exceptions on a 1:1
Maybe not 1:1, but you talked about the need to have a large number of
final classes. That's what I was thinking of.
> But your making many of my points; exceptions are (better,
> faster, stronger) error codes.
Yes. But not just that. Exceptions allow you to *not* write error
handling code everywhere so that the normal program flow is easier to
read. This is assuming you're writing exception safe code.
> My issue regarding the weak 'is a'
> relationship stems from the extra information in any given final typed
> exception being very specific to that particular exception. So its hard
> for me to see the rational between treating some of these as a group
> while not all of them: if MyGroupException is providing no more
> information then Exception, why can a function recover a
> MyGroupException and not a general Exception?
Borrowing part of the the exception hierarchy from another post in this
+--GetOptException (bad name, I prefer CommandLineException)
+--IOException (OK, I added this branch, just to show the idea)
A ReadErrorException 'is a ' IOException which 'is a' Exception.At some
point or level in the code, I may want to handle IOExceptions because I
know what to do with those, but not FileExceptions because I don't know
what to do with those. If I were to catch Exception, then I would be
responsible for re-throwing FileException and any other non-IOException,
which I don't want to have to do all over.
I think a class hierarchy for exceptions is justified. As an analogue,
say that you have a GUI class hierarchy where you have a Window class
and various kinds of derived window subclasses. You will probably write
a function at some point that takes a WindowPtr as a parameter and works
on any type of window (but not any class in the whole library). A
catch() would work on a similar idea as the function handling Windows,
catching all IOExceptions but not any and all exceptions in the hierarchy.
I think it is about having a balance between the number of different
exception types and the attributes or error codes you put into them. You
may decide that there are too many different types derived from
IOException and you always end up catching them all anyway. So you can
get rid of the ReadErrorException and WriteErrorException and instead
just make these as error codes you can extract from the IOException
class. But still, IOException is different from Exception and
FileException. And I'm assuming that MyGroupException would be different
from Exception in a similar way. Maybe it's not a perfect solution, but
what is better?
More information about the Digitalmars-d