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