The Right Approach to Exceptions

Jonathan M Davis jmdavisProg at gmx.com
Mon Feb 20 10:23:04 PST 2012


On Monday, February 20, 2012 11:57:07 Andrei Alexandrescu wrote:
> On 2/20/12 11:44 AM, foobar wrote:
> > This extra processing is orthogonal to the exception. the same exception
> > can be logged to a file, processed (per above example) and generate
> > graphical notification to the user, etc. The exception contains the
> > information pertaining only to what went wrong. the rest is not part of
> > this discussion.
> 
> Exactly. I don't see how a disagreement follows from here. So isn't it
> reasonable to design the exception such that it can offer information
> pertaining to what went wrong, in a uniform manner?

In most cases, that's not even vaguely possible. An exception for a socket 
would probably include an IP and port in its additonal information. An 
exception for a file operation would include the file name that it tried to 
operate on. An exception for malformed unicode would include information on 
the string and the bad character. An exception from parsing XML would give you 
information about which part of the XML had the problem and what the problem 
was. All of these situations are completely different. Using a Variant is the 
equivalent of using a void* and casting to the actual information based no the 
type of the exception or on a type code in the exception. How is that better 
than simply having an actual exception hierarchy with each of the derived 
exceptions having fields specific to their circumstances? I contend that it's 
far worse. It's throwing away type information simply in an effort to 
genericize. Genericity can be very useful, but it can also be over-applied.

I don't see how you could possibly make that uniform. It's very non-uniform by 
its very nature. The handling _needs_ to be non-uniform.

> > The exact same exception in the example would also be thrown on a
> > mistyped URL in an application that tries to scrape some info from a
> > website for further processing. The error is still the same - the url is
> > incorrect but different use cases handle it differently. In the former
> > example I might call to a i18n lib (someone already mentioned gettext)
> > while in the latter I'll call a logging library with the the mistyped
> > url (for statistics' sake).
> > in the first I use the url to find a closest match, in the second I want
> > to log said requested url. Both handled by *other* mechanisms.
> > in both cases the exception needs a url field and in both cases I have
> > no need for the Variant[string] map.
> 
> The Variant[string] map saves a lot of duplication whenever you want to
> format a human-readable string (which is a common activity with
> exceptions). It transforms this (I'm too lazy to write code anew by
> hand, so I'll paste Jonathan's):
> 
[snip]
> 
> The stringTemplate function loads the formatting template from a table
> indexed on typeid(e).toString() and formats it with the info. It's
> simple factorization.

That only works in this particular case, because it happens that the code is 
just figuring out how to translate the exception into an error message without 
trying to recover. If you want to do anything fancier (like suggest the flag 
that the user probably meant), you need those extra fields in the exceptions.

Also, much of the time, the application is going to want to print its own 
error messages, not what library that its calling used. And frequently, the 
application can give much better messages, because it knows what it's doing, 
not just the operation that it tried and failed. Exceptions are for 
_developers_, not users, and in general, exception messages should not be 
printed to users.

And moving any messages that exceptions have out of the exception itself is 
_bad_ for debugging. The exception should contain its own information, not 
have it externalized for internationalization purposes. If its in the 
exception itself, you can see it in the debugger. That doesn't work anywhere 
near as well when you externalize its stuff.

I gave the getopt example, because we were talking about getopt, and it was 
easy to write. It shows one way that the fields in the exception could be 
useful. But it's an abnormality in that it's whole purpose is to allow you to 
figure out how to best give an error message to the user. Many, many other 
exceptions need to be processed by the program without necessarily informing 
the user of anything. So, getopt was probably a bad example. It just was an 
easy one to give.

- Jonathan M Davis


More information about the Digitalmars-d mailing list