Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

unsigned int std::uncaught_exceptions()

41 views
Skip to first unread message

Andrei Alexandrescu (See Website for Email)

unread,
Jul 29, 2004, 6:29:18 AM7/29/04
to
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! ]

Alf P. Steinbach

unread,
Jul 29, 2004, 11:18:32 AM7/29/04
to
* 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?

Alexander Terekhov

unread,
Jul 29, 2004, 11:19:44 AM7/29/04
to

"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.

Chris Uzdavinis

unread,
Jul 29, 2004, 5:07:09 PM7/29/04
to
> 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.

Andrei Alexandrescu (See Website for Email)

unread,
Jul 29, 2004, 5:16:21 PM7/29/04
to
"Alexander Terekhov" <tere...@web.de> wrote in message
news:4108E978...@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

Maxim Yegorushkin

unread,
Jul 29, 2004, 5:19:39 PM7/29/04
to
Andrei Alexandrescu (See Website for Email) <SeeWebsit...@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

Andrei Alexandrescu (See Website for Email)

unread,
Jul 30, 2004, 5:52:49 AM7/30/04
to
"Maxim Yegorushkin" <e-m...@yandex.ru> wrote in message
news:opsbwn04...@my.ipcb.mos...

> Andrei Alexandrescu (See Website for Email)
<SeeWebsit...@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

Alf P. Steinbach

unread,
Jul 30, 2004, 5:53:38 AM7/30/04
to
* Andrei Alexandrescu (See Website for Email):

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 ]

Alexander Terekhov

unread,
Jul 30, 2004, 10:53:33 AM7/30/04
to

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.

Alexander Terekhov

unread,
Jul 30, 2004, 10:55:20 AM7/30/04
to

"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.

Alexander Terekhov

unread,
Jul 30, 2004, 11:26:09 AM7/30/04
to

"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.

Alexander Terekhov

unread,
Jul 30, 2004, 11:26:59 AM7/30/04
to

"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.

Sergey P. Derevyago

unread,
Jul 30, 2004, 11:35:19 AM7/30/04
to
"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

Herb Sutter

unread,
Jul 30, 2004, 11:47:13 PM7/30/04
to
On 30 Jul 2004 11:35:19 -0400, "Sergey P. Derevyago"

<non-ex...@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)

Alexander Terekhov

unread,
Jul 31, 2004, 2:48:37 PM7/31/04
to

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.

White Wolf

unread,
Jul 31, 2004, 10:14:32 PM7/31/04
to
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!

White Wolf

unread,
Jul 31, 2004, 10:15:01 PM7/31/04
to
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.

White Wolf

unread,
Jul 31, 2004, 10:15:30 PM7/31/04
to
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.

Sergey P. Derevyago

unread,
Jul 31, 2004, 10:19:31 PM7/31/04
to
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

Roger Orr

unread,
Jul 31, 2004, 10:22:50 PM7/31/04
to
Herb Sutter <hsu...@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

Andrei Alexandrescu (See Website for Email)

unread,
Aug 1, 2004, 7:03:08 AM8/1/04
to
"Alexander Terekhov" <tere...@web.de> wrote in message
news:410BA7EC...@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

Andrei Alexandrescu (See Website for Email)

unread,
Aug 1, 2004, 7:03:41 AM8/1/04
to
"Roger Orr" <rog...@howzatt.demon.co.uk> wrote in message
news:7a474e76.04073...@posting.google.com...

> Herb Sutter <hsu...@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

Gabriel Dos Reis

unread,
Aug 1, 2004, 1:10:20 PM8/1/04
to
"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsit...@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
g...@integrable-solutions.net

Andrei Alexandrescu (See Website for Email)

unread,
Aug 2, 2004, 6:11:50 AM8/2/04
to
"Gabriel Dos Reis" <g...@integrable-solutions.net> wrote in message
news:m3vfg2j...@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

Herb Sutter

unread,
Aug 2, 2004, 6:20:31 AM8/2/04
to
On 1 Aug 2004 07:03:41 -0400, "Andrei Alexandrescu \(See Website for

Email\)" <SeeWebsit...@moderncppdesign.com> wrote:
>"Roger Orr" <rog...@howzatt.demon.co.uk> wrote in message
>news:7a474e76.04073...@posting.google.com...
> > Herb Sutter <hsu...@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 ]

Herb Sutter

unread,
Aug 2, 2004, 6:21:29 AM8/2/04
to
On 31 Jul 2004 22:19:31 -0400, "Sergey P. Derevyago"

<non-ex...@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 ]

Andrei Alexandrescu (See Website for Email)

unread,
Aug 3, 2004, 7:36:22 AM8/3/04
to
"Herb Sutter" <hsu...@gotw.ca> wrote in message
news:mjsqg0lr6g8r8mken...@4ax.com...

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

johnchx

unread,
Aug 4, 2004, 8:05:52 AM8/4/04
to
"Andrei Alexandrescu \(See Website for Email\)"
<SeeWebsit...@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."

David B. Held

unread,
Aug 4, 2004, 8:09:48 AM8/4/04
to
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

Alexander Terekhov

unread,
Aug 4, 2004, 9:58:24 AM8/4/04
to

"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.

Andrei Alexandrescu (See Website for Email)

unread,
Aug 4, 2004, 11:15:21 PM8/4/04
to
"David B. Held" <dh...@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

Niklas Matthies

unread,
Aug 4, 2004, 11:18:23 PM8/4/04
to
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

Niklas Matthies

unread,
Aug 4, 2004, 11:23:10 PM8/4/04
to
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

Sergey P. Derevyago

unread,
Aug 5, 2004, 10:02:46 AM8/5/04
to
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

David Abrahams

unread,
Aug 24, 2008, 11:02:21 PM8/24/08
to

on Thu Aug 07 2008, "Sergey P. Derevyago" <non-existent-AT-iobox.com> wrote:

> Long ago I've posted some messages on this topic, in particular this one:
> Message-ID: <410A48AB...@iobox.com>
> http://groups.google.com/group/comp.lang.c++.moderated/msg/41de6076288f7c89

I've never understood how your solution addresses the problem.

In the above posting, you write:

> The solution is destructor with in_stack_unwinding parameter:
>
> class A {
> // ...
> ~A(bool in_stack_unwinding) {
> if (in_stack_unwinding) { /* ... */ }
> else { /* ... */ }
> }
> };
>

That appears to only change the interface, but not address the semantic
issues with uncaught_exception. More specifically:

under what conditions will in_stack_unwinding be true above?

If that bool has the same semantics as uncaught_exception(), then all
the problems described in http://www.gotw.ca/gotw/047.htm still apply
(let's ignore the "morality" argument given there; I object to the whole
approach).

If you're suggesting semantics equivalent to "is it safe to throw," I
don't _think_ that's actually implementable.

> But there exist some points that deserve C++ specific attention. And one
> of these points is "exceptions being thrown as smart pointers to actual
> exception objects".
>
> In particular, the object is being thrown is always sh_ptr<Exception>:
> typedef sh_ptr<Exception> shException;
> See http://ders.stml.net/cpp/mtprog/doc/exception_8hpp-source.html for
> the details.

Can you give a summary of what you achieve by throwing smart pointers?

> There also can be nested exceptions and used like this:
>
> void f(mem_pool& mp, int i)
> {
> shException exc(mp, 0);
> try {
> g(mp, i);
> return;
> }
> catch (shException she) { if (she->is<MsgException>()) throw; else
> exc=she; }
> catch (...) { exc=recatchException(mp, _FLINE_); } // recatch!
>
> throw newException(_FLINE_, "Problems in f()", exc); // use as nested!
> }
>
> And you can browse the documentation to quickly get the remaining
> details: http://ders.stml.net/cpp/mtprog/doc/exception_8cpp-source.html

I'm sorry, but that simply seems to link to an HTML-ized copy of the
code, and I don't have the time to try to analyze your code to figure
out what you're trying to accomplish. Summarizing the problem you're
trying to solve, and how your code solves the problem, would help lots.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Sergey P. Derevyago

unread,
Aug 25, 2008, 9:58:37 AM8/25/08
to
David Abrahams wrote:
>> class A {
>> // ...
>> ~A(bool in_stack_unwinding) {
>> if (in_stack_unwinding) { /* ... */ }
>> else { /* ... */ }
>> }
>> };
>>
>
> That appears to only change the interface, but not address the semantic
> issues with uncaught_exception. More specifically:
>
> under what conditions will in_stack_unwinding be true above?
>
Please refer to 15.2p3: The process of calling destructors for
automatic objects constructed on the path from a try block to a
throw-expression is called "stack unwinding."
I.e. in_stack_unwinding destructor argument is true if the
destructor
was called during the stack unwinding. According to 15.2 if
(in_stack_unwinding) then an exception can leave the destructor without
a terminate().

> If you're suggesting semantics equivalent to "is it safe to throw," I
> don't _think_ that's actually implementable.
>

But it IS.
The EH-support runtime calls a destructor with
in_stack_unwinding==true
argument so this destructor does really know whether it's safe to throw
exceptions.

> Can you give a summary of what you achieve by throwing smart pointers?
>

1. Smart pointers are cheap to copy and the throwing/catching
exceptions supposes copying. So you can create arbitrary big Exception
objects, throw them as sh_ptr<Exception>(/* ... */) and they will be
copied almost for free.
2. sh_ptr copy constructor doesn't throw exceptions so there is no
chance to get terminate() during the copying of sh_ptr<Exception>(). On
the contrary, Exception copy constructor does really throw exceptions so
its copying is expensive and can lead to terminate().
3. It's really easy to create the chains of the nested Exception
objects.
4. Having recatchException() function you can use the following
uniform
exception handler in almost all of the places:

void f()
{
try {
// ...
g();
// ...

return;
}
catch (...) {


throw newException(_FLINE_, "Problems in f()",

recatchException(mp, _FLINE_));
}
}
--
With all respect, Sergey. http://ders.stml.net/
mailto : ders at skeptik.net

--

David Abrahams

unread,
Aug 25, 2008, 11:22:19 PM8/25/08
to

on Mon Aug 25 2008, "Sergey P. Derevyago" <non-existent-AT-iobox.com> wrote:

> David Abrahams wrote:
>>> class A {
>>> // ...
>>> ~A(bool in_stack_unwinding) {
>>> if (in_stack_unwinding) { /* ... */ }
>>> else { /* ... */ }
>>> }
>>> };
>>>
>>
>> That appears to only change the interface, but not address the semantic
>> issues with uncaught_exception. More specifically:
>>
>> under what conditions will in_stack_unwinding be true above?
>>
> Please refer to 15.2p3: The process of calling destructors for
> automatic objects constructed on the path from a try block to a
> throw-expression is called "stack unwinding."
> I.e. in_stack_unwinding destructor argument is true if the
> destructor was called during the stack unwinding. According to 15.2 if
> (in_stack_unwinding) then an exception can leave the destructor
> without a terminate().

Then that's still not very useful, because the dtor might be called from
inside a catch block where the programmer needs non-throwing operations,
and in_stack_unwinding would be false. A catch block where there is a
final "throw;" is often written as though it were the semantic
equivalent of a dtor; the only difference of course is what happens when
you get another exception, and most programs simply outlaw that
possibility.

>> If you're suggesting semantics equivalent to "is it safe to throw," I
>> don't _think_ that's actually implementable.
>>
> But it IS.

I don't see how. The information about what is "safe" is not encoded in
the program text; it's a function of the semantics the programmer is
trying to achieve.

Basically, it's like this: if you define "safe" to be something that can
be known by the compiler, I claim it's not a useful definition, and if
you define "safe" in a useful way, I claim it's not implementable :-)

> The EH-support runtime calls a destructor with
> in_stack_unwinding==true argument so this destructor does really know
> whether it's safe to throw exceptions.

Time to give your definition of "safe," then ;-)

>> Can you give a summary of what you achieve by throwing smart pointers?
>>
> 1. Smart pointers are cheap to copy and the throwing/catching
> exceptions supposes copying.

Throwing requires an accessible copy ctor, but I know of at least one
implementation where you can throw an rvalue without copying it. I
don't see how catching supposes copying. But anyway...

> So you can create arbitrary big Exception
> objects, throw them as sh_ptr<Exception>(/* ... */) and they will be
> copied almost for free.

Okay. I *think* the cost of copying exceptions should not be a major
concern. Have you profiled?

> 2. sh_ptr copy constructor doesn't throw exceptions so there
> is no chance to get terminate() during the copying of
> sh_ptr<Exception>().

Yes, that's a useful known technique.

> On the contrary, Exception copy constructor does
> really throw exceptions so its copying is expensive and can lead to
> terminate().

Depends on what Exception is, neh?

> 3. It's really easy to create the chains of the nested Exception
> objects.

Okay.

> 4. Having recatchException() function you can use the following
> uniform exception handler in almost all of the places:
>
> void f()
> {
> try {
> // ...
> g();
> // ...
>
> return;
> }
> catch (...) {
> throw newException(_FLINE_, "Problems in f()",
> recatchException(mp, _FLINE_));
> }

I still don't understand what recatchException is supposed to do.
What's mp above?

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Sergey P. Derevyago

unread,
Aug 26, 2008, 11:46:03 AM8/26/08
to
David Abrahams wrote:
> Then that's still not very useful, because the dtor might be called from
> inside a catch block where the programmer needs non-throwing operations,
> and in_stack_unwinding would be false.
>
Could you show the code, please?

> I don't see how. The information about what is "safe" is not encoded in
> the program text; it's a function of the semantics the programmer is
> trying to achieve.
>

The point is that if in_stack_unwinding==true then an exception from
the destructor *shall* lead to terminate(). No guess, this is a strong
guarantee.

> Time to give your definition of "safe," then ;-)
>

Let's put it another way: if in_stack_unwinding==true then it's UNSAFE
to throw exceptions.

> Okay. I *think* the cost of copying exceptions should not be a major
> concern. Have you profiled?
>

I agree, no need to profile :)
But it's always _safe_ to copy sh_ptr<Exception>() object: we have
nothrow guarantee here.

>> On the contrary, Exception copy constructor does
>> really throw exceptions so its copying is expensive and can lead to
>> terminate().
>
> Depends on what Exception is, neh?
>

Non-trivial Exception classes tend to be unsafe.
So it really makes sense to use sh_ptr<Exception> object rather than
fight all of the dirty details (of nothrow guarantee).

>> 3. It's really easy to create the chains of the nested Exception
>> objects.
>
> Okay.
>

And it was the root of the problem!

At the time, I tried to get portable and easy-to-use C++ exception
traces that somehow resemble my Java code. The direct solution is
sh_ptr<Exception>...

>> 4. Having recatchException() function you can use the following
>> uniform exception handler in almost all of the places:
>>
>> void f()
>> {
>> try {
>> // ...
>> g();
>> // ...
>>
>> return;
>> }
>> catch (...) {
>> throw newException(_FLINE_, "Problems in f()",
>> recatchException(mp, _FLINE_));
>> }
>
> I still don't understand what recatchException is supposed to do.
> What's mp above?
>

shException recatchException(mem_pool& mp, const FileLine& location)
{
try { throw; }
catch (shException she) {
return she;
}
catch (const std::exception& se) {
return newStdExternException(mp, location, se.what(),
typeid(se).name());
}
catch (...) {
return newUnknExternException(mp, location, "Unknown exception");
}
}

You can use the following link to see the details:
http://ders.stml.net/cpp/mtprog/doc/exception_8cpp-source.html


--
With all respect, Sergey. http://ders.stml.net/
mailto : ders at skeptik.net

--

David Abrahams

unread,
Aug 28, 2008, 4:33:04 PM8/28/08
to

on Tue Aug 26 2008, "Sergey P. Derevyago" <non-existent-AT-iobox.com> wrote:

> David Abrahams wrote:
>> Then that's still not very useful, because the dtor might be called from
>> inside a catch block where the programmer needs non-throwing operations,
>> and in_stack_unwinding would be false.
>>
> Could you show the code, please?


struct File
{
// ...

~File(bool unwinding)
{
if (int err = close(this->x))
{
if (!unwinding)
throw couldnt_close(this->x, err);
}
}

os_file_handle x;
}

void rollback()
{
if ( logging_enabled)
{
File f(log);
f << "rolling_back\n";
}
// ...further rollback actions...
}


void f()
{
try
{
// ...
}

catch(...)
{
rollback();
raise;
}
}

>> I don't see how. The information about what is "safe" is not encoded in
>> the program text; it's a function of the semantics the programmer is
>> trying to achieve.
>>
> The point is that if in_stack_unwinding==true then an exception from
> the destructor *shall* lead to terminate(). No guess, this is a strong
> guarantee.

Not *that* strong ;-)

If the exception is never caught, off to terminate you go,
potentially without any unwinding.

>> Time to give your definition of "safe," then ;-)
>>
> Let's put it another way: if in_stack_unwinding==true then it's UNSAFE
> to throw exceptions.

Okay, but that doesn't help me. I'm going to throw exceptions from
dtors, you need to know the converse: if it's UNSAFE to throw
exceptions, in_stack_unwinding==true. uncaught_exception doesen't tell
you that, and neither does your idea, AFAICT.

In fact, as far as I can tell, uncaught_exception() will be
true during a dtor in exactly the same cases where your enhanced dtor's
unwinding argument will be true. If I'm wrong, please demonstrate how.

>> Okay. I *think* the cost of copying exceptions should not be a major
>> concern. Have you profiled?
>>
> I agree, no need to profile :)
> But it's always _safe_ to copy sh_ptr<Exception>() object: we have
> nothrow guarantee here.

Yes, that is a compelling argument.

>>> On the contrary, Exception copy constructor does
>>> really throw exceptions so its copying is expensive and can lead to
>>> terminate().
>>
>> Depends on what Exception is, neh?
>>
> Non-trivial Exception classes tend to be unsafe.
> So it really makes sense to use sh_ptr<Exception> object rather than
> fight all of the dirty details (of nothrow guarantee).

Okay. I'm not sure whether I agree. What is your argument against
Pimpl exception objects that contain nothing more than a shared_ptr?
That approach at least preserves the ability to catch base classes.

>>> 3. It's really easy to create the chains of the nested Exception
>>> objects.
>>
>> Okay.
>>
> And it was the root of the problem!

Which problem?

> At the time, I tried to get portable and easy-to-use C++ exception
> traces that somehow resemble my Java code. The direct solution is
> sh_ptr<Exception>...

Is that the problem you're solving, then?

>>> 4. Having recatchException() function you can use the following
>>> uniform exception handler in almost all of the places:
>>>
>>> void f()
>>> {
>>> try {
>>> // ...
>>> g();
>>> // ...
>>>
>>> return;
>>> }
>>> catch (...) {
>>> throw newException(_FLINE_, "Problems in f()",
>>> recatchException(mp, _FLINE_));
>>> }
>>
>> I still don't understand what recatchException is supposed to do.
>> What's mp above?
>>
>
> shException recatchException(mem_pool& mp, const FileLine& location)
> {
> try { throw; }
> catch (shException she) {
> return she;
> }
> catch (const std::exception& se) {
> return newStdExternException(mp, location, se.what(),
> typeid(se).name());
> }
> catch (...) {
> return newUnknExternException(mp, location, "Unknown exception");
> }
> }

Okay. I'm not sure why newException isn't calling recatchException
instead, but at least now I understand what you're doing.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Sergey P. Derevyago

unread,
Aug 30, 2008, 10:58:43 AM8/30/08
to
David Abrahams wrote:
>>> Then that's still not very useful, because the dtor might be called from
>>> inside a catch block where the programmer needs non-throwing operations,
>>> and in_stack_unwinding would be false.
>>>
>> Could you show the code, please?
>
>
> struct File
> {
> // ...
>
> ~File(bool unwinding)
> {
> if (int err = close(this->x))
> {
> if (!unwinding)
> throw couldnt_close(this->x, err);
> }
> }
>
> os_file_handle x;
> }
>
> void rollback()
> {
> if ( logging_enabled)
> {
> File f(log);
> f << "rolling_back\n";
> }
> // ...further rollback actions...
> }
>
>
> void f()
> {
> try
> {
> // ...
> }
> catch(...)
> {
> rollback();
> raise;
> }
> }
>
I see no _additional_ problems here because File ctor can also throw
just like the File dtor in question.

>> The point is that if in_stack_unwinding==true then an exception from
>> the destructor *shall* lead to terminate(). No guess, this is a strong
>> guarantee.
>
> Not *that* strong ;-)
>

I prefer really robust solutions ;)

> In fact, as far as I can tell, uncaught_exception() will be
> true during a dtor in exactly the same cases where your enhanced dtor's
> unwinding argument will be true. If I'm wrong, please demonstrate how.
>

Please, consider the following code:

void f()
{
A a;
throw something;
}

A::~A(bool unwinding)
{
B b;
}

In this case A's unwinding==true but B's unwinding==false. Does
uncaught_exception() have the same semantics?

> Okay. I'm not sure whether I agree. What is your argument against
> Pimpl exception objects that contain nothing more than a shared_ptr?
> That approach at least preserves the ability to catch base classes.
>

It complicates the life if you need to build the nested exception
chains. The chain of sh_ptr<BaseException> objects is really easy to get
if you always throw sh_ptr<BaseException>.

>> And it was the root of the problem!
>
> Which problem?
>

The problem is the chain of nested exceptions.

Please refer to the head of this thread
http://groups.google.com/group/comp.lang.c++.moderated/msg/66a0c6484460d4ef
where I get the following output:
-----------------------------------8<-----------------------------------
Exception #1:
ders::Exception [..\tst_exc.cpp:127], message: Problems in f()
ders::Exception [..\tst_exc.cpp:156], message: Problems in g()
ExampleException [..\tst_exc.cpp:136], message: Hello from g()

Exception #2:
ders::Exception [..\tst_exc.cpp:127], message: Problems in f()
ders::Exception [..\tst_exc.cpp:156], message: Problems in g()
ders::StdExternException [..\tst_exc.cpp:154],
typeName="St12out_of_range", message: vector::_M_range_check

Exception #3:
ders::Exception [..\tst_exc.cpp:127], message: Problems in f()
ders::Exception [..\tst_exc.cpp:156], message: Problems in g()
ders::UnknExternException [..\tst_exc.cpp:154], typeName="unknown",
message: Unknown exception

Exception #4:
ders::UnknExternException [..\tst_exc.cpp:110], typeName="unknown",
message: Unknown exception

Non-empty exit message
-----------------------------------8<-----------------------------------

>> At the time, I tried to get portable and easy-to-use C++ exception
>> traces that somehow resemble my Java code. The direct solution is
>> sh_ptr<Exception>...
>
> Is that the problem you're solving, then?
>

Yes, it is.
But it was discovered that the throw sh_ptr<BaseException> approach has
additional advantages too.

>> shException recatchException(mem_pool& mp, const FileLine& location)
>> {
>> try { throw; }
>> catch (shException she) {
>> return she;
>> }
>> catch (const std::exception& se) {
>> return newStdExternException(mp, location, se.what(),
>> typeid(se).name());
>> }
>> catch (...) {
>> return newUnknExternException(mp, location, "Unknown exception");
>> }
>> }
>
> Okay. I'm not sure why newException isn't calling recatchException
> instead, but at least now I understand what you're doing.
>

Prefer "one class (or function), one responsibility", you know ;)


--
With all respect, Sergey. http://ders.stml.net/
mailto : ders at skeptik.net

--

David Abrahams

unread,
Aug 31, 2008, 4:59:05 AM8/31/08
to

on Sat Aug 30 2008, "Sergey P. Derevyago" <non-existent-AT-iobox.com> wrote:

> David Abrahams wrote:
>>>> Then that's still not very useful, because the dtor might be called from
>>>> inside a catch block where the programmer needs non-throwing operations,
>>>> and in_stack_unwinding would be false.
>>>>
>>> Could you show the code, please?
>>
>>

<snip my example>

> I see no _additional_ problems here

Additional to what?

> because File ctor can also throw just like the File dtor in question.

Assume you know the file ctor won't throw, or that it is constructed in
a different scope and moved into place, or pick any other minor
variation one needs so the dtor is the only thing that could throw
during rollback.

>>> The point is that if in_stack_unwinding==true then an exception from
>>> the destructor *shall* lead to terminate(). No guess, this is a strong
>>> guarantee.
>>
>> Not *that* strong ;-)
>>
> I prefer really robust solutions ;)

I understand that. I'm saying your solution doesn't seem to be as
robust as you think.

>> In fact, as far as I can tell, uncaught_exception() will be
>> true during a dtor in exactly the same cases where your enhanced dtor's
>> unwinding argument will be true. If I'm wrong, please demonstrate how.
>>
> Please, consider the following code:
>
> void f()
> {
> A a;
> throw something;
> }
>
> A::~A(bool unwinding)
> {
> B b;
> }
>
> In this case A's unwinding==true but B's unwinding==false.

Really? That's even less useful than uncaught_exception()'s semantics!
It's equally unsafe to throw from ~B as it is from ~A. Furthermore,
stack unwinding *will* be underway when B is destroyed, so having
unwinding==false in B's dtor is in some sense a lie.

> Does uncaught_exception() have the same semantics?

No, thank goodness! The following demonstrates that throwing from ~B is
just as unsafe as throwing from ~A.

// test.cpp
#include <exception>
#include <iostream>

int const something = 5;

struct B
{
~B()
{
std::cout << "~B: " << std::uncaught_exception() << std::endl;
throw something;
}
};


struct A
{
~A()
{
std::cout << "~A: " << std::uncaught_exception() << std::endl;
B b;
}
};


void f()
{
A a;
throw something;
}

int main()
{
try
{
f();
}
catch(...)
{
std::cout << "caught" << std::endl;
}
}

$g++ test.cpp -o test && test
~A: 1
~B: 1
terminate called after throwing an instance of 'int'
/bin/bash: line 1: 24839 Aborted ./fu

>> Okay. I'm not sure whether I agree. What is your argument against
>> Pimpl exception objects that contain nothing more than a shared_ptr?
>> That approach at least preserves the ability to catch base classes.
>>
> It complicates the life if you need to build the nested exception
> chains. The chain of sh_ptr<BaseException> objects is really easy to get
> if you always throw sh_ptr<BaseException>.

Yes, but it requires everyone to cooperate in that protocol. No
environment I work in can offer an assurance that it will be the case.

>>> shException recatchException(mem_pool& mp, const FileLine& location)
>>> {
>>> try { throw; }
>>> catch (shException she) {
>>> return she;
>>> }
>>> catch (const std::exception& se) {
>>> return newStdExternException(mp, location, se.what(),
>>> typeid(se).name());
>>> }
>>> catch (...) {
>>> return newUnknExternException(mp, location, "Unknown exception");
>>> }
>>> }
>>
>> Okay. I'm not sure why newException isn't calling recatchException
>> instead, but at least now I understand what you're doing.
>>
> Prefer "one class (or function), one responsibility", you know ;)

I'd be paying more attention to "Don't Repeat Yourself," in this case,
but to each his own.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Sergey P. Derevyago

unread,
Sep 22, 2008, 8:24:09 PM9/22/08
to
David Abrahams wrote:
>> Please, consider the following code:
>>
>> void f()
>> {
>> A a;
>> throw something;
>> }
>>
>> A::~A(bool unwinding)
>> {
>> B b;
>> }
>>
>> In this case A's unwinding==true but B's unwinding==false.
>
> Really? That's even less useful than uncaught_exception()'s semantics!
> It's equally unsafe to throw from ~B as it is from ~A.
>
The point is that with the (bool unwinding) parameter the destructor
doesn't have to _guess_ whether it's safe to throw: if unwinding==true
then an exception from the destructor shall lead to terminate(), period.
On the contrary, uncaught_exception() doesn't give you so strong guarantee.

As for the example above, it's perfectly safe for ~B dtor to throw if
its unwinding==true. The potential problem lives in ~A, not in ~B! So
the unwinding destructor parameter clearly "outperforms"
uncaught_exception()'s semantics.

> Furthermore,
> stack unwinding *will* be underway when B is destroyed, so having
> unwinding==false in B's dtor is in some sense a lie.
>

No lies: unwinding==false means that the destructor wasn't called by EH
runtime during stack unwinding so this destructor in this context shall
throw without terminate().


--
With all respect, Sergey. http://ders.stml.net/
mailto : ders at skeptik.net

--

0 new messages