www.digitalmars.com         C & C++   DMDScript  

D - throws?

reply Russ Lewis <russ deming-os.org> writes:
Is there a reason not to implement a throws declaration?

IMHO, this is good because it enforces design decisions and interfaces.
It forces the implementer of the function to either handle the
exceptions he can generate, or publish to all his callers what he might
do.
Aug 17 2001
next sibling parent reply Chris Friesen <cfriesen nortelnetworks.com> writes:
Russ Lewis wrote:
 
 Is there a reason not to implement a throws declaration?
 
 IMHO, this is good because it enforces design decisions and interfaces.
 It forces the implementer of the function to either handle the
 exceptions he can generate, or publish to all his callers what he might
 do.

If I write a function that calls another function (etc, etc...), do I have to include all of their throw list in my throws declaration? If yes, then it becomes messy. If not, then what is the point of it since I can't tell just by looking at a function what it can potentially throw? Chris -- Chris Friesen | MailStop: 043/33/F10 Nortel Networks | work: (613) 765-0557 3500 Carling Avenue | fax: (613) 765-2986 Nepean, ON K2H 8E9 Canada | email: cfriesen nortelnetworks.com
Aug 17 2001
next sibling parent reply Russ Lewis <russ deming-os.org> writes:
Chris Friesen wrote:

 Russ Lewis wrote:
 Is there a reason not to implement a throws declaration?

 IMHO, this is good because it enforces design decisions and interfaces.
 It forces the implementer of the function to either handle the
 exceptions he can generate, or publish to all his callers what he might
 do.

If I write a function that calls another function (etc, etc...), do I have to include all of their throw list in my throws declaration? If yes, then it becomes messy. If not, then what is the point of it since I can't tell just by looking at a function what it can potentially throw?

Yes. Basically, the theory is that you should either handle the exception (catch it) or alert those who call you that you might throw it. Yes, it gets messy (declaration wise). But I think that without it, things get messy implementation wise, which, IMHO, is far worse.
Aug 17 2001
parent Russ Lewis <russ deming-os.org> writes:
Russ Lewis wrote:

 Chris Friesen wrote:

 Russ Lewis wrote:
 Is there a reason not to implement a throws declaration?

 IMHO, this is good because it enforces design decisions and interfaces.
 It forces the implementer of the function to either handle the
 exceptions he can generate, or publish to all his callers what he might
 do.

If I write a function that calls another function (etc, etc...), do I have to include all of their throw list in my throws declaration? If yes, then it becomes messy. If not, then what is the point of it since I can't tell just by looking at a function what it can potentially throw?

Yes. Basically, the theory is that you should either handle the exception (catch it) or alert those who call you that you might throw it. Yes, it gets messy (declaration wise). But I think that without it, things get messy implementation wise, which, IMHO, is far worse.

Another thought.... New exceptions tend to filter up through the call tree. That is, if you choose, late in the game, to throw a totally new type of exception, that exception has serious impacts on the other code. If they consider you a minor function and want to catch all of your errors, then when you add a new exception your are breaking their program. Thus, a small change in your library might suddenly cause total program crashes for what they consider to be minor errors. By forcing declaration, you are specifying the impact that the change will have on all of your users. Anyone who builds a new version of code using yours will get syntax errors because of your declaration change. They must then think about the best way to change it - they either allow the exception to continue to be thrown further up the tree, or they catch it. Either way, they are *forced* to at least consider your error conditions, which I consider a Very Good Thing.
Aug 17 2001
prev sibling parent reply "John Carney" <john.carney pacific.net.au> writes:
"Chris Friesen" <cfriesen nortelnetworks.com> wrote in message
news:3B7D59CE.448CFD79 nortelnetworks.com...
 Russ Lewis wrote:
 Is there a reason not to implement a throws declaration?

 IMHO, this is good because it enforces design decisions and interfaces.
 It forces the implementer of the function to either handle the
 exceptions he can generate, or publish to all his callers what he might
 do.

If I write a function that calls another function (etc, etc...), do I have

 include all of their throw list in my throws declaration?  If yes, then it
 becomes messy.  If not, then what is the point of it since I can't tell

 looking at a function what it can potentially throw?

 Chris

I totally agree with the original poster on this one. Yes, it does mean that functions must declare the exceptions thrown by functions they call. I disagree that this becomes 'messy'. 'Messy' is when lazy programmers let uncaught exceptions propagate up the call stack resulting in upper-level functions having to deal gracefully with exceptions that would have been better handled closer to the source. When I use a library function I want to know about *all* of the exceptions that it will throw. Rigid exception specification guarantees this. Cheers, John Carney.
Aug 17 2001
parent reply Christophe de Dinechin <descubes earthlink.net> writes:
John Carney wrote:

 I totally agree with the original poster on this one. Yes, it does mean that
 functions must declare the exceptions thrown by functions they call. I
 disagree that this becomes 'messy'. 'Messy' is when lazy programmers let
 uncaught exceptions propagate up the call stack resulting in upper-level
 functions having to deal gracefully with exceptions that would have been
 better handled closer to the source.

 When I use a library function I want to know about *all* of the exceptions
 that it will throw. Rigid exception specification guarantees this.

There are two philosophies of error recovery: error recovery as cleanup, and error recovery as repair. Eiffel implements the first one, C++ inconsistently implements the second one. Error recovery as cleanup means that a catch handler does not try to "keep" the exception, but simply to locally cleanup in case of error (close files, release locks, etc). In Eiffel (and LX), by default, exceptions keep propagating, as opposed to C++ where they by default are finished by a catch block. In this philosophy, a component never decides what to do with an error, but leaves it to the component user to decide. Error recovery as repair assumes that the closer to the source you catch an error, the easier it is to fix, and thus tries to prevent an error from propagating. In my opinion, it generally lends to less robust software, because errors tend to be ignored in running software. C++ is inconsistent (as I wrote in another post) in that it implements repair-style in the catch block (an exception doesn't propagate unless you explicitly rethrow), but it offers cleanup-style interfaces (the throw spec says what you throw, not what you catch). Interestingly, there is no way to really bring a throw spec and a catch totally in sync. A catch says "I won't throw that", but there is no (simple) way to say "I will throw all but that" (the complex way being "catch that and rethrow, catch dot-dot-dot and ignore", which is ugly. In short, my recommendation would be to go for an "error recovery as cleanup" model, in which case throw specs are inherently bad (you don't know what kind of errors may occur, you let the caller decide). In the case of "real" code with callbacks, virtual functions, etc, it tends to map to real-life situations better, in my experience. Christophe
Aug 18 2001
parent Dan Hursh <hursh infonet.isl.net> writes:
	What happens if you have to write bullet proof code.  (Think a
pacemaker.)  I need to know ever error a function could throw at me so I
can make sure to deal with it gracefully.  In you're suggestion you are
pretty much saying "I could throw anything at you, include exception
classes that are out of you're scope."  Writing and maintain throws()
clauses is truly a royal pain, but it does allow for a good amount
certainty that you are handling every known error condition a function
can throw at you, provided that the compiler will actually enforce the
clauses.  (Does C++ require the compiler to gripe?)  Granted it makes
template code much more difficult to write if you are forced to specify
a throws clause.
	Now what we need to design is a sure way to tell the programmer all of
the unknown errors that are in the program. ;-)

Dan

Christophe de Dinechin wrote:
 
 John Carney wrote:
 
 I totally agree with the original poster on this one. Yes, it does mean that
 functions must declare the exceptions thrown by functions they call. I
 disagree that this becomes 'messy'. 'Messy' is when lazy programmers let
 uncaught exceptions propagate up the call stack resulting in upper-level
 functions having to deal gracefully with exceptions that would have been
 better handled closer to the source.

 When I use a library function I want to know about *all* of the exceptions
 that it will throw. Rigid exception specification guarantees this.

There are two philosophies of error recovery: error recovery as cleanup, and error recovery as repair. Eiffel implements the first one, C++ inconsistently implements the second one. Error recovery as cleanup means that a catch handler does not try to "keep" the exception, but simply to locally cleanup in case of error (close files, release locks, etc). In Eiffel (and LX), by default, exceptions keep propagating, as opposed to C++ where they by default are finished by a catch block. In this philosophy, a component never decides what to do with an error, but leaves it to the component user to decide. Error recovery as repair assumes that the closer to the source you catch an error, the easier it is to fix, and thus tries to prevent an error from propagating. In my opinion, it generally lends to less robust software, because errors tend to be ignored in running software. C++ is inconsistent (as I wrote in another post) in that it implements repair-style in the catch block (an exception doesn't propagate unless you explicitly rethrow), but it offers cleanup-style interfaces (the throw spec says what you throw, not what you catch). Interestingly, there is no way to really bring a throw spec and a catch totally in sync. A catch says "I won't throw that", but there is no (simple) way to say "I will throw all but that" (the complex way being "catch that and rethrow, catch dot-dot-dot and ignore", which is ugly. In short, my recommendation would be to go for an "error recovery as cleanup" model, in which case throw specs are inherently bad (you don't know what kind of errors may occur, you let the caller decide). In the case of "real" code with callbacks, virtual functions, etc, it tends to map to real-life situations better, in my experience. Christophe

Aug 23 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
Throw declarations in C++ have a subtle problem: the throw declaration
declares the _opposite_ of what the implementation can easily guarantee.

For instance:

    int foo() throw (A) {
        try {
            bar()
        } catch(B&) {
            glop()
        }
    }

what the implementation guarantees is, roughly, that you won't throw B,
but there is no throw spec that will let you "say" that.


Christophe

Russ Lewis wrote:

 Is there a reason not to implement a throws declaration?

 IMHO, this is good because it enforces design decisions and interfaces.
 It forces the implementer of the function to either handle the
 exceptions he can generate, or publish to all his callers what he might
 do.

Aug 17 2001
parent Russ Lewis <russ deming-os.org> writes:
Christophe de Dinechin wrote:

 Throw declarations in C++ have a subtle problem: the throw declaration
 declares the _opposite_ of what the implementation can easily guarantee.

 For instance:

     int foo() throw (A) {
         try {
             bar()
         } catch(B&) {
             glop()
         }
     }

 what the implementation guarantees is, roughly, that you won't throw B,
 but there is no throw spec that will let you "say" that.

Actually, I would argue the reverse. In C++, where you don't have throws declarations, that is what it means. But if you *must* declare all possible thrown exceptions for *every* function, then you know (by its declaration) what bar() might throw. You are not saying that foo() throws all exceptions except B...you are saying that foo() throws any exceptions that bar() might throw...except that it catches B.
Aug 18 2001
prev sibling parent "kaffiene" <kaffiene xtra.co.nz> writes:
I think that a throws declaration is a fantastic idea.  The thing is that it
forces programmers to explicitly handle or explicitly delegate the
responsibility of dealing with any given exception that can occur in it.
This is a really good thing.

C++ ends up with the lame try..catch block in the middle of main to catch
everything - which means that any usefull handling (and possibly error
recovery) is not done.  The throws declaration tends to make people consider
exceptions closer to the source - where they can do something usefull about
it.

This is *A VERY GOOD THING* :-)

Peter.


"Russ Lewis" <russ deming-os.org> wrote in message
news:3B7D3698.1A47A8A8 deming-os.org...
 Is there a reason not to implement a throws declaration?

 IMHO, this is good because it enforces design decisions and interfaces.
 It forces the implementer of the function to either handle the
 exceptions he can generate, or publish to all his callers what he might
 do.

Aug 18 2001