Sean Kelly wrote:
Andrei Alexandrescu Wrote:
Consider:
void fun() { try { throw new Exception("a"); } finally { throw new
Exception("b"); } }
Currently this function would unceremoniously terminate the
program. I think it shouldn't.
Have you tried it? I think the call to terminate() is commented out.
It was like this before I ever started mucking with the runtime many
moons ago. What I think currently happens is that the exception in
the finally clause will replace the one being thrown. That or it's
discarded, I really can't remember which. Either way, the current
behavior is a bit weird.
Guilty as charged. Haven't tried. I don't have D installed at work, but
I tried a Java example and indeed it looks like the last exception
thrown just takes over.
class Test {
public static void main(String[] args) {
System.out.println("Hello World!");
try {
(new Test()).fun();
} catch (Exception e) {
System.out.print(e);
System.out.print(e.getCause());
System.out.print("\n");
e.printStackTrace();
}
}
void fun() throws Exception {
try {
throw new Exception("a");
} finally {
throw new Exception("b");
}
}
}
Even more interestingly, calling printStackTrace() does not acknowledge
the originating exception. Calling getCause() returns null. So
essentially at the catch point I'm not sure there's a way to get to the
"a" exception.
What should happen is that the "a" exception should be propagated
unabated, and the "b" exception should be appended to it. The
Exception class should have a property "next" that returns a
reference to the next exception thrown (in this case "b"),
effectively establishing an arbitrarily long singly-linked list of
exceptions.
The Exception class already has a "next" property, but I've always
seen this as a way to nest exceptions. For example:
try { throw new Exception; } catch( Exception e ) { throw new
MyException( e ); }
So a network API might throw a NetworkException that references a
SocketException with more detailed info about the exact problem, etc.
The reason I'm unsure about chaining related exceptions as opposed to
use chaining as a means of repackaging exceptions is that the catch
handler that ultimately executes will be the one that matches the
first exception in the chain, not the first that matches any
exception in the chain. I haven't thought about this too carefully,
but it seems like it might be difficult to write correct code with
this model.
A friend told me that that's what Java does, with the difference
that the last exception thrown takes over, so the chain comes
reversed. I strongly believe "a" is the main exception and "b" is a
contingent exception, so we shouldn't do what Java does. But Java
must have some good reason to go the other way.
When a dozen holes appear in a dike, I'm not sure it matters which
one you try to plug first :-) Unless there's some way to start with
the biggest one, I suppose. Seems like with the suggested model, the
correct approach may be to always catch Exception and walk the whole
chain to figure out what to do. But that sounds awfully close to
C-style error handling.
Well I'm not sure about the metaphor. What I can tell from my code is
that exceptions are often contingent one upon another (not parallel and
independent). A typical example: writing to a file fails, but then
closing it also fails and possibly attempting to remove the partial file
off disk also fails. In that case, the important message is that the
file couldn't be written to; the rest is aftermath. If the doctor says
"You have a liver problem, which causes your nails to have a distinctive
shape" you don't mind the nails as much as the liver.
There's also the opposite flow, for example an exception in some
validation prevents a database update. But I don't think code regularly
does essential work in destructors, finally blocks, and scope
statements. The essential work is done on the straight path, and the
important error is happening on the straight path. In fact, what I just
wrote tilted me a bit more in favor of the "master exception +
contingent camarilla" model. That model also suggests that most of the
time you only need to look at the top exception thrown to figure out the
root of the problem; talking to the camarilla is optional.
Andrei