The Right Approach to Exceptions

Jim Hewes 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.
>
> [snip]
>

I imagine that you would disagree with the design of the .NET Framework 
then, which uses ArgumentException all over.
http://msdn.microsoft.com/en-us/library/system.argumentexception.aspx

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 
they 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
> basis.

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 
thread...

Error
  +--Exception
      +--GetOptException (bad name, I prefer CommandLineException)
      +--FileException
      +--IOException (OK, I added this branch, just to show the idea)
          +--ReadErrorException
          +--WriteErrorException

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?

Jim


More information about the Digitalmars-d mailing list