The Right Approach to Exceptions
Jonathan M Davis
jmdavisProg at gmx.com
Sat Feb 18 16:03:04 PST 2012
On Saturday, February 18, 2012 17:30:10 Andrei Alexandrescu wrote:
> On 2/18/12 5:20 PM, Jonathan M Davis wrote:
> > On Saturday, February 18, 2012 17:13:16 Andrei Alexandrescu wrote:
> >> On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
> >> GetOptException
> >> FlagArgumentMissingException
> >> InvalidFlagArgumentException
> >> UnknownFlagException
> >> FileException
> >> FileNotFoundException
> >> NotFileException
> >> NotDirException
> >> AccessDeniedException
> >> I died inside a little.
> > If you actually want to _handle_ exceptions, how else do you expect to do
> > it? Simply put a code of some kind on the exceptions and then have switch
> > statement to handle them?
> The alternative is with virtuals. Do you see a lot of virtuals in base
> exceptions? Do you see dramatically different interface for different
> exception types?
You mean virtual functions? The problem is that each exception type could have
information specific to _it_ which makes no sense in a base class. For
instance, Exception does to have errno in it. FileException does.
If we have GetOptException, it should have a variable for which flag failed.
Exception doesn't have that. And what if the flag failed because it was given a
bad argument? Then the exception needs a field for that argument. Then you can
get something like
stderr.writefln("%s is missing an argument", mae.flag);
stderr.writelfln("%s is not a valid argument for %s. You must give it a
%s.", mae.arg, mae.flag, mae.expectedType);
stderr.writefln("%s is not a known flag.", ufe.ufe);
stderr.writefln("There was an error with %s", goe.flag);
//A delegate that you passed to getopt threw an exception.
stderr.writeln("An unexpected error occured.");
You can't do _anything_ like that right now. And none of that makes sense as
virtual functions. And yes, in this case, you're still printing out in every
case rather than doing more dynamic handling, but you get much better error
messages here, and it may be that a program would want to do something fancier
than what I'm doing here (particularly if you're using delegates with getopt).
And of course other exception types lend themselves better to doing extra
handling rather than printing messages (e.g. parsing exceptions or file
handling exceptions might make it so that the program tries again or tries
something else, and the user doesn't get involved at all).
And even if all of the exception types that you were dealing with were
identical save for their type, they could still be very useful. Knowing that
you got an IOException when handling a stream rather than a UTFException gives
you some idea of what's wrong even if the exception types aren't giving much
in the way of additional useful information (though hopefully, they would).
In order to actually handle exceptions rather than just print out messages (or
to print out more useful messages like in the case of getopt), you need to
have exceptions with types of _some_ kind so that code can know what it's
dealing with. Messages are really only of any use for humans.
So, you either end up with an exception with a field indicating its type (which
really doesn't scale very well - particularly when you don't control all of
the code you're dealing with and someone could screw up and reuse the same
value for as an existing type) which you then switch on, or you have the
exceptions in an inheritance hierarchy to indicate their type, which works
much better for giving additional information, because then that information
can be in fields in the derived exception type rather than the equivalent of a
void* in a field in Exception which then must be cast appropriately based on
the type field.
- Jonathan M Davis
More information about the Digitalmars-d