Topic: broken std::uncaught_exception() (Was: Exception Handling)
Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/12/12 Raw View
Christopher Eltschka wrote:
>
> Is there any multithreading system which doesn't give you the thread
> number of the current thread? That's all you need for "thread-local"
> storage:
>
> int& some_value()
> {
> static int sv[MAX_THREADS];
> return sv[current_thread_id()];
> }
Probably not. However, calling current_thread_id() may be slightly
costly, and it may return an index into a table that is sparse from the
point of view of the application (i.e., an index into all threads in the
entire system), or it may return an index whose minimum is some large
value, or it may return the address of some OS data structure. In all
those cases, you'd have to use a map instead of a simple array, which
adds still more overhead.
So I guess we wouldn't be "sunk" if we didn't have true thread-local
storage, but we'd be very unhappy yachtsmen.
--
Ciao,
Paul
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/12/09 Raw View
Paul D. DeRocco wrote:
>
> Jerry Leichter wrote:
> >
> > If you really want an el-cheap implementation, you use a mutex to
> > allow only one thread to be using the exception handling machinery at
> > a time. I haven't yet seen a compiler that tried that approach -
> > doing it right isn't *that* hard.)
>
> ... as long as the OS provides a thread-local storage mechanism. If
> Win32 didn't use the FS register for that purpose, we'd be sunk.
Is there any multithreading system which doesn't give you the thread
number of the current thread? That's all you need for "thread-local"
storage:
int& some_value()
{
static int sv[MAX_THREADS];
return sv[current_thread_id()];
}
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/11/24 Raw View
Jerry Leichter wrote:
>
> If you really want an el-cheap implementation, you use a mutex to
> allow only one thread to be using the exception handling machinery at
> a time. I haven't yet seen a compiler that tried that approach -
> doing it right isn't *that* hard.)
... as long as the OS provides a thread-local storage mechanism. If
Win32 didn't use the FS register for that purpose, we'd be sunk.
--
Ciao,
Paul
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/11/20 Raw View
Paul D. DeRocco wrote:
> I'm not saying that uncaught_exception() is ill-defined, or broken.
> ...
>
> In other words, uncaught_exception effectively references a global
> variable, thus making any system that uses it open to interference
> from outside.
Good point. I assume that this is yet one more thing that makes
C++ thread-unsafe. In a multithreaded program, what sort of hoops
does the implementor of uncaught_exception() have to jump through
so that multiple simultaneously throwing destructors work correctly?
(Does each thread have to have its own uncaught_exception() state
variable?)
-- David R. Tribble, dtribble@technologist.com --
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Jerry Leichter <leichter@smarts.com>
Date: 1998/11/20 Raw View
| > In other words, uncaught_exception effectively references a global
| > variable...
|
| Good point. I assume that this is yet one more thing that makes
| C++ thread-unsafe. In a multithreaded program, what sort of hoops
| does the implementor of uncaught_exception() have to jump through
| so that multiple simultaneously throwing destructors work correctly?
| (Does each thread have to have its own uncaught_exception() state
| variable?)
This is no big deal - there already has to be per-thread storage for the
pending exception. After all, each thread must be able to independently
throw and catch exceptions, or you can't effectively use exceptions
together with multithreading. Several compilers have gone through a
stage where they generated MT-safe code - but not if exceptions were
enabled. (It's no good to say that the space is allocated dynamically -
you still need a pointer to that dynamically allocated data, and that
pointer is thread-specific. If you really want an el-cheap implementa-
tion, you use a mutex to allow only one thread to be using the exception
handling machinery at a time. I haven't yet seen a compiler that tried
that approach - doing it right isn't *that* hard.)
-- Jerry
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: zivc@peach-networks.com (Ziv Caspi)
Date: 1998/11/16 Raw View
On 12 Nov 98 18:07:56 GMT, Hyman Rosen <hymie@prolifics.com> wrote:
>"Paul D. DeRocco" wrote:
>> I'm not saying that uncaught_exception() is ill-defined, or broken. I'm
>> saying that what people inevitably want it to do isn't quite what it
>> does, and that what it actually does really has no use. Whether there is
>> some uncaught exception somewhere is an intrinsically uninteresting
>> piece of information.
>
>What is really needed is the following:
>
>template<typename Exception> bool std::will_be_caught();
>
>At some point in a destructor (or even not in one), you say
>
> if (std::will_be_caught<MyException>())
> throw MyException();
> else
> /* Do something else */;
>
This is only partially satisfactory. A more complete step would be to
allow exceptions to be resumed. That is, in the "try-catch" context we
will have the ability to add code that is run in the context of the
"throw" phrase, perform any decisions that are required, and only
*then* determine if to actually start rolling back the stack or not.
This is *also* a partial solution, of course. The problem arises
we have been trained only to think in terms of blocks:
begin-a
begin-b
end-b
end-a
But, as many have stated here, exceptions are a good example where
it is reasonable to have the end-a and end-b swapped.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/11/17 Raw View
Gargantua Blargg wrote:
[...]
> This leaves those "normal" classes that have possible errors in
> destruction (writing to a file, for example). As others have suggested, I
> usually add explicit close() (and similar) functions that should be called
> by the user just before the object is destructed(), to prevent errors from
> happening in the destructor. But even this isn't enough, since an
> exception could occur between the use of this object and the call to
> close():
IMHO, if something can fail in a destructor, a callback function
is appropriate. The question remains, of course, if the callback
should always be called, or only from destructor. In the latter case,
it could be called from the catch(...) block, allowing it to examine
the exception that was caught:
>
> File file( "foo" ); // buffers writes in its cache
>
> file.write( "blabla" ); // now something is in the buffer
>
> func_that_could_throw(); // throws exception
>
> file.flush(); // flushes buffer and checks for write errors
>
> File::~File() // called during stack unwinding
> {
bool retry=false;
int retry_num=0;
while (retry) try
{
> flush(); // gotta do this before closing
}
catch(...)
{
retry=callback(this, retry_num++);
}
> close();
> // ...
> }
bool example_callback(File* f, int retry_num)
{
try
{
throw; // re-throw the exception which caused the callback call
}
catch (disk_removed)
{
cerr << "Please re-insert disk and press return" << endl;
string dummy;
readline(cin, dummy);
}
catch (write_error)
{
if (retry_num<3) // try up to three times
return true;
cerr << "could not write data on file" << f.name() << endl;
return false; // give up
}
catch (...)
{
cerr << "Unknown error" << endl;
return false;
}
}
>
> Since the flush in the user code is bypassed by the exception, a write
> error could now occur in File's destructor. So is the user supposed to
> call error-prone transaction completion functions for objects after every
> use, before any point an exception could be thrown? Maybe so.
With a callback, you still can intercept those errors (and even
retry, a choice exceptions cannot give you). And you might even
throw from there, since you as supplier of the callback know what
context the destructor of the given object might be called, and
may therefore be able to determine cases where throwing _is_ safe.
(An example is a single objcect allocated with new, and explicitly
destructed through delete.)
[...]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/11/12 Raw View
Christopher M. Gurnee wrote:
>
> Valentin Bonnard wrote:
> >|> I agree that current uncaught_exception() specificatons are
> >|> broken: this function indicates if there is an active exception
> >|> somewhere in the system, not if we are currently doing the
> >|> stack walkback:
> >
> >|> struct E { ~E () { std::cout << std::uncaught_exception() <<
> std::endl;
> >|> }
> >
> >|> E x;
> >|> throw 0;
> >
> >|> correctly prints true but
> >
> >|> struct F { ~F () { E e; } } f;
> >|> throw 0;
> >
> >|> incorrectly prints true ! (we aren't doing a walkback because
> >|> of exceptions, we are normally exiting a function).
> <clip>
>
> I'm not sure if I understand or agree with your reasoning here. f is
> being destroyed due to unwinding here.
Yes
> When f's dtor exists, and e's
> lifetime ends, e's dtor is executed, giving it the chance to throw.
Yes
> Under normal (non-stack-unwinding) circumstances, e could "safely" throw
> an exception, which f could "safely" ignore. If it were to throw one
> here, the exception would propagate out of F::~F, and terminate() would
> be called (this is pretty much a repeat of what James says below).
Yes, but irrelevant
> If uncaught_exception returned false in e's dtor, all dtors (such as
> F::~F)would have to catch any exceptions indirectly thrown and not
> rethrow them if uncaught_exception is true, kind of like in James'
> example below.
Yes
> While this is a possible definition of
> uncaught_exception, IMHO it's a bit cumbersome. How do you (Valentin
> Bonnard) suggest uncaught_exception should be defined?
How do you (Christopher M. Gurnee) suggests it should be defined ?
[ Please indicate the first point where you disagree in
the following: ]
Let me answer my own question: as it is now.
To quote Christopher M. Gurnee, "it's a bit cumbersome." Every
throwing function anywhere (except the main function), including
library functions, included standard library functions, must
check uncaught_exception() first.
And note that no one does that: not the standard, not my code,
note third party code I have seen, probably not even your code.
So _your_ prefered specification makes things cumbersome in _your_
opinion.
--
Valentin Bonnard mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: blargg@flash.net (Gargantua Blargg)
Date: 1998/11/13 Raw View
In article <364B0EA8.D10C69A@prolifics.com>, Hyman Rosen
<hymie@prolifics.com> wrote:
> "Paul D. DeRocco" wrote:
> > I'm not saying that uncaught_exception() is ill-defined, or broken. I'm
> > saying that what people inevitably want it to do isn't quite what it
> > does, and that what it actually does really has no use. Whether there is
> > some uncaught exception somewhere is an intrinsically uninteresting
> > piece of information.
>
> What is really needed is the following:
>
> template<typename Exception> bool std::will_be_caught();
>
> At some point in a destructor (or even not in one), you say
>
> if (std::will_be_caught<MyException>())
> throw MyException();
> else
> /* Do something else */;
...
I think this discussion for a solution is solving the immediate problem at
hand, namely allowing user code to keep terminate() from being called, yet
throw exceptions from destructors, but the discussion is not looking at
the bigger picture, which is throwing exceptions from destructors, and
problems with that.
Now to just focus on the problem at hand :-) your idea for a
std::will_be_caught<>() just makes user code more dependent on where it is
called from. Change a catch spec here, get different behavior there, where
"there" throws an exception or does something else (what else? who knows).
I don't like the hidden coupling that is created. And through my
associative memory, this is triggering memory of discussion on exception
specifications, and how they have some of the same problems (though not as
hidden).
Just looking at throwing an exception from a destructor, the major problem
of the object not being destroyed comes up:
class X {
auto_ptr<int> use_count;
X() { /* ... */ }
~X()
{
if ( bla bla )
throw XYZ();
// is use_count destructed even when the throw occurs?
}
};
I can't find the appropriate section in the standard. I found this part:
> 15.2 Constructors and destructors
>
> 2 An object that is partially constructed will have destructors executed
> only for its fully constructed sub-objects.
(hey, I *tried* to purchase the darn ANSI standard, but it wouldn't
validate the credit card, so this is from CD2)
Is an X object considered partially constructed inside X::~X() (i.e.,
use_count still needs to be destructed)?
I think that in general, classes need to be written in every way possible
to insure that destruction will not require any resources, or any
operations that could fail. Obviously some classes will be written
specifically to throw an exception in this case, but the majority won't.
Those that intentionally throw an exception (an AutoThrowErrorCode that
throws an exception if the error code is not extracted, for example)
should be treated like a throw (by the user), and shouldn't be created
where an exception can be thrown during their lifetime, so they aren't a
problem.
This leaves those "normal" classes that have possible errors in
destruction (writing to a file, for example). As others have suggested, I
usually add explicit close() (and similar) functions that should be called
by the user just before the object is destructed(), to prevent errors from
happening in the destructor. But even this isn't enough, since an
exception could occur between the use of this object and the call to
close():
File file( "foo" ); // buffers writes in its cache
file.write( "blabla" ); // now something is in the buffer
func_that_could_throw(); // throws exception
file.flush(); // flushes buffer and checks for write errors
File::~File() // called duing stack unwinding
{
flush(); // gotta do this before closing
close();
// ...
}
Since the flush in the user code is bypassed by the exception, a write
error could now occur in File's destructor. So is the user supposed to
call error-prone transaction completion functions for objects after every
use, before any point an exception could be thrown? Maybe so.
Maybe, in this example, buffering (caching) could be turned on and off bu
a member function of File. It would only be turned on in performance
bottlenecks of the system, where close attention would be paid to possible
exception throwing points. This is similar to sync versus async exceptions
occurring in modern RISC processors, where instructions can complete out
of order, and an address error from an earlier instruction could occur
*after* some other operation was already performed.
It's almost like breaking the system into multiple separate entities. In
the example, there is the main flow of the program, and the side flow of
the File object. It handles synchronization of the view it provides and
the physical file system. Operations may or may not happen immediately
with respect to the outside world. Who's to say which error happened
first? Was it the error in func_that_could_throw() or the write error when
flushing the buffer? Heck, if we think about those OSes that don't
actually allocate the memory until it is accessed, even something
seemingly synchronous like operator new () could turn out to be
asychronous just like the file flushing!
Maybe it would be good to separate the errors into synchronous errors that
involve the current control flow, and errors that involve another (not
necessarily real with regard to the OS) thread? However, there is
certainly code that wants to treat all these erros as synchronous, where
the order of things matters, and completing one after the other,
successfully, matters. In these, buffering would probably not be used,
since it is obviously not possible (with simple buffering that assumes a
write error will not occur) to buffer yet tell when a write error is going
to occur before the write is even performed!
Perhaps there are two categories of functions: those that must know
exactly what external effects have been applied when, and those that are
only concerned with completing successfully, or, failing that, returning
an error of the first cause of failure, and cleaning up everything to how
it was before.
I'd say the second class is quite common in everyday application code. An
example would be saving to a file. If it fails due to out of memory, for
example, we don't care if any pending writes to the file fail. We just
want to delete the original file and exit the function. In general, this
kind of semi-rollback is fairly easily implementable.
What I'm trying to do is bring this issue from the solving of this
destructor invocation issue and seeing if we can throw, to a general
design issue of what works in real systems. At the very least, for me, it
moves my activity from trying to fix the language, or hating it, to
solving the problem in the abstract design space, where languages don't
exist (well, don't have as much influence on thought :-), and then seeing
how the solution can work with C++ as it currently is defined.
Anyway, in the common case, it seems that the we simple want to ignore any
other errors! Perhaps other errors could be reported during debugging, in
case there was a programmer error (example: couldn't delete temp file
because it was open).
It's late and I need some sleep. Hope this gets the ball rolling in
someone's head. Thanks for reading.
--
blarggflash.net | Gargantua Blargg | http://www.flash.net/~blargg/
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: abrahams@spam.motu.com (David Abrahams)
Date: 1998/11/11 Raw View
On 10 Nov 1998 18:45:14 GMT, jkanze@otelo.ibmmail.com wrote:
>If I understand correctly, the purpose of uncaught_exception is to
>inform whether a destructor can safely throw.
No, the purpose of uncaught_exception is to do what the standard says.
The standard says it returns true between the time of executing a
throw-expression and entering the matching handler (synopsis).
[I don't understand how anyone can claim in "incorrectly" does
anything in a conforming compiler]
>In the example you give
>as "incorrect", the destructor cannot safely throw; if it does,
>terminate will be called.
>
>A more interesting example might involve:
>
> struct F { ~F() { try { E e ; } catch (...) {} } } ;
>
>What does the standard say about this?
Recalling: E::~E() prints the value of std::uncaught_exception().
The standard says that if an exception is thrown which causes an F to
be destroyed, true will be printed.
-Dave
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/11/12 Raw View
jkanze@otelo.ibmmail.com wrote:
>=20
> |> I agree that current uncaught_exception() specificatons are
> |> broken: this function indicates if there is an active exception
> |> somewhere in the system, not if we are currently doing the
> |> stack walkback:
>=20
> |> struct E { ~E () { std::cout << std::uncaught_exception() << std::en=
dl;
> |> }
>=20
> |> E x;
> |> throw 0;
>=20
> |> correctly prints true but
>=20
> |> struct F { ~F () { E e; } } f;
> |> throw 0;
>=20
> |> incorrectly prints true ! (we aren't doing a walkback because
> |> of exceptions, we are normally exiting a function).
>=20
> |> Note that
>=20
> |> struct F { E e; /*~F () {} */ } f;
> |> throw 0;
>=20
> |> correctly prints true.
>=20
> |> uncaught_exception() breaks basic functionnal decomposition; a
> |> function does its job, honors its exception spec, ect, it doesn't
> |> have to know that its caller is already playing with exceptions.
>=20
> |> uncaught_exception() should indicate if we are destroyed because
> |> of a walk-back.
>=20
> If I understand correctly, the purpose of =AB uncaught_exception =BB is=
to
> inform whether a destructor can safely throw.=20
No, you don't understand it at all. ;-)
Any function can safely throw (not just the dtor) only if the=20
code is exception safe, for a sufficently strong definition=20
of exception safe. uncaught_exception() of course won't tell=20
you that.
uncaught_exception() should only tell you if you are being=20
destroyed because of a stack walk-back.
Else, we end-up with silly things like:
struct E { ~E () {
try {
if (uncaught_exception())
throw 1;
}
catch (...)
{
if (rand () % 2)
throw;
}
} e;
throw 0;
With my definition it is clear that (uncaught_exception()) is=20
true here.
> In the example you give
> as "incorrect", the destructor cannot safely throw; if it does,
> terminate will be called.
Correct
> A more interesting example might involve:
>=20
> struct F { ~F() { try { E e ; } catch (...) {} } } ;
>=20
> What does the standard say about this?
Again, try blocks should not and do not affect=20
uncaught_exception().
--=20
Valentin Bonnard mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/11/12 Raw View
David Abrahams wrote:
>
> No, the purpose of uncaught_exception is to do what the standard says.
> The standard says it returns true between the time of executing a
> throw-expression and entering the matching handler (synopsis).
> [I don't understand how anyone can claim in "incorrectly" does
> anything in a conforming compiler]
I'm not saying that uncaught_exception() is ill-defined, or broken. I'm
saying that what people inevitably want it to do isn't quite what it
does, and that what it actually does really has no use. Whether there is
some uncaught exception somewhere is an intrinsically uninteresting
piece of information.
Assume you've got a closed system in which exceptions are used, and
uncaught_exception() is used within that system to find out whether it
is safe to throw or not. Now invoke that supposedly closed system from
within a destructor during stack unwinding on behalf of some exception
that exists totally outside that closed system. What happens is that
uncaught_exception() now returns true, even though that closed system
cannot possibly be interested in that external exception.
In other words, uncaught_exception effectively references a global
variable, thus making any system that uses it open to interference from
outside.
--
Ciao,
Paul
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Hyman Rosen <hymie@prolifics.com>
Date: 1998/11/12 Raw View
"Paul D. DeRocco" wrote:
> I'm not saying that uncaught_exception() is ill-defined, or broken. I'm
> saying that what people inevitably want it to do isn't quite what it
> does, and that what it actually does really has no use. Whether there is
> some uncaught exception somewhere is an intrinsically uninteresting
> piece of information.
What is really needed is the following:
template<typename Exception> bool std::will_be_caught();
At some point in a destructor (or even not in one), you say
if (std::will_be_caught<MyException>())
throw MyException();
else
/* Do something else */;
The function needs to do the some of the same work that actually
throwing an exception does - namely, walk back on the runtime stack
to see if there is a catch block that would handle an exception of
this type. There needs to be another version which checks whether
a rethrow is safe -
bool std::rethrow_will_be_caught();
try {
/* ... */
} catch (...) {
if (std::rethrow_will_be_caught())
throw;
else
/* Do something else */
}
If these functions are called from a DIDSUCE, they do not walk back on
the stack farther than the destructor's stack frame.
Note that it's OK for these functions to be slow and expensive, since
they are presumably called only when an exception is about to be thrown,
which is also permitted to be slow and expensive.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: jkanze@otelo.ibmmail.com
Date: 1998/11/10 Raw View
|> I agree that current uncaught_exception() specificatons are
|> broken: this function indicates if there is an active exception
|> somewhere in the system, not if we are currently doing the
|> stack walkback:
|> struct E { ~E () { std::cout << std::uncaught_exception() << std::endl;
|> }
|> E x;
|> throw 0;
|> correctly prints true but
|> struct F { ~F () { E e; } } f;
|> throw 0;
|> incorrectly prints true ! (we aren't doing a walkback because
|> of exceptions, we are normally exiting a function).
|> Note that
|> struct F { E e; /*~F () {} */ } f;
|> throw 0;
|> correctly prints true.
|> uncaught_exception() breaks basic functionnal decomposition; a
|> function does its job, honors its exception spec, ect, it doesn't
|> have to know that its caller is already playing with exceptions.
|> uncaught_exception() should indicate if we are destroyed because
|> of a walk-back.
If I understand correctly, the purpose of uncaught_exception is to
inform whether a destructor can safely throw. In the example you give
as "incorrect", the destructor cannot safely throw; if it does,
terminate will be called.
A more interesting example might involve:
struct F { ~F() { try { E e ; } catch (...) {} } } ;
What does the standard say about this?
--
James Kanze mailto: kanze@gabi-soft.fr
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Christopher M. Gurnee" <gurnec_at_mediaone_dot_net@127.0.0.1>
Date: 1998/11/11 Raw View
Valentin Bonnard wrote:
>|> I agree that current uncaught_exception() specificatons are
>|> broken: this function indicates if there is an active exception
>|> somewhere in the system, not if we are currently doing the
>|> stack walkback:
>
>|> struct E { ~E () { std::cout << std::uncaught_exception() <<
std::endl;
>|> }
>
>|> E x;
>|> throw 0;
>
>|> correctly prints true but
>
>|> struct F { ~F () { E e; } } f;
>|> throw 0;
>
>|> incorrectly prints true ! (we aren't doing a walkback because
>|> of exceptions, we are normally exiting a function).
<clip>
I'm not sure if I understand or agree with your reasoning here. f is
being destroyed due to unwinding here. When f's dtor exists, and e's
lifetime ends, e's dtor is executed, giving it the chance to throw.
Under normal (non-stack-unwinding) circumstances, e could "safely" throw
an exception, which f could "safely" ignore. If it were to throw one
here, the exception would propagate out of F::~F, and terminate() would
be called (this is pretty much a repeat of what James says below).
If uncaught_exception returned false in e's dtor, all dtors (such as
F::~F)would have to catch any exceptions indirectly thrown and not
rethrow them if uncaught_exception is true, kind of like in James'
example below. While this is a possible definition of
uncaught_exception, IMHO it's a bit cumbersome. How do you (Valentin
Bonnard) suggest uncaught_exception should be defined?
jkanze@otelo.ibmmail.com wrote in message
<729ur6$qc1$1@nnrp1.dejanews.com>...
>If I understand correctly, the purpose of uncaught_exception is to
>inform whether a destructor can safely throw. In the example you give
>as "incorrect", the destructor cannot safely throw; if it does,
>terminate will be called.
>
>A more interesting example might involve:
>
> struct F { ~F() { try { E e ; } catch (...) {} } } ;
(followed by "throw 0;" I assume)
>
>What does the standard say about this?
The standard says uncaught_exception should still return true in e's
dtor. Perhaps uncaught_exception's definition should have included an
exception to the rule like "if a new try-block is entered during stack
unwinding, returns false until that block is exited." But then, what if
the catch block didn't catch all exceptions? Throwing some exceptions
from e would still be unsafe. How about something like "... is exited
iff a catch block of the form catch(...) exists." Or something more
general like "... iff a catch block for every possible exception that
could be thrown," taking exception specs of all called functions into
account. This is quickly becoming very complicated : )
-Chris Gurnee
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/11/10 Raw View
abrahams@motu.com wrote:
>
> In article <36396107.816F5644@ix.netcom.com>,
> "Paul D. DeRocco" <pderocco@ix.netcom.com> wrote:
> > [code snipped]
> >
> > Nice, but it still has the problem, true of all things that use
> > uncaught_exception(), that it may be called while unwinding the stack on
> > behalf of some unrelated exception that it doesn't have anything to do
> > with, and be fooled into thinking it can't do something that it really
> > can, or should do something that it mustn't, etc.
>
> With all due respect, it sounds like you are appealing to vague fears here.
> Please justify your claim with an example.
I agree that current uncaught_exception() specificatons are
broken: this function indicates if there is an active exception
somewhere in the system, not if we are currently doing the
stack walkback:
struct E { ~E () { std::cout << std::uncaught_exception() << std::endl;
}
E x;
throw 0;
correctly prints true but
struct F { ~F () { E e; } } f;
throw 0;
incorrectly prints true ! (we aren't doing a walkback because
of exceptions, we are normally exiting a function).
Note that
struct F { E e; /*~F () {} */ } f;
throw 0;
correctly prints true.
uncaught_exception() breaks basic functionnal decomposition; a
function does its job, honors its exception spec, ect, it doesn't
have to know that its caller is already playing with exceptions.
uncaught_exception() should indicate if we are destroyed because
of a walk-back.
--
Valentin Bonnard mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]