Topic: Exception Specification: Solution in current C++?
Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 9 Mar 1994 11:57:21 GMT Raw View
g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
>Unless I've misunderstood the issues involved, the main complaint with the
>exception specification is that it throws out important information --
>i.e. it calls unexpected() when it violates the exception specification.
No, this doesn't throw away any information.
My complaint is that the default behaviour of unexpected() is
to call abort(). This will *not* lead to more robust programs, IMHO.
>If this is the case, I'm I the only person that reads ARM?
No ;-).
>The solution to this problem is stated quite clearly in section 15.6.2c.
>One could write:
>
> void pass_throw() { throw; }
>
> int main() {
> set_unexpected(&pass_through);
> // Now exception signatures will be ignored and no valuable
> // exception information will be lost.
> }
Yes, I have been suggesting that this would be a better default
unexpected() than that specified by the ARM.
The problem is that if you are writing a class library, it's not
practical to override `unexpected()'. Overriding `unexpected()'
has similar problems to overriding the global `operator new()'.
>Personally, I think one is asking for trouble if one completely ignores
>violations of the exception specification, one should at least write it
>to an error log file.
Yes, this is probably a good idea.
>One question though: in order to generate an effective error log, one
>needs to know information such as where the exception occured, what type
>of exception occured, etc. One should at least know the information
>provided by the class xmsg. Now, is there a way to access this information
>to write it into the error log?
It's not possible to determine where the exception occurred.
It is possible to do a case analysis on the type of the exception
and to recover the information provided by the class xmsg,
I believe. The following should work, unless I've misinterpreted the
ARM.
void pass_throw() {
error_log << "Unexpected exception" << endl;
try {
throw;
}
catch (const xmsg &x) {
// recover xmsg information,
// and print relevent bits to error log
throw;
}
catch (const xmsg *xp) {
// recover xmsg information,
// and print relevent bits to error log
throw;
}
catch (...) {
error_log << "Sorry, I couldn't work out what type
of exception it was." << endl;
}
}
Note that this technique could also be used to transform the exceptions
to `Unexpected' *without* losing information, although there are some
difficulties.
>If so how, and if not, shouldn't that information be available to the
>programmer so that it can be used? I'm thinking of something more in lines
>of a function, say:
> const xmsg* exception_information();
>that returns information about the exception (and (xmsg*)0 if we are not
>currently in an exception handler or called by one). This would be enough
>for the error-log and would add quite a bit of functionality to the
>exception specification. It would, at minimum, provide a context for
>the exception, and possibly provide information about the exception itself.
Providing the context for the exception is a bit tricky. What do you
want in the way of context, exactly? Clearly source code filename and
line number is not going to be possible once the debugging information
has been removed from the executable. I guess it would be possible for
the exception mechanism to store the instruction pointer whenever an
exception is thrown (other than rethrows), and to provide a way of
accessing this.
>Consider the case where a library is upgraded and now another library
>doesn't work properly (it calls unexpected()) because the new library
>throws a Network exception, xnetwork. Now if xnetwork is derived from
>xmsg, it's possible to turn off this specific exception specification
>violation while allowing all others to be preserved. This is what I
>mean:
>
> void pass_throw() {
> if (dynamic_cast<xnetwork*>(exception_information()))
> throw; // ignore network exception specification errors
>
> // All other exceptions may not be ignored.
> terminate();
> }
This can be done using the technique I described above:
void pass_throw() {
try {
throw;
}
catch(const xnetwork&) {
throw; // ignore network exception specification errors
}
// All other exceptions may not be ignored.
terminate();
}
--
Fergus Henderson - fjh@munta.cs.mu.oz.au
Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: Wed, 9 Mar 1994 13:20:53 GMT Raw View
In article <9406821.28524@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
>>The solution to this problem is stated quite clearly in section 15.6.2c.
>>One could write:
>>
>> void pass_throw() { throw; }
>>
>> int main() {
>> set_unexpected(&pass_through);
>> // Now exception signatures will be ignored and no valuable
>> // exception information will be lost.
>> }
>
>Yes, I have been suggesting that this would be a better default
>unexpected() than that specified by the ARM.
>
>The problem is that if you are writing a class library, it's not
>practical to override `unexpected()'. Overriding `unexpected()'
>has similar problems to overriding the global `operator new()'.
>
True, but I can't (yet, at least) see any way to give exception signatures
any teeth without this default. One might argue that without this default,
exception signatures are little more than syntactical comments with little
semantic value.
I'd just as soon avoid exception signatures, but others here clearly have
other opinions.
>>If so how, and if not, shouldn't that information be available to the
>>programmer so that it can be used? I'm thinking of something more in lines
>>of a function, say:
>> const xmsg* exception_information();
>>that returns information about the exception (and (xmsg*)0 if we are not
>>currently in an exception handler or called by one). This would be enough
>>for the error-log and would add quite a bit of functionality to the
>>exception specification. It would, at minimum, provide a context for
>>the exception, and possibly provide information about the exception itself.
>
>Providing the context for the exception is a bit tricky. What do you
>want in the way of context, exactly? Clearly source code filename and
>line number is not going to be possible once the debugging information
>has been removed from the executable. I guess it would be possible for
>the exception mechanism to store the instruction pointer whenever an
>exception is thrown (other than rethrows), and to provide a way of
>accessing this.
>
Originally, I meant the context of the exception at the point of the last
throw, but now I think (as has been suggested here) that it's sufficient for
void f() throw(S) { ... }
to be translated into
void f() {
try {
...
}catch(S){
throw;
}catch(...){
call_unexpected(__FILE__,__LINE__,"");
}
}
Where the default behavior of:
void call_unexpected(char const* filename,
char const* linenumber,
// Compiler-specific extra information
char const* optional_debug_info=0
);
is to call unexpected(). The default behaviour of unexpected() would then
be to print the context information to cerr and call terminate.
What irks me about the current definition (if I understand it well), is that
when an exception specification is violated, you know absolutely nothing
about where the violation occurred. Using the default for unexpected(),
you terminate the program abruptly with no explanations. This *might* be
acceptable if you have the source code and a good debugger, but suppose
the library itself (or an executable without source code) has an exception
signature violation? What information can be given to the vendor of the
library or executable to that the vendor can localize and fix the problem?
>>Consider the case where a library is upgraded and now another library
>>doesn't work properly (it calls unexpected()) because the new library
>>throws a Network exception, xnetwork. Now if xnetwork is derived from
>>xmsg, it's possible to turn off this specific exception specification
>>violation while allowing all others to be preserved. This is what I
>>mean:
>>
>> void pass_throw() {
>> if (dynamic_cast<xnetwork*>(exception_information()))
>> throw; // ignore network exception specification errors
>>
>> // All other exceptions may not be ignored.
>> terminate();
>> }
>
>This can be done using the technique I described above:
>
> void pass_throw() {
> try {
> throw;
> }
> catch(const xnetwork&) {
> throw; // ignore network exception specification errors
> }
>
> // All other exceptions may not be ignored.
> terminate();
> }
Nice trick!
Thanks, that addresses one of my main concerns about exception specifications.
>
>--
>Fergus Henderson - fjh@munta.cs.mu.oz.au
Take care
Robert
--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse |"If we have to re-invent the wheel, |
| EMAIL: g2devi@cdf.utoronto.ca | can we at least make it round this time"|
+----------------------------------+------------------------------------------/
Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Thu, 10 Mar 1994 17:36:35 GMT Raw View
pascual@gonzo.tid.es (Pascual Juan) writes:
>g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
>|> void pass_throw() { throw; }
>|>
>|> int main() {
>|> set_unexpected(&pass_through);
>|>
>|> // Now exception signatures will be ignored and no valuable
>|> // exception information will be lost.
>|> }
>|>
>|> Now there is no loss of information and the application can be made more
>|> fault-tolerant.
>
>If you read ARM 15.6.2c. carefully, in the first paragraph he says:
>
>"The idea is that a violation of a promise to throw only a specified set of
>exceptions is a serious design error that must be fixed before any further
>progress can be made."
Without static checking, this error is likely to be quite common.
The whole point of exception handling is to built more robust and
fault-tolerant applications. If applications commonly contain
exception signature errors, and these errors cause the applications
to abort, how will this improve fault-tolerance or robustness?
>If it can be fixed in compile time it must be done.
Sure. With static checking, the error would always be detected at
compile time, and so the problem wouldn't arise at run time.
>Otherwise, ignoring unexpected exceptions IS NOT SAFER THAN EXITING.
The code above doesn't ignore unexpected exceptions, it passes them on
up the call chain until it finds some routine that is willing to deal
with the exception. I think it's probable that this will produce a
more reasonable end result than just aborting. (Of course, you should
also at least log the fact that there was a design error.)
>In a variation of the
>ARM 15.6.2c. example, let's supose a medical application:
[example omitted]
>I think it's clear enough.
I don't. What's the point of this example?
How does it relate to using `set_unexpected(pass_thru)'?
--
Fergus Henderson - fjh@munta.cs.mu.oz.au
Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Thu, 10 Mar 1994 17:40:05 GMT Raw View
:Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
: The problem is that if you are writing a class library, it's not
: practical to override `unexpected()'. Overriding `unexpected()'
: has similar problems to overriding the global `operator new()'.
I concur.
: It's not possible to determine where the exception occurred.
I think it is... see below.
: Providing the context for the exception is a bit tricky. What do you
: want in the way of context, exactly? Clearly source code filename and
: line number is not going to be possible once the debugging information
: has been removed from the executable. I guess it would be possible for
: the exception mechanism to store the instruction pointer whenever an
: exception is thrown (other than rethrows), and to provide a way of
: accessing this.
I believe filename and line number is possible in much the same way as the
C preprocessor does now. Whatever code is generated to detect out-of-sig
exceptions will call unexpected(). If unexpected took the two arguments
the unexpected function would know where the exception occurred. For
example take the following pseudo code that the compiler could generate:
if (!IsInSignature(Exception))
unexpected("file.cxx", 301);
else
throw(Exception);
Stephen Cipolli
Datascope Corp.
These opinions are mine alone.
Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Mon, 7 Mar 1994 19:42:44 GMT Raw View
Robert N. Deviasse (g2devi@cdf.toronto.edu) wrote:
: Unless I've misunderstood the issues involved, the main complaint with the
: exception specification is that it throws out important information --
: i.e. it calls unexpected() when it violates the exception specification.
: For some fault-tolerant applications, all exceptions must be dealt with
: (the consequences of failure are too high) so this loss of information is
: intolerable since it's nearly impossible to recover from a situation where
: you know nothing about.
: If this is the case, I'm I the only person that reads ARM? The solution to
: this problem is stated quite clearly in section 15.6.2c. One could write:
: void pass_throw() { throw; }
: int main() {
: set_unexpected(&pass_through);
: // Now exception signatures will be ignored and no valuable
: // exception information will be lost.
: }
: Now there is no loss of information and the application can be made more
: fault-tolerant.
: Personally, I think one is asking for trouble if one completely ignores
: violations of the exception specification, one should at least write it
: to an error log file. As follows:
: void pass_throw() {
: error_log << "Unexpected exception ignored at time:"
: << current_time
: << endl ;
: throw;
: }
: If this solution is acceptable to all, then let's put the issue to rest.
: C++ needs to be standardized and changes in default behaviour tend to have
: a ripple effect.
This is the likely course of action for those of us who want to use throw
clauses in our prototypes to eventually reap the benefits of whatever static
checking we eventually get. It, however, does not solve the static checking
problem. Your proposal simply by-passes the unexpected mechanism. The code
either terminates due to lack of an appropriate catch or is caught by a
catch(...) (It could also wind up in an appropriate catch, but it is likely
that an out-of-signature exception is also outside of all catch). In either
case you find yourself in the uneviable position of handling an unknown
exception at run-time. Also, you fail to propose work arounds for indirect
use of exceptions (function pointers, templates, etc.) which the
static-checkers have sited as neccessary to allow complete static checking
(now or later). I will not attempt to continue into the "throws nothing" vs.
"throws anything" argument, since it hinges on many more issues.
: One question though: in order to generate an effective error log, one
: needs to know information such as where the exception occured, what type
: of exception occured, etc. One should at least know the information
: provided by the class xmsg. Now, is there a way to access this information
: to write it into the error log?
: If so how, and if not, shouldn't that information be available to the
: programmer so that it can be used? I'm thinking of something more in lines
: of a function, say:
: const xmsg* exception_information();
: that returns information about the exception (and (xmsg*)0 if we are not
: currently in an exception handler or called by one). This would be enough
: for the error-log and would add quite a bit of functionality to the
: exception specification. It would, at minimum, provide a context for
: the exception, and possibly provide information about the exception itself.
: Consider the case where a library is upgraded and now another library
: doesn't work properly (it calls unexpected()) because the new library
: throws a Network exception, xnetwork. Now if xnetwork is derived from
: xmsg, it's possible to turn off this specific exception specification
: violation while allowing all others to be preserved. This is what I
: mean:
: void pass_throw() {
: if (dynamic_cast<xnetwork*>(exception_information()))
: throw; // ignore network exception specification errors
: // All other exceptions may not be ignored.
: terminate();
: }
: This level of functionality would tend to make exception signatures more
: plausable for general use.
I think an easier solution might be to have the unexpected function accept
two arguments, char *filename and int linenumber, and have the implementation
plug in what amounts to __FILE__ and __LINE__ when it is called. That at least
covers the "where", the "what" is not possible unless all exceptions are forced
to be derived from a common base. I don't think such a restriction will pass
muster with the C++ committee, nor would I recommend it.
I appreciate your effort.
Stephen Cipolli
Datascope Corp.
These opinions are mine alone.
Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: Thu, 3 Mar 1994 17:13:07 GMT Raw View
Unless I've misunderstood the issues involved, the main complaint with the
exception specification is that it throws out important information --
i.e. it calls unexpected() when it violates the exception specification.
For some fault-tolerant applications, all exceptions must be dealt with
(the consequences of failure are too high) so this loss of information is
intolerable since it's nearly impossible to recover from a situation where
you know nothing about.
If this is the case, I'm I the only person that reads ARM? The solution to
this problem is stated quite clearly in section 15.6.2c. One could write:
void pass_throw() { throw; }
int main() {
set_unexpected(&pass_through);
// Now exception signatures will be ignored and no valuable
// exception information will be lost.
}
Now there is no loss of information and the application can be made more
fault-tolerant.
Personally, I think one is asking for trouble if one completely ignores
violations of the exception specification, one should at least write it
to an error log file. As follows:
void pass_throw() {
error_log << "Unexpected exception ignored at time:"
<< current_time
<< endl ;
throw;
}
If this solution is acceptable to all, then let's put the issue to rest.
C++ needs to be standardized and changes in default behaviour tend to have
a ripple effect.
One question though: in order to generate an effective error log, one
needs to know information such as where the exception occured, what type
of exception occured, etc. One should at least know the information
provided by the class xmsg. Now, is there a way to access this information
to write it into the error log?
If so how, and if not, shouldn't that information be available to the
programmer so that it can be used? I'm thinking of something more in lines
of a function, say:
const xmsg* exception_information();
that returns information about the exception (and (xmsg*)0 if we are not
currently in an exception handler or called by one). This would be enough
for the error-log and would add quite a bit of functionality to the
exception specification. It would, at minimum, provide a context for
the exception, and possibly provide information about the exception itself.
Consider the case where a library is upgraded and now another library
doesn't work properly (it calls unexpected()) because the new library
throws a Network exception, xnetwork. Now if xnetwork is derived from
xmsg, it's possible to turn off this specific exception specification
violation while allowing all others to be preserved. This is what I
mean:
void pass_throw() {
if (dynamic_cast<xnetwork*>(exception_information()))
throw; // ignore network exception specification errors
// All other exceptions may not be ignored.
terminate();
}
This level of functionality would tend to make exception signatures more
plausable for general use.
Take care
Robert
--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse |"If we have to re-invent the wheel, |
| EMAIL: g2devi@cdf.utoronto.ca | can we at least make it round this time"|
+----------------------------------+------------------------------------------/
Author: pascual@gonzo.tid.es (Pascual Juan)
Date: Fri, 4 Mar 1994 09:21:09 GMT Raw View
In article <1994Mar3.171307.29800@cdf.toronto.edu>, g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
|> For some fault-tolerant applications, all exceptions must be dealt with
|> (the consequences of failure are too high) so this loss of information is
|> intolerable since it's nearly impossible to recover from a situation where
|> you know nothing about.
|>
|> If this is the case, I'm I the only person that reads ARM? The solution to
|> this problem is stated quite clearly in section 15.6.2c. One could write:
|>
|> void pass_throw() { throw; }
|>
|> int main() {
|> set_unexpected(&pass_through);
|>
|> // Now exception signatures will be ignored and no valuable
|> // exception information will be lost.
|>
|> }
|>
|> Now there is no loss of information and the application can be made more
|> fault-tolerant.
|>
If you read ARM 15.6.2c. carefully, in the first paragraph he says:
"The idea is that a violation of a promise to throw only a specified set of
exceptions is a serious design error that must be fixed before any further
progress can be made."
If it can be fixed in compile time it must be done. Otherwise, ignoring
unexpected exceptions IS NOT SAFER THAN EXITING. In a variation of the
ARM 15.6.2c. example, let's supose a medical application:
extern void avoid_illness() throw (Flu, Cancer);
void pass_through() { throw }
void life()
{
PFV old = set_unexpected(&pass_through);
try {
avoid_illness();
}
catch (Flu) {
eat_an_aspirin();
continue;
}
catch (Cancer) {
get_radiotherapy();
pray();
continue;
}
catch (...) { // You can catch A.I.D.S. or something nasty like that.
cerr << "I don't know anything about your ill. "
<< "It could be nothing or you can die..."
<< "Better if you write your will." << endl;
}
set_unexpected(old);
}
I think it's clear enough. I don't want to compile nor link my code with
unknown/unexpected exceptions.
|> Take care
|> Robert
It's just what I'm trying. Please read my collection of proposals for
compile-time checking of signatures.
--
-------------------------------------------------------------------------------
|||| ## #### | Pascual Juan | _V_
|||| ## _|_ ## ## Telefonica I+D | E-mail: pascual@gonzo.tid.es | _(,|,)`
@@@@ ## | ## ## (Telefonica R&D) | Phone: +34-1-337-47-04 | | ___ ')
@@@@ ## #### | fax: +34-1-337-42-22 | |_|`__/
-------------------------------------------------------------------------------