As Herb wrote in GotW and in Exceptional C++, std::uncaught_exception() is
not a useful function because it can't be used to detect whether or not an
exception was engendered in a particular scope.
What I think would be good to have would be kind of an exception stack; that
could be implemented but it's not easy.
The next good thing to have is a simple unsigned int
std::uncaught_exceptions() that tells how many exceptions are being thrown
right now; that way, a class' constructor can store the current value
returned by std::uncaught_exceptions, and then the destructor can compare
that value with the current one. If the current one is greater than the old
one, then the variable's scope is being exited via an exception.
So I'd like to ask:
1. Would others find such a function useful?
2. Is this function easy to implement?
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SeeWebsiteForEmail (94)
|
7/29/2004 10:29:18 AM |
|
* Andrei Alexandrescu (See Website for Email):
>
> The next good thing to have is a simple unsigned int
> std::uncaught_exceptions() that tells how many exceptions are being thrown
> right now; that way, a class' constructor can store the current value
> returned by std::uncaught_exceptions, and then the destructor can compare
> that value with the current one. If the current one is greater than the old
> one, then the variable's scope is being exited via an exception.
>
> So I'd like to ask:
>
> 1. Would others find such a function useful?
It would be interesting to know what _a_ usage could be.
Say a destructor encounters a problem.
If the destructor is not invoked by exception-caused stack unwinding,
then it could conceivably report the problem via an exception.
But if it is invoked that way, then it would have to use some
alternative.
And given that this alternative is built into the destructor anyway,
why not use that in both cases?
Quoting from the FAQ (17.3):
<quote>
The easy way to prevent this is never throw an exception from a destructor. But
if you really want to be clever, you can say never throw an exception from a
destructor while processing another exception. But in this second case, you're
in a difficult situation: the destructor itself needs code to handle both
throwing an exception and doing "something else", and the caller has no
guarantees as to what might happen when the destructor detects an error (it
might throw an exception, it might do "something else"). So the whole solution
is harder to write. So the easy thing to do is always do "something else". That
is, never throw an exception from a destructor.
Of course the word never should be "in quotes" since there is always some
situation somewhere where the rule won't hold. But certainly at least 99% of the
time this is a good rule of thumb.
</quote>
> 2. Is this function easy to implement?
I can see no reason why it shouldn't be since the runtime keeps track of this
information anyway, but I'm no compiler writer.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
alfps
|
7/29/2004 3:18:32 PM
|
|
"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
> that value with the current one. If the current one is greater than the old
> one, then the variable's scope is being exited via an exception.
Extending C++ to allow destructors (and pre-destructos, of course)
to have an optional bool "is_unwinding" argument is probably much
better. std::expected_exception<>() just ought to become standard
as well. AFAIK, David Abrahams loves it. ;-)
regards,
alexander.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alexander
|
7/29/2004 3:19:44 PM
|
|
> The next good thing to have is a simple unsigned int
> std::uncaught_exceptions() that tells how many exceptions are being thrown
> right now;
> So I'd like to ask:
>
> 1. Would others find such a function useful?
Seems like it could be useful, though probably few would actually use
it. (I've seen your talk on injecting context info into the exception
object... and see that that idiom may benefit. I don't know of many
other ways this would be used, though.)
Once you consider threads, a few minor problems arise. Clearly the
value would have to be in thread-specific-storage. However, the way
you describe using this (storing the initial value in an object's
constructor, and comparing with that to the current value in the
destructor) may not work if your object is used in different threads
as the values for different stacks would be reported. If you're
careful, I don't think this would be a real problem though. Just
always declare the object on the stack, and never copy it or pass its
address around, and threads won't be a problem.
> 2. Is this function easy to implement?
It seems like it would be straight forward. After completing the
evaluation of the object to be thrown, increment this number. After
completing initialization of the exception-declaration in the matching
handler, decrement the number. Then std::uncaught_exception could be
rewritten in terms of this number very easily.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
chris
|
7/29/2004 9:07:09 PM
|
|
"Alexander Terekhov" <terekhov@web.de> wrote in message
news:4108E978.C236261B@web.de...
> "Andrei Alexandrescu (See Website for Email)" wrote:
> [...]
> > that value with the current one. If the current one is greater than the
old
> > one, then the variable's scope is being exited via an exception.
>
> Extending C++ to allow destructors (and pre-destructos, of course)
> to have an optional bool "is_unwinding" argument is probably much
> better.
That's cool and would perfectly fit what I was thinking of doing - planting
objects on the stack that track the "health" of a system and log exceptional
behavior.
In my current framework, I can do that only if the state of the system is
initially "healthy". The logger object's constructor asserts that
std::uncaught_exception is false, and the destructor tests whether
std::uncaught_exception is true. That's the only way in which you can get
sensible information.
I have a macro EXECUTION_CONTEXT that can be used like this:
void UpdateDatabase() {
EXECUTION_CONTEXT("Updating Database");
... code ...
}
Now if an exception is thrown from within the function, the string will be
properly logged and nice error messages can be created, e.g.:
<<
Error: User's spouse cut access to funds
While: Performing ATM transaction at Fry's
While: Withdrawing funds
While: Updating Database
>>
The "While" lines reflect the salient places in the execution stack (where
the system authors planted EXECUTION_CONTEXTs). There is an increasing level
of detail. Without execution contexts, only the throw and catch sites can
issue messages:
<<
Error: User's spouse cut access to funds
While: Updating Database
>>
which gives a less informative error message (the lowest-level problem and
the highest-level context) while losing important information about the
dynamic trace that generated the exception.
> std::expected_exception<>() just ought to become standard
> as well. AFAIK, David Abrahams loves it. ;-)
For those as lazy as my usual self to google around, here's a link:
http://lists.boost.org/MailArchives/boost/msg53429.php
Not that I understood a lot :o). What's that?
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
7/29/2004 9:16:21 PM
|
|
Andrei Alexandrescu (See Website for Email) <SeeWebsiteForEmail@moderncppdesign.com> wrote:
> What I think would be good to have would be kind of an exception stack; that
> could be implemented but it's not easy.
>The next good thing to have is a simple unsigned int
> std::uncaught_exceptions() that tells how many exceptions are being thrown
> right now; ...
IOW, you propose that throwing an exception during stack unwinding should not cause terminate(), rather it should push the active exception in some kind of a stack, don't you?
This leads to questions as to whether the stack unwinding process should finish its job (invoking destructors) before the new exception is thrown or be postponed before the new exception is handled; or where the execution must continue after handling the active exception and popping the previous one from the stack; or which catch() blocks should be tryed for the previous exception - the next blocks or the same blocks which were tryed for the just handled exception?
Could you please elaborate?
--
Maxim Yegorushkin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Maxim
|
7/29/2004 9:19:39 PM
|
|
"Maxim Yegorushkin" <e-maxim@yandex.ru> wrote in message
news:opsbwn04azti5cme@my.ipcb.mos...
> Andrei Alexandrescu (See Website for Email)
<SeeWebsiteForEmail@moderncppdesign.com> wrote:
>
> > What I think would be good to have would be kind of an exception stack;
that
> > could be implemented but it's not easy.
> >The next good thing to have is a simple unsigned int
> > std::uncaught_exceptions() that tells how many exceptions are being
thrown
> > right now; ...
>
> IOW, you propose that throwing an exception during stack unwinding should
not cause terminate(), rather it should push the active exception in some
kind of a stack, don't you?
No. The model would be the same. I was just thinking that some access to the
exceptions that could possibly be rethrown would be nice. Consider:
try {
struct A {
~A() {
try {
B obj;
throw Exception2();
}
catch (...) {
}
}
} obj;
throw Exception1();
}
catch (...) {
}
When Exception1 is being thrown, A's destructor is invoked. Exception1 is
active. But A's destructor issues another try, so now Exception2 is active
on top of Exception1. I was saying that it would be nice to have access to
both, say, inside B's destructor.
> This leads to questions as to whether the stack unwinding process should
finish its job (invoking destructors) before the new exception is thrown or
be postponed before the new exception is handled; or where the execution
must continue after handling the active exception and popping the previous
one from the stack; or which catch() blocks should be tryed for the previous
exception - the next blocks or the same blocks which were tryed for the just
handled exception?
>
> Could you please elaborate?
Wow, my head is spinning :o). I just meant the above, no more. The model
wouldn't change, just access to what's going on would improve.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
7/30/2004 9:52:49 AM
|
|
* Andrei Alexandrescu (See Website for Email):
>
> That's cool and would perfectly fit what I was thinking of doing - planting
> objects on the stack that track the "health" of a system and log exceptional
> behavior.
>
> In my current framework, I can do that only if the state of the system is
> initially "healthy". The logger object's constructor asserts that
> std::uncaught_exception is false, and the destructor tests whether
> std::uncaught_exception is true. That's the only way in which you can get
> sensible information.
>
> I have a macro EXECUTION_CONTEXT that can be used like this:
>
> void UpdateDatabase() {
> EXECUTION_CONTEXT("Updating Database");
> ... code ...
> }
>
> Now if an exception is thrown from within the function, the string will be
> properly logged and nice error messages can be created, e.g.:
>
> <<
> Error: User's spouse cut access to funds
> While: Performing ATM transaction at Fry's
> While: Withdrawing funds
> While: Updating Database
> >>
>
> The "While" lines reflect the salient places in the execution stack (where
> the system authors planted EXECUTION_CONTEXTs). There is an increasing level
> of detail. Without execution contexts, only the throw and catch sites can
> issue messages:
>
> <<
> Error: User's spouse cut access to funds
> While: Updating Database
> >>
>
> which gives a less informative error message (the lowest-level problem and
> the highest-level context) while losing important information about the
> dynamic trace that generated the exception.
I gather that the problem might be that this doesn't work well in code
called from destructors, nor with a certain very popular compiler...
One solution within the current language and lib is to restrict code that would
like to track exceptions to Single Normal Exit behavior.
Then all that's needed is a statement before the end of the block, that
informs the tracker object that the block completed normally (otherwise
an exception can be assumed).
Hth.
- Alf
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
alfps
|
7/30/2004 9:53:38 AM
|
|
Maxim Yegorushkin wrote:
[...]
> Could you please elaborate?
Try
http://groups.google.com/groups?selm=3CBC5F22.76708D0%40web.de
(Subject: Re: Alexandrescu on error handling at ACCU conference 2002)
I mean:
---
throw 0
throw 1
throw 2
throw 3
throw 4
throw 5
throw 6
throw 7
throw 8
throw 9
Okay... ENOUGH active exceptions! ;-)
caught 9
caught 8
caught 7
caught 6
caught 5
caught 4
caught 3
caught 2
caught 1
caught 0
---
;-)
regards,
alexander.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alexander
|
7/30/2004 2:53:33 PM
|
|
"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
> http://lists.boost.org/MailArchives/boost/msg53429.php
>
> Not that I understood a lot :o). What's that?
Not going into details, it's mostly about how to fix exceptions
specifications and make it safe to call fclose() from C++ {pre}
destructors on a POSIX threaded system without wasting cycles on
cancel_off_guard a la
http://www.terekhov.de/DESIGN-futex-CV.cpp
---
~futex_condvar() {
mutex::guard guard(m_mutex);
assert(m_waiters[0] == m_wakeups);
while (m_waiters[0]) {
int ftx = m_futex = EOC();
mutex::release_guard release_guard(guard);
cancel_off_guard no_cancel;
^^^^^^^^^^^^^^^^^^^^^^^^^^
m_futex.wait(ftx);
}
}
---
Good old two-phase EH (also known as "condition handling" on MVS
[z/OS] and [Open]VMS, but without "restart" semantics ;-) ).
http://groups.google.com/groups?threadm=3EB82EA0.F40E66C4%40web.de
(Subject: __attribute__((cleanup(function)) versus try/finally)
regards,
alexander.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alexander
|
7/30/2004 2:55:20 PM
|
|
"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
> Wow, my head is spinning :o). I just meant the above, no more. The model
> wouldn't change, just access to what's going on would improve.
Yeah.
1. deprecate uncaught_exception()
2. mandate 2-phase EH and fix ES
3. size_t exception_scope() retuns 0 (no exceptions active) or scope
number
4. T * active_exception<T>(size_t scope = 0)" (scope zero means
current -- in effect, exception_scope()), works with void.
5. bool exception_caught<T>(T *) to check whether it is caught
e.g. if (std::exception_caught(std::active_exception<void>()))
we're inside catch handler.
or something like that.
regards,
alexander.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alexander
|
7/30/2004 3:26:09 PM
|
|
"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
> > Extending C++ to allow destructors (and pre-destructos, of course)
> > to have an optional bool "is_unwinding" argument is probably much
> > better.
>
> That's cool and would perfectly fit what I was thinking of doing - planting
> objects on the stack
^^^^^^^^^^^^^^^^^^^^
< dreams on, syntax/keywords aside >
template< context >
struct _Uninit_fill_cleanup {
typeof(context._First) m_Next;
_Uninit_fill_cleanup() :
m_Next(context._First) {
}
~_Uninit_fill_cleanup(bool unwinding) {
if (unwinding)
for (; context.m_First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}
};
template< class _FwdIt, class _Tval >
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val) {
_Uninit_fill_cleanup< context > _Cleanup;
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}
<dreams off>
Or things like that. Oder?
regards,
alexander.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alexander
|
7/30/2004 3:26:59 PM
|
|
"Andrei Alexandrescu (See Website for Email)" wrote:
> As Herb wrote in GotW and in Exceptional C++, std::uncaught_exception() is
> not a useful function because it can't be used to detect whether or not an
> exception was engendered in a particular scope.
>
IMHO std::uncaught_exception() is useless by definition rather than due its
return type.
The solution is destructor with in_stack_unwinding parameter:
class A {
// ...
~A(bool in_stack_unwinding) {
if (in_stack_unwinding) { /* ... */ }
else { /* ... */ }
}
};
Please refer to my old posting
http://groups.google.com/groups?selm=3BC7214E.CDD9CF8E%40iobox.com
And moreover, if all exceptions have common base class (virtual base,
actually) life becomes more interesting:
class A {
// ...
~A(const VBException* pe) {
if (pe) { /* use pe */ }
else { /* no exceptions */ }
}
};
> What I think would be good to have would be kind of an exception stack; that
> could be implemented but it's not easy.
>
An exception stack is already implemented in all recent C++ compilers, we can
use it right now.
For example, my Russian "Interfaces and Messages" tutorial
http://ders.angen.net/cpp3/intmsg/intmsg.html suggests to throw exceptions as
sh_ptr<CException>. That is the object is being thrown is always
sh_ptr<CException>.
So the following code:
-----------------------------------8<-----------------------------------
#include <stdio.h>
#include <vector>
#include "cexception.hpp"
using namespace std;
class ExampleCException : public CException { // CException as a base
protected:
ExampleCException(const FileLine& loc, const std::string& msg,
sh_ptr<CException> nest) : CException(loc, msg, nest) {}
public:
friend sh_ptr<CException> newExampleCException(const FileLine& loc,
const std::string& msg, sh_ptr<CException> nest=sh_ptr<CException>());
virtual std::string getClassName() const { return "ExampleCException"; }
};
inline sh_ptr<CException> newExampleCException(const CException::FileLine&
loc,
const std::string& msg, sh_ptr<CException> nest)
{
#ifndef DERS_RETHROW_BUG // usual implementation
return sh_ptr<CException>(new ExampleCException(loc, msg, nest));
#else // for broken compilers
return CException::current=sh_ptr<CException>(new ExampleCException(loc, msg,
nest));
#endif
}
int i;
void g()
{
try {
switch (i) {
case 1: throw newExampleCException(_FLINE_, "Hello from g()");
case 2: {
vector<int> v;
v.at(0);
}
case 3: throw 3;
}
}
catch (...) {
throw newCException(_FLINE_, "Problems in g()", toCException(_FLINE_));
}
}
void f()
{
try { g(); }
catch (...) {
throw newCException(_FLINE_, "Problems in f()", toCException(_FLINE_));
}
}
int main()
{
for (i=1; i<=4; i++) {
try {
if (i<4) f();
else throw 4;
}
catch (...) {
printf("\tException #%d:\n%s", i,
toCException(_FLINE_)->toStringAll().c_str());
}
}
}
-----------------------------------8<-----------------------------------
produces the following output:
Exception #1:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
ExampleCException [ceexample.cpp:64]: Hello from g()
Exception #2:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
STDExternalCException [ceexample.cpp:81], typeName="St12out_of_range",
message="vector"
Exception #3:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
UnknownExternalCException [ceexample.cpp:81], typeName="unknown",
message="Unknown exception"
Exception #4:
UnknownExternalCException [ceexample.cpp:107], typeName="unknown",
message="Unknown exception"
The code is available here: http://ders.angen.net/cpp3/intmsg/code.zip
Note, only one catch(...) per function can be used.
And old constructions like this:
void f()
{
try { g(); }
catch (const ExampleCException&) {} // ignore ExampleCException
catch (...) { throw; } // rethrow all the others
}
can be rewritten in the following way:
void f()
{
try { g(); }
catch (...) {
sh_ptr<CException> ce=toCException(_FLINE_);
if (ce->is<ExampleCException>()) {} // ignore ExampleCException
else { throw; } // rethrow all the others
}
}
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Sergey
|
7/30/2004 3:35:19 PM
|
|
On 30 Jul 2004 11:35:19 -0400, "Sergey P. Derevyago"
<non-existent@iobox.com> wrote:
> IMHO std::uncaught_exception() is useless by definition rather than due its
>return type.
> The solution is destructor with in_stack_unwinding parameter:
>
> class A {
> // ...
> ~A(bool in_stack_unwinding) {
> if (in_stack_unwinding) { /* ... */ }
> else { /* ... */ }
> }
> };
>
>Please refer to my old posting
>http://groups.google.com/groups?selm=3BC7214E.CDD9CF8E%40iobox.com
And see Item 19 in More Exceptional C++ for why this has the same problems
and why this particular example doesn't actually do what you think (for
most motivations to use this idiom).
Herb
---
Herb Sutter (www.gotw.ca)
Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Visual C++ architect, Microsoft (www.gotw.ca/microsoft)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Herb
|
7/31/2004 3:47:13 AM
|
|
Herb Sutter wrote:
[...]
> > ~A(bool in_stack_unwinding) {
> > if (in_stack_unwinding) { /* ... */ }
> > else { /* ... */ }
> > }
> > };
> >
> >Please refer to my old posting
> >http://groups.google.com/groups?selm=3BC7214E.CDD9CF8E%40iobox.com
>
> And see Item 19 in More Exceptional C++ for why this has the same problems
> and why this particular example doesn't actually do what you think (for
> most motivations to use this idiom).
Nobody has said anything about throwing destructors. Destructors
shall have implicit throw()-nothing ES ("fixed edition") by default
imposed on them. And it wouldn't make {pre}~T(bool) less useful
(for most motivations to use this idiom ;-) ).
regards,
alexander.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alexander
|
7/31/2004 6:48:37 PM
|
|
Alexander Terekhov wrote:
> "Andrei Alexandrescu (See Website for Email)" wrote:
> [...]
>> Wow, my head is spinning :o). I just meant the above, no more. The model
>> wouldn't change, just access to what's going on would improve.
>
> Yeah.
>
> 1. deprecate uncaught_exception()
>
> 2. mandate 2-phase EH and fix ES
>
> 3. size_t exception_scope() retuns 0 (no exceptions active) or scope
> number
>
> 4. T * active_exception<T>(size_t scope = 0)" (scope zero means
> current -- in effect, exception_scope()), works with void.
>
> 5. bool exception_caught<T>(T *) to check whether it is caught
> e.g. if (std::exception_caught(std::active_exception<void>()))
> we're inside catch handler.
>
> or something like that.
Now all you need to do is to make that into a proposal anbd present it in
Redmond. :-) I am serious.
--
WW aka Attila
:::
Of course I don't look busy.....I did it right the first time!
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
White
|
8/1/2004 2:14:32 AM
|
|
Alexander Terekhov wrote:
[SNIP]
> Nobody has said anything about throwing destructors. Destructors
> shall have implicit throw()-nothing ES ("fixed edition") by default
> imposed on them. And it wouldn't make {pre}~T(bool) less useful
> (for most motivations to use this idiom ;-) ).
Except (AFAIU) that once you are in a catch block you are not in stack
unwinding. But that catch block may still re-throw, and so it goes.
Meaning that the fact that the code is not in stack-unwinding it does not
mean that it is not really in stack-unwinding. ;-)
Of course, for Andrei's idea this does not matter, since anything created
and destroyed inside a catch-block (a handler) is not part of the original
execution context, therefore it is not ought to report about its
destruction...
I would also like to note, that the above idea is nice and dandy, except if
the original throw was reporting about a reosurce shortage, in which case
this nice idea about reporting execution contexts will not work. Unless we
prepare the report _before_ we know it will be needed. But that does not
seem to be a good idea.
--
WW aka Attila
:::
Black holes suck.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
White
|
8/1/2004 2:15:01 AM
|
|
Alf P. Steinbach wrote:
> * Andrei Alexandrescu (See Website for Email):
>>
>> The next good thing to have is a simple unsigned int
>> std::uncaught_exceptions() that tells how many exceptions are being
>> thrown right now; that way, a class' constructor can store the current
>> value returned by std::uncaught_exceptions, and then the destructor can
>> compare that value with the current one. If the current one is greater
>> than the old one, then the variable's scope is being exited via an
>> exception.
>>
>> So I'd like to ask:
>>
>> 1. Would others find such a function useful?
>
> It would be interesting to know what _a_ usage could be.
>
> Say a destructor encounters a problem.
>
> If the destructor is not invoked by exception-caused stack unwinding,
> then it could conceivably report the problem via an exception.
>
> But if it is invoked that way, then it would have to use some
> alternative.
>
> And given that this alternative is built into the destructor anyway,
> why not use that in both cases?
May I quote Herb Sutter here (as it seems to be customary): "C'mon buddy!
Pick a lane! Any lane!" :-)
My quote is from More Exception C++, so I've already won, because my book is
mooore than your book! ;-) Anyways, the point is:
Item 19, More Exceptional C++ by Herb Sutter
"The second and more fundamental problem with this solution is not
technical, but moral. It is a poor design to give T::~T() two different
modes of operation for its error reporting semantics."
Please read the rest in the book! The point briefly is that C++ is designed
to be a rather male language, with boring and predictable behavior. That is
of course only my interpretation.
However the idea of making destructors behave differently under different
circumstances raises more than moral issues. It also triggers my Vulcan
genes, and strikes me as not logical. It is either an error or not. If it
is not (can be ignored) then why would you bother to throw an exception? If
it is an error, then how do you expect the error recovery work, when you are
unable to roll back?
My point being: if you have a system, where neither commit nor rollback of a
transaction is failsafe (under normal circumstances) you have bigger
troubles than crashes due to two active exceptions.
Last but not least I did not write this because I thought that any of you
(Alf or Andrei) suggested this behavior. I just felt that it might make
sense to clarify the situation so that others do not misunderstand it
either.
--
WW aka Attila
:::
A closed mouth gathers no feet.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
White
|
8/1/2004 2:15:30 AM
|
|
Herb Sutter wrote:
> >Please refer to my old posting
> >http://groups.google.com/groups?selm=3BC7214E.CDD9CF8E%40iobox.com
>
> And see Item 19 in More Exceptional C++ for why this has the same problems
> and why this particular example doesn't actually do what you think (for
> most motivations to use this idiom).
>
"you never know what people might know" Bjarne Stroustrup
Actually, I don't think that destructors must be allowed to throw exceptions,
so it seems like this reference to the Item 19 is rather irrelevant :)
Also the most interesting part of my message is "exception objects are
(shared) smart pointers" idiom. Could you please provide some comments on this
topic?
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Sergey
|
8/1/2004 2:19:31 AM
|
|
Herb Sutter <hsutter@gotw.ca> wrote in message
> On 30 Jul 2004 11:35:19 -0400, "Sergey P. Derevyago" wrote:
> > The solution is destructor with in_stack_unwinding parameter:
> >
> > class A {
> > // ...
> > ~A(bool in_stack_unwinding) {
> > if (in_stack_unwinding) { /* ... */ }
> > else { /* ... */ }
> > }
> And see Item 19 in More Exceptional C++ for why this has the same problems
> and why this particular example doesn't actually do what you think (for
> most motivations to use this idiom).
I think it depends on exactly what condition sets the variable to 'true'.
If true only when the dtor is invoked by the stack unwinding code then
it would be of some use.
Roger Orr
--
MVP in C++ at www.brainbench.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
rogero
|
8/1/2004 2:22:50 AM
|
|
"Alexander Terekhov" <terekhov@web.de> wrote in message
news:410BA7EC.56EA3F6A@web.de...
> Nobody has said anything about throwing destructors. Destructors
> shall have implicit throw()-nothing ES ("fixed edition") by default
> imposed on them. And it wouldn't make {pre}~T(bool) less useful
> (for most motivations to use this idiom ;-) ).
Indeed. A very good motivation would be to avoid having to call Dismiss() in
ScopeGuard. If ScopeGuard could detect that the scope is exited through an
exception, it knew what it has to do.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
8/1/2004 11:03:08 AM
|
|
"Roger Orr" <rogero@howzatt.demon.co.uk> wrote in message
news:7a474e76.0407311034.67f805e9@posting.google.com...
> Herb Sutter <hsutter@gotw.ca> wrote in message
> > On 30 Jul 2004 11:35:19 -0400, "Sergey P. Derevyago" wrote:
> > > The solution is destructor with in_stack_unwinding parameter:
> > >
> > > class A {
> > > // ...
> > > ~A(bool in_stack_unwinding) {
> > > if (in_stack_unwinding) { /* ... */ }
> > > else { /* ... */ }
> > > }
>
> > And see Item 19 in More Exceptional C++ for why this has the same
problems
> > and why this particular example doesn't actually do what you think (for
> > most motivations to use this idiom).
>
> I think it depends on exactly what condition sets the variable to 'true'.
> If true only when the dtor is invoked by the stack unwinding code then
> it would be of some use.
Great point. Again, if the bool is set to true if A's particular scope is
exited via an exception, and if you replace A with ScopeGuard, you can have
ScopeGuard properly call its guarding function, otherwise it does nothing.
This has no interaction with whether there are nested try's etc.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
8/1/2004 11:03:41 AM
|
|
"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> writes:
| As Herb wrote in GotW and in Exceptional C++, std::uncaught_exception() is
| not a useful function because it can't be used to detect whether or not an
| exception was engendered in a particular scope.
|
| What I think would be good to have would be kind of an exception stack; that
| could be implemented but it's not easy.
|
| The next good thing to have is a simple unsigned int
| std::uncaught_exceptions() that tells how many exceptions are being thrown
| right now; that way, a class' constructor can store the current value
| returned by std::uncaught_exceptions, and then the destructor can compare
| that value with the current one. If the current one is greater than the old
| one, then the variable's scope is being exited via an exception.
|
| So I'd like to ask:
|
| 1. Would others find such a function useful?
Without clear statements and examples of the problems such function
would solve and the kind of programming style it supports, it is hard
for me to say anything about its usefulness :-)
--
Gabriel Dos Reis
gdr@integrable-solutions.net
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Gabriel
|
8/1/2004 5:10:20 PM
|
|
"Gabriel Dos Reis" <gdr@integrable-solutions.net> wrote in message
news:m3vfg2j11w.fsf@uniton.integrable-solutions.net...
> Without clear statements and examples of the problems such function
> would solve and the kind of programming style it supports, it is hard
> for me to say anything about its usefulness :-)
A couple of examples have been given already. ScopeGuard-style
(http://www.cuj.com/experts/1812/alexandr.htm) objects would be my favorite.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
8/2/2004 10:11:50 AM
|
|
On 1 Aug 2004 07:03:41 -0400, "Andrei Alexandrescu \(See Website for
Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote:
>"Roger Orr" <rogero@howzatt.demon.co.uk> wrote in message
>news:7a474e76.0407311034.67f805e9@posting.google.com...
> > Herb Sutter <hsutter@gotw.ca> wrote in message
> > > On 30 Jul 2004 11:35:19 -0400, "Sergey P. Derevyago" wrote:
> > > > The solution is destructor with in_stack_unwinding parameter:
> > > >
> > > > class A {
> > > > // ...
> > > > ~A(bool in_stack_unwinding) {
> > > > if (in_stack_unwinding) { /* ... */ }
> > > > else { /* ... */ }
> > > > }
> >
> > > And see Item 19 in More Exceptional C++ for why this has the same
>problems
> > > and why this particular example doesn't actually do what you think (for
> > > most motivations to use this idiom).
> >
> > I think it depends on exactly what condition sets the variable to 'true'.
> > If true only when the dtor is invoked by the stack unwinding code then
> > it would be of some use.
>
>Great point. Again, if the bool is set to true if A's particular scope is
>exited via an exception, and if you replace A with ScopeGuard, you can have
>ScopeGuard properly call its guarding function, otherwise it does nothing.
I'm probably still missing the point because I jumped in partway through.
Q: What do you propose the result of the test be in the following case
that arranges to call ~A during stack unwinding? And is the bool variable
set automatically by the compiler/runtime or by manual user intervention?
class U {
public:
~U() {
try {
A a;
// do work
}
catch( ... ) {
// clean up
}
}
};
int main() {
try {
U u;
throw 1;
}
catch(...) {
}
}
Herb
---
Herb Sutter (www.gotw.ca)
Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Visual C++ architect, Microsoft (www.gotw.ca/microsoft)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Herb
|
8/2/2004 10:20:31 AM
|
|
On 31 Jul 2004 22:19:31 -0400, "Sergey P. Derevyago"
<non-existent@iobox.com> wrote:
> Actually, I don't think that destructors must be allowed to throw exceptions,
>so it seems like this reference to the Item 19 is rather irrelevant :)
My question (just posted elsewhere in the thread) is under what
circumsances the proposal would make the test evaluate to true.
One common use that I've seen for wanting to do this is to choose between
throwing vs. reporting an error some other way (or not at all), but that's
only one. Another common use, also mentioned in MXC++ Item 19, is a
Transaction class whose destructor automatically does a RollBack if
uncaught_exception is true when the destructor runs.
So is this proposed test different enough from uncaught_exception to make
these examples work correctly in the problem case described in Item 19
(and just posted in a sister post in this thread)?
> Also the most interesting part of my message is "exception objects are
>(shared) smart pointers" idiom. Could you please provide some comments on this
>topic?
I didn't read the links or the code. Quick thoughts on the two-line
description above: That can have benefits, but wouldn't one drawback be
that it requires dynamic allocation (of the exception object)? Less
importantly, it requires catch sites to catch(shared_ptr<SomeExType>)
which can be a bit verbose. Just some quick ideas off the cuff; I haven't
stopped to give it actual thought. There could be other killer
advantages/drawbacks.
Herb
---
Herb Sutter (www.gotw.ca)
Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Visual C++ architect, Microsoft (www.gotw.ca/microsoft)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Herb
|
8/2/2004 10:21:29 AM
|
|
"Herb Sutter" <hsutter@gotw.ca> wrote in message
news:mjsqg0lr6g8r8mken218hvil5o03fr2arm@4ax.com...
> On 1 Aug 2004 07:03:41 -0400, "Andrei Alexandrescu \(See Website for
> Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote:
> >Great point. Again, if the bool is set to true if A's particular scope
is
> >exited via an exception, and if you replace A with ScopeGuard, you can
have
> >ScopeGuard properly call its guarding function, otherwise it does
nothing.
>
> I'm probably still missing the point because I jumped in partway through.
> Q: What do you propose the result of the test be in the following case
> that arranges to call ~A during stack unwinding? And is the bool variable
> set automatically by the compiler/runtime or by manual user intervention?
>
> class U {
> public:
> ~U() {
> try {
> A a;
> // do work
> }
> catch( ... ) {
> // clean up
> }
> }
> };
>
> int main() {
> try {
> U u;
> throw 1;
> }
> catch(...) {
> }
> }
It's very simple, really. First, it's the runtime system that takes care of
passing the appropriate Boolean value to the destructors.
Second, the useful semantics would be:
* If the code "// do work" does NOT thrown an exception, ~A(false) is
invoked when A's scope is exited.
* If the code "// do work" DOES throw an exception, ~A(true) is invoked when
A's scope is exited.
A simple way to put it is, "For an automatic object obj of type T,
obj.~T(true) is invoked iff obj's scope is exited via an exception thrown
after obj was constructed". I think having such a piece of information would
be very useful.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
8/3/2004 11:36:22 AM
|
|
"Andrei Alexandrescu \(See Website for Email\)"
<SeeWebsiteForEmail@moderncppdesign.com> wrote i
> A simple way to put it is, "For an automatic object obj of type T,
> obj.~T(true) is invoked iff obj's scope is exited via an exception thrown
> after obj was constructed". I think having such a piece of information would
> be very useful.
Another way to put it -- less precise but possibly more intuititve --
might be, "...is invoked iff obj's lifetime is ending *because of*
stack unwinding, rather than simply *during* stack unwinding."
std::uncaught_exception tell us "during," which is sometimes
misinterpreted as "because of."
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
johnchx2
|
8/4/2004 12:05:52 PM
|
|
Andrei Alexandrescu (See Website for Email) wrote:
> [...]
> Second, the useful semantics would be:
>
> * If the code "// do work" does NOT thrown an exception, ~A(false) is
> invoked when A's scope is exited.
>
> * If the code "// do work" DOES throw an exception, ~A(true) is invoked when
> A's scope is exited.
>
> A simple way to put it is, "For an automatic object obj of type T,
> obj.~T(true) is invoked iff obj's scope is exited via an exception thrown
> after obj was constructed". I think having such a piece of information would
> be very useful.
What happens when a user needs to call the d'tor explicitly? Do they
get to pass the bool param or not? And if not, would this be the first
time that a C++ function is getting called with a different number of
arguments than is declared?
Dave
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
David
|
8/4/2004 12:09:48 PM
|
|
"David B. Held" wrote:
[...]
> What happens when a user needs to call the d'tor explicitly? Do they
> get to pass the bool param or not?
They can and "pT->~T(std::rand());" shall be well-formed even
as pseudo-destructor call, of course. Compiler shall simply
silently ignore the argument for {pre}destructors in the
"classic" (bool-less) form.
> And if not, would this be the first
> time that a C++ function is getting called with a different number of
> arguments than is declared?
That is sorta true (see above). That bool parameter should have
implicit "= false" default argument (in addition to implicit
throw() ES ;-) ). So that you can have only one {pre}destructor;
either with or without bool, but not both forms. Oder?
regards,
alexander.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alexander
|
8/4/2004 1:58:24 PM
|
|
"David B. Held" <dheld@codelogicconsulting.com> wrote in message
news:ceoi8r$pcd$1@news.astound.net...
> Andrei Alexandrescu (See Website for Email) wrote:
> > [...]
> > Second, the useful semantics would be:
> >
> > * If the code "// do work" does NOT thrown an exception, ~A(false) is
> > invoked when A's scope is exited.
> >
> > * If the code "// do work" DOES throw an exception, ~A(true) is invoked
when
> > A's scope is exited.
> >
> > A simple way to put it is, "For an automatic object obj of type T,
> > obj.~T(true) is invoked iff obj's scope is exited via an exception
thrown
> > after obj was constructed". I think having such a piece of information
would
> > be very useful.
>
> What happens when a user needs to call the d'tor explicitly? Do they
> get to pass the bool param or not? And if not, would this be the first
> time that a C++ function is getting called with a different number of
> arguments than is declared?
Two words: new-expression. Two more words: delete-expression.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
8/5/2004 3:15:21 AM
|
|
On 2004-08-04 13:58, Alexander Terekhov wrote:
> "David B. Held" wrote:
> [...]
>> What happens when a user needs to call the d'tor explicitly? Do they
>> get to pass the bool param or not? And if not, would this be the first
>> time that a C++ function is getting called with a different number of
>> arguments than is declared?
>
> That is sorta true (see above). That bool parameter should have
> implicit "= false" default argument (in addition to implicit
I believe that the default should be 'true', since this is what
destructors currently have to assume (i.e. worst-case).
> throw() ES ;-) ). So that you can have only one {pre}destructor;
> either with or without bool, but not both forms. Oder?
Having two desctructors (one with bool parameter and one whithout)
instead of one destructor with a default argument would have the
benefit of facilitating/enabling binary compatibility with existing
client code that expects to call a no-arg destructor.
-- Niklas Matthies
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Niklas
|
8/5/2004 3:18:23 AM
|
|
On 2004-08-04 12:09, David B. Held wrote:
> Andrei Alexandrescu (See Website for Email) wrote:
>> [...]
>> Second, the useful semantics would be:
>>
>> * If the code "// do work" does NOT thrown an exception, ~A(false) is
>> invoked when A's scope is exited.
>>
>> * If the code "// do work" DOES throw an exception, ~A(true) is invoked when
>> A's scope is exited.
>>
>> A simple way to put it is, "For an automatic object obj of type T,
>> obj.~T(true) is invoked iff obj's scope is exited via an exception thrown
>> after obj was constructed". I think having such a piece of information would
>> be very useful.
>
> What happens when a user needs to call the d'tor explicitly? Do they
> get to pass the bool param or not?
A backward-compatible scheme would be to have both the bool destructor
and the traditional no-arg destructor as overloads, where if one of
them is defined, the compiler-generated default implementation of the
other is
~A() { this->~A(true); }
or
~A(bool b) { this->~A(); }
respectively.
-- Niklas Matthies
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Niklas
|
8/5/2004 3:23:10 AM
|
|
Herb Sutter wrote:
> > Actually, I don't think that destructors must be allowed to throw exceptions,
> >so it seems like this reference to the Item 19 is rather irrelevant :)
>
> My question (just posted elsewhere in the thread) is under what
> circumsances the proposal would make the test evaluate to true.
>
IMHO the answer is obvious: in_stack_unwinding parameter is true if
destructor is called by runtime during the process of stack unwinding
(15.2p3).
> One common use that I've seen for wanting to do this is to choose between
> throwing vs. reporting an error some other way (or not at all), but that's
> only one.
>
IMHO destructors must not be allowed to throw exceptions.
> Another common use, also mentioned in MXC++ Item 19, is a
> Transaction class whose destructor automatically does a RollBack if
> uncaught_exception is true when the destructor runs.
>
Yes, in_stack_unwinding value is useful in this case.
> > Also the most interesting part of my message is "exception objects are
> >(shared) smart pointers" idiom. Could you please provide some comments on this
> >topic?
>
> I didn't read the links or the code.
>
Well, the following snippet shows some points:
void g()
{
try {
switch (i) {
case 1: throw newExampleCException(_FLINE_, "Hello from g()");
case 2: {
vector<int> v;
v.at(0);
}
case 3: throw 3;
}
}
catch (...) {
throw newCException(_FLINE_, "Problems in g()", toCException(_FLINE_));
}
}
void f()
{
try { g(); }
catch (...) {
throw newCException(_FLINE_, "Problems in f()", toCException(_FLINE_));
}
}
Exception #1:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
ExampleCException [ceexample.cpp:64]: Hello from g()
Exception #2:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
STDExternalCException [ceexample.cpp:81], typeName="St12out_of_range",
message="vector"
Exception #3:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
UnknownExternalCException [ceexample.cpp:81], typeName="unknown",
message="Unknown exception"
> Quick thoughts on the two-line
> description above: That can have benefits, but wouldn't one drawback be
> that it requires dynamic allocation (of the exception object)?
>
IMHO it's not a drawback.
> Less importantly, it requires catch sites to catch(shared_ptr<SomeExType>)
> which can be a bit verbose.
>
Good joke, thank you.
> Just some quick ideas off the cuff; I haven't
> stopped to give it actual thought. There could be other killer
> advantages/drawbacks.
>
And here are some important advantages:
1. Copy constructor of the object being thrown doesn't throw exceptions. I.e.
no std::terminate() during the copying (15.5.1p1).
2. Any MyException object can contain a lot of data and these data members
are allowed to throw during both the construction and copying: no
std::terminate() will be called because the actual object is being thrown is
sh_ptr<CException> and MyException is derived from CException.
P.S. There is some substantial disadvantage, though. Generally speaking,
reference counting is not MT-friendly, so in the case of my simple sh_ptr<>
parallel copying of the same sh_ptr object in different threads must be
avoided. But some other implementation dependant techniques could be applied
to make shared smart pointer both MT-safe and fast enough.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Sergey
|
8/5/2004 2:02:46 PM
|
|
|
33 Replies
77 Views
(page loaded in 0.277 seconds)
Similiar Articles:7/22/2012 1:35:07 PM
|