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

try
    getopt(args, ...)
catch(MissingArgumentException mae)
{
    stderr.writefln("%s is missing an argument", mae.flag);
    return -1;
}
catch(InvalidArgumentException iae)
{
    stderr.writelfln("%s is not a valid argument for %s. You must give it a 
%s.", mae.arg, mae.flag, mae.expectedType);
    return -1;
}
catch(UnknownFlagException ufe)
{
    stderr.writefln("%s is not a known flag.", ufe.ufe);
    return -1;
}
catch(GetOptException goe)
{
    stderr.writefln("There was an error with %s",  goe.flag);
    return -1;
}
//A delegate that you passed to getopt threw an exception.
catch(YourException ye)
{
    //...
}
catch(Exception e)
{
    stderr.writeln("An unexpected error occured.");
    return -1;
}

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 mailing list