The Right Approach to Exceptions

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Mon Feb 20 10:45:57 PST 2012


On 2/20/12 12:23 PM, Jonathan M Davis wrote:
> 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.

Please hear me out.

> 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?

It is better because you get to factor out all formatting in one place. 
You combine a string template that has references to symbolic names, 
with a context giving the symbolic names. Otherwise the code doing so 
would need to duplicate that code all over the place - exactly as you 
did in your example.

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

No, it's not throwing away anything. If you want to handle the actual 
exception, sure, you get typed access to the state. All I want is push 
one interface method up.

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

No, it doesn't, and if you just hear me out for a second you'll see how. 
It's very simple. Formatting is all about pairing symbolic names with 
data. A format engine would load a string template that uses symbolic 
name such as "file", "line", "IP", "port", and a context which contains 
bindings of these names to values. That way formatting does not need to 
know what an IP means etc.

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

Sure. Again, this is not advocating replacement of exception hierarchies 
with tables!

> Also, much of the time, the application is going to want to print its own
> error messages, not what library that its calling used.

Sure. Then the application has its own string templates. That was the 
purpose of the whole thing. Exceptions give bindings, applications give 
formatting templates.

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

Yup. Yup.

> And moving any messages that exceptions have out of the exception itself is
> _bad_ for debugging.

Sure. Print typeid(e) if you so want, and handle explicit exception 
specifically as needed.

> The exception should contain its own information, not
> have it externalized for internationalization purposes.

And that information should be accessible in a uniform manner.

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

Code like that is present in very many catch blocks.

> It shows one way that the fields in the exception could be
> useful.

To me it shows how code can be duplicated without necessity.

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

You may want to give another one.


Andrei



More information about the Digitalmars-d mailing list