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

RAII and resource release functions that can throw

16 views
Skip to first unread message

Scott Meyers

unread,
Feb 19, 2005, 8:26:23 PM2/19/05
to
I have an old question that I, of all people, should know the answer to.
But I don't.

C++ uses RAII to avoid leaking resources, and destructors don't throw
because it makes things all icky if they do. No problem, I'm on board.
But then I get questions like this:

What if the destruction of the resource requires some exception handling,
such as logging the exception, translating the exception to app-specific
exception and returns to the caller, etc. For example, for a db
connection, I might want to do: (quite frequently found in similar JDBC
codes in Java world.)

void doSomething()
{
Connection* conn = NULL;
conn* = createConnection();
// ... do something
try {
if (conn != NULL) {
conn.close();
}
delete conn;
conn = NULL;
} catch (SQLException sqle) {
// 1. log the exception in some log file.
// 2. throw some app-specific exception or warning based on SQLException.
}
}

Adding RAII for the DB connection is easy, and taking care of logging in
the dtor is equally easy. But dealing with an exception arising from close
is not easy. The connection-managing object could swallow the exception,
but that doesn't seem right. It could translate the exception into a
different error-reporting strategy (e.g., errno), but that loses the
advantages of using exceptions for error reporting (e.g, they can't be
ignored). We could move the call to close out of the dtor and into a
different function that clients would be expected to call manually, but
that would lose the advantages of RAII.

There are a whole host of things we COULD do about combining RAII with
resource release functions that can throw, but I'm interested in what
people actually DO do. FWIW, I checked Herb's library, but I didn't see
any definitive advice on this topic, and some quick googling around didn't
reveal anything, either, but I'm sure this has been hashed over many times.
Feel free to respond with reference to a publication or a URL.

Yours in woeful uninformedness,

Scott

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Trevor L. Jackson, III

unread,
Feb 19, 2005, 11:47:18 PM2/19/05
to
Scott Meyers wrote:

Destructors need to be able to throw. If excepetions are to be the
universal mechanism for reporting exceptional conditions that cause
lower levels of the software to bail, then they need to be uuniverally
available. Any mechanism with exceptions (pun intended) in its
availability is not suitable for use as a general purpose facility in
the language.

I would love to use exceptions in produciton code. But I can't. As
currently designed they don't work.

This particular issue is a reasonably good example of the ugly nooks and
crannies of the language. There are too many special cases that just
don't work in a straightforward manner. Much of this is due to the C
language roots of C++. But a lot of it is due to the fact that the
language is not defined as a standardaization of existing practice. It
is more of a language design exercise, and parts of that design are
simply non-functional.

/tj3


/tj3

Michael Pryhodko

unread,
Feb 20, 2005, 12:06:50 AM2/20/05
to
> What if the destruction of the resource requires some exception
> handling, such as logging the exception, translating the exception
> to app-specific exception and returns to the caller, etc.

You should do smth like this:

{
Auto_DB_connection db_conn = ...;

// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}

Auto_DB_connection's destructor should not throw (but can log).

Successful resource release should be the part of 'do anything you
need', if you can not guarantee its success :)). Sometimes you do not
care -- in this case you could omit explicit release call (for example
closing CloseHandle for mutex)

Bye.
Sincerely yours, Michael.

White Wolf

unread,
Feb 20, 2005, 12:01:59 AM2/20/05
to
Scott Meyers wrote:
> I have an old question that I, of all people, should know the answer to.
> But I don't.

Then the answer must be: It depends. And as I will try to illuminate (yes,
I do read Harry Potter) in this post it later, I believe that the answer in
this case *is*: It depends.

> C++ uses RAII to avoid leaking resources, and destructors don't throw
> because it makes things all icky if they do. No problem, I'm on board.
> But then I get questions like this:
>
> What if the destruction of the resource requires some exception
> handling, such as logging the exception, translating the exception to
> app-specific exception and returns to the caller, etc. For example, for
> a db connection, I might want to do: (quite frequently found in similar
> JDBC codes in Java world.)

My standard answer is: don't use Java. :-) But I guess one cannot say that
during paid consulting... ;-)

[CODE-SNIPPED]

> Adding RAII for the DB connection is easy, and taking care of logging in
> the dtor is equally easy. But dealing with an exception arising from
> close is not easy.

It definitely is not. The reason is, that you are facing a way too vague
question. I have met this question in two forms during my life. It wasn't
usually in C++ (as I recall) but all of such questions has lead to two
solutions, none of which is possibly acceptable for your case, if not, there
is a third one. But let's first see the two "basic" options.

Option one is the smoke-the-weed one: I ask the system to remove a record
(let's suppose I have some sort of ScopeGuard). The system says: there is
no such record. I say (with a touch of the 70ies): whatever. So let's just
forget about it! (Maybe log it, that usually means the same thing). I
wanted to delete it. It is not there. So, I have got what I wanted, didn't
I? ;-)

Option two is the fall-on-your-sword one: So someone comes to me: 'Attila,
you have said that we should check all the erors.', 'And you remembered!'
say I. 'So I have here this call to close, which can return an error. But
what should I do if it does?' This conversation actually has happened. I
have asked the person to look at the documentation (as a last reserve) and
come back tell me *when* will close return an error. The answer was: when
it has got an invalid file handle. The usual reaction is: 'But in my code
it is impossible to have an invalid file handle there.' A quick look at the
code, it is really true. It is as simple as possible, no way to get garbage
or a bad handle. So the answer is: the error should *never* happen. That
is spelled assert in C (and C++). So the answer here is the same as every
case when the impossible is detected: die fast and try to do no more damage
on the way.

However these simple answers may not be good enough for something as complex
as a database connection. First of all let me state it at the beginning
that if your database (or anything else "independent") is not designed to
protect itself (transactional behavior) you are looking at Mr. Disaster, and
driving to the server room at 3am in a snowstorm to restore everything from
backup and happy things like that.

I will continue where you see ###, because it belongs there. But before
that, it is worth to mention that one strategy may not be sufficient to
handle all possible database-close errors. If you get an error saying 'Due
to earlier errors all database connections has been closed and all open
transactions will be rolled back.', you can happily forget about the error.
But if you get a 'Internal error.' or a 'I dunno about that connection.'
error, you are better of killing the process as fast as possible. And if
you are paranoid (as I am) the text to your (operator's) pager will go out
*not* if the system dies with printing some special message, but when it
dies *without* printing that special message. I talk about servers, of
course.

Die is basically what terminate does, so we can achieve that with not
writing a catch. But terminate is a half-graceful shutdown, and it possibly
does too much, so IMNSHO it is better to just simply assert/abort. The
other case (ignoring the non-errors) is very simple (write a catch and eat
it), but IMNSHO it is a good idea to make it a rule: only specific errors
can be ignored, not every possible exception (or large groups of them).

> The connection-managing object could swallow the exception,
> but that doesn't seem right.

I wholeheartedly agree with you on that, that is the reason why I do not
like the idea (which is in the original ScopeGuard sources on the CUJ FTP;
once it will work...) of catching and ignoring errors by default. Some
errors you can and should ignore, because they are not really errors. But
this has to be a conscious decision every time and for every error!

> It could translate the exception into a
> different error-reporting strategy (e.g., errno), but that loses the
> advantages of using exceptions for error reporting (e.g, they can't be
> ignored). We could move the call to close out of the dtor and into a
> different function that clients would be expected to call manually, but
> that would lose the advantages of RAII.

###

You don't have to drop RAII completely. But you can delay it. ;-)

I have made code which shows what I talk about, but I guess it is too long
to post here. Anyway, I will tell the "trick" I thought of. For all of
these "special" things (where destruction may really fail outside of the
die/ignore realm) you create a trendy global array variable. Ehem.
Something like that, a simple storage where you can register for cleanup (if
normal RAII fails with an exception and you catch it). You also make your
exception base class destructor call that cleanup function. (Or you make
another, RAII type, which will be in a scope around your try-catch, all of
those which finally handle exceptions.)

Suppose you had an out of memory situation. You could not close your
database connection due to that, but later, when your exception's destructor
runs, the database connection can already be closed. So you retry then. If
it still does not work... I guess you just have to clean up as much as you
can and quit. Nothing will save you from the 3am walk-in, except if you are
the boss so you can blame someone else. ;-)

> There are a whole host of things we COULD do about combining RAII with
> resource release functions that can throw, but I'm interested in what
> people actually DO do.

For that, one example is Andrei's ScopeGuard article, where Andrei bets his
bucks on hiding all exceptions. I disagree. :-)

Of course what I wrote above is only my vivid imagination. I did not use
exceptions (yet) in huge quantities, and where I did use, it was simplicity
itself. So although my ideas (except for the do-nothing-and-crash :-)
aren't practice (yet), this is what I would/will do. But only in addition
to making it possible for the independent elements to re-synchronize,
providing transactional behavior - and do some magical design where bringing
one side down will not bring the other side down. But I guess that brings
us to a totally separate topic. ;-) And finally we get to where we've
started: you need to have reliability in the system (somewhere) to be able
to make stable code. The requirement on destructors (for exception safety
in C++) is just a manifestation of that.

> FWIW, I checked Herb's library, but I didn't see
> any definitive advice on this topic, and some quick googling around
> didn't reveal anything, either, but I'm sure this has been hashed over
> many times. Feel free to respond with reference to a publication or a
> URL.

If you want me I can also post code (feel free to email me, anyone not only
Scott). I did not post the code because it is long but (I think) trivial
(which means that if it is not, I have many bugs in it :-).

> Yours in woeful uninformedness,
--
Yours in woeful uninformadness,
WW aka Attila
:::
It is far more impressive when others discover your good qualities without
your help.

Roland Pibinger

unread,
Feb 20, 2005, 12:14:33 PM2/20/05
to
On 20 Feb 2005 00:06:50 -0500, "Michael Pryhodko"
<mpry...@westpac.com.au> wrote:

>> What if the destruction of the resource requires some exception
>> handling, such as logging the exception, translating the exception
>> to app-specific exception and returns to the caller, etc.
>
>You should do smth like this:
>
>{
>Auto_DB_connection db_conn = ...;
>
>// do anything you need
>
>// everything went fine -- try to close connection explicitly
>// could throw
>db_conn.close();
>}
>
>Auto_DB_connection's destructor should not throw (but can log).
>
>Successful resource release should be the part of 'do anything you
>need', if you can not guarantee its success :)). Sometimes you do not
>care -- in this case you could omit explicit release call (for example
>closing CloseHandle for mutex)

Good point! If you feel the need to handle an exception the destructor
is not the right place for the 'throwing' piece of code. I'd write the
database example in the following way:

void doSomething (Database& db)
{
Auto_DB_connection db_conn (db);
// ... do something

db_conn.commitWork(); // last statment
}
// db_conn.rollbackWork() is called in the destructor of db_conn
// if db_conn.commitWork() is not reached, e.g. due to
// an exception

Best wishes,
Roland Pibinger

White Wolf

unread,
Feb 20, 2005, 12:21:21 PM2/20/05
to
Trevor L. Jackson, III wrote:
[SNIP]

> Destructors need to be able to throw.

They are able to throw.

> If excepetions are to be the
> universal mechanism for reporting exceptional conditions that cause
> lower levels of the software to bail, then they need to be uuniverally
> available.

They are universally available. As much as they can be. But I am open to
suggestions which show us how C++ could handle more than one exception
(basicaly meaning more than one thread of execution) in one thread of
execution.

> Any mechanism with exceptions (pun intended) in its
> availability is not suitable for use as a general purpose facility in
> the language.

Would you please share the reasons behind this opinion (statement)? Why do
you say exceptions have exceptions in their availability? And why do you
think if they have, that stops you from using them effectively? Have you
tried? Or is this all on theoretical level? In my previous post I gave 3
ways to handle the situation Scott has asked about. Two out of which is
*very* simple, and covers most of the cases one meets, at least that is my
feeling. Even if it does not, I gave the 3rd option.

I would also be happy to see your suggestions on how can someone (in any
language) write safe code, if both the commit and the rollback operations
can fail? I believe that (regardless of the programming language of choice)
this is simply impossible. Unless the system does absolutely nothing. But
I think that can already be achieved in C++, although I think it needs to
run in freestanding environment.

> I would love to use exceptions in produciton code. But I can't. As
> currently designed they don't work.

That is a statement which has been proved to be false many years ago.
Unless you have new reasons to share with us. If you do, please do share
them.

> This particular issue is a reasonably good example of the ugly nooks and
> crannies of the language.

I am sorry, but this is the point where I *strongly* need to ask you, to
*please* actually tell something other than your verdict on C++. So far all
I read is flaming dislike (to be PC) towards C++ without a single technical
reasoning or fact to support it.

> There are too many special cases that just
> don't work in a straightforward manner.

Again. Flaming statement, absolutely no reasoning, not even an example.

> Much of this is due to the C
> language roots of C++.

Much of what? Do you mean that C++ is unable to handle two pathes of
execution (two exceptions) at the same time because of C? C has no
exceptions. I fail to see your point.

> But a lot of it is due to the fact that the
> language is not defined as a standardaization of existing practice.

Again, a statement with no technical facts ot back it up. I am not saying
you don't have them, but these statements are just too one-sided to just
drop them in without proving them.

> It
> is more of a language design exercise, and parts of that design are
> simply non-functional.

Again: zero technical facts, but a broad statement (or should I say
judgement) on the work of many people. Even if they have done a very bad
job during the years of standardization, don't they deserve the courtesy of
being told the reason why you think what they have made is non-functional?

As for the moderators: I thought that the rule for flame was "if in doubt,
reject". I suspect that my post will be rejected based on that clause. I
just wonder how hard it is to see if a post is nothing else but an outburst
with absolutely no technical facts or reasoning of any kind? We - who work
with C++ - have enough attacks already to handle daily. I for one do not
need any more ammunition for those people who refuse to look at C++ code but
they "know" C++ is a bad choice for a language. Especially not this kind of
ammunition. Since it contains absolutely no reasoning or technical facts
(only the fallacies we have heard for years), it gives unbeatable
ammunition. Why unbeatable? Because it has been approved by the moderators
(and not taken as flame) so it must be true. But there is not a single fact
to be discussed or debated, so there is zero chance to prove that it is
false or that it is way too exaggerated.

--
WW aka Attila
:::
Intel: We put the 'um...' in Pentium.

Andreas Huber

unread,
Feb 20, 2005, 12:16:46 PM2/20/05
to
Michael Pryhodko wrote:
>> What if the destruction of the resource requires some exception
>> handling, such as logging the exception, translating the exception
>> to app-specific exception and returns to the caller, etc.
>
> You should do smth like this:
>
> {
> Auto_DB_connection db_conn = ...;
>
> // do anything you need
>
> // everything went fine -- try to close connection explicitly
> // could throw
> db_conn.close();
> }

Shouldn't that rather read:

{
Auto_DB_connection db_conn = ...;
try
{


// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}

catch ( ... )
{
db_conn.close();
}
}

?

Regards,

--
Andreas Huber

When replying by private email, please remove the words spam and trap
from the address shown in the header.

James Kanze

unread,
Feb 20, 2005, 1:22:50 PM2/20/05
to
Scott Meyers wrote:
> I have an old question that I, of all people, should know the
> answer to. But I don't.

> C++ uses RAII to avoid leaking resources, and destructors
> don't throw because it makes things all icky if they do. No
> problem, I'm on board. But then I get questions like this:

> What if the destruction of the resource requires some
> exception handling, such as logging the exception,
> translating the exception to app-specific exception and
> returns to the caller, etc. For example, for a db
> connection, I might want to do: (quite frequently found in
> similar JDBC codes in Java world.)

Using RAII is quite frequent in the Java world:-)?

> void doSomething()
> {
> Connection* conn = NULL;
> conn* = createConnection();
> // ... do something
> try {
> if (conn != NULL) {
> conn.close();
> }
> delete conn;
> conn = NULL;
> } catch (SQLException sqle) {
> // 1. log the exception in some log file.
> // 2. throw some app-specific exception or warning based on
SQLException.
> }
> }

> Adding RAII for the DB connection is easy, and taking care of
> logging in the dtor is equally easy. But dealing with an
> exception arising from close is not easy. The
> connection-managing object could swallow the exception, but
> that doesn't seem right. It could translate the exception
> into a different error-reporting strategy (e.g., errno), but
> that loses the advantages of using exceptions for error
> reporting (e.g, they can't be ignored). We could move the
> call to close out of the dtor and into a different function
> that clients would be expected to call manually, but that
> would lose the advantages of RAII.

There is a simple rule: if an operation might fail, and you need
to react to the error condition, that operation must be done
explicitly, before destruction. Period. No exceptions.

The key, of course, is "and you need to react to the error
condition". Very often, once an error has occured, and an
exception has been thrown further down, you don't need to react
to further error conditions. In my own code, it's not rare for
a class to e.g. close a file in a destructor, ignoring any
errors, but that the "normal" control flow will pass threw an
explicitly called function which closes the file, checking for
errors. In many cases, the the class will memorize the fact
that the file has been explicitly closed, and if the destructor
is invoked without the file having been explicitly closed, it
will also take actions to ensure that the results of a probably
incomplete write are not used (delete the file, write a bad
checksum into the last record, etc.). But the destructor will
never propagate an error out. (Note that the transaction idiom
works very well here. The normal close is in commit, and at
that point, you ensure the transactional integrity, by whatever
means are necessary. Calling the destructor before commit has
finished causes a roll-back, and you'be better make sure that
you can roll-back without any errors if you want to ensure
transactional integrity.)

> There are a whole host of things we COULD do about combining
> RAII with resource release functions that can throw, but I'm
> interested in what people actually DO do.

Well, I can only describe what I actually do. This is one case
I can't talk too much for others -- on most of the projects I've
worked on, this sort of thing has been part of my
responsibilities, so I don't know what the others would have
done.

--
James Kanze home: www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

Dietmar Kuehl

unread,
Feb 20, 2005, 4:33:00 PM2/20/05
to
I just want to comment on the non-technical part of this article:

White Wolf wrote:
> As for the moderators: I thought that the rule for flame was "if in doubt,
> reject". I suspect that my post will be rejected based on that clause. I
> just wonder how hard it is to see if a post is nothing else but an
> outburst
> with absolutely no technical facts or reasoning of any kind?

The context set by Scott Meyers provides technical facts (albeit incomplete
and misleading ones: it can be made safe to throw an exception from a
destructor but typically it is avoided due to the necessary extra work and
the fact that normally the two simple approaches you mention are the right
way to go anyway). With this context I don't consider the article to be a
flamebait (it was not a flame at all since no entity was directly attacked,
although the Committee was criticized but also with a reason, namely that
it did not standardization but invention).

> We - who work
> with C++ - have enough attacks already to handle daily. I for one do not
> need any more ammunition for those people who refuse to look at C++ code
> but they "know" C++ is a bad choice for a language.

We definitely agree on this part. However, the general statement that
destructors cannot throw would indeed be a strong limitation of the language
if it were true (which it is not: the key to use 'uncaught_exception()' to
detect whether it is safe to throw a new exception or whether another
exception also needs to be taken care of): it is not just a plain complaint
about something but it is rooted in a technical concern. Of course, deducing
that exceptions are unusable in C++ due to such a limitation, even if it
were true, is a little bit strong but not necessarily unreasonable.

> Especially not this kind of
> ammunition. Since it contains absolutely no reasoning or technical facts
> (only the fallacies we have heard for years), it gives unbeatable
> ammunition. Why unbeatable? Because it has been approved by the
> moderators (and not taken as flame) so it must be true.

This is not at all the case: although I detect many false statements in
articles, I don't reject them and this is definitely not only true for me
but also for the other moderators! The fact that an article is approved has
nothing to do with the correctness of the statements in the article. All we
judge is whether the article is about C++, is a flame, and a few rather
technical things (we also reject duplicates, trivial or FAQ stuff, excessive
quoting, huge code examples, non-English language, etc.). For the given
article I considered whether it is a flame but decided that there is enough
technical background (by the context given by Scott; as a stand-alone
article without the quoting it would have been rejected as flamebait).

> But there is not a single
> fact to be discussed or debated, so there is zero chance to prove that it
> is false or that it is way too exaggerated.

>From the above discussion it should be clear that we disagree on this
assessment: there is a statement at the root (that exceptions cannot be
thrown from destructors) which is considered to be a fact. It is wrong but
this is exactly the start for the discussion (or, if it is indeed correct
and I'm wrong about the possibilities based on 'uncaught_exception()' we
might discuss what we should do about the restriction; actually, even if
'uncaught_exception()' can be used as I think it can, it may be reasonable
to discuss whether there should be some mechanism allowing exception
chaining).
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

mark

unread,
Feb 20, 2005, 4:36:42 PM2/20/05
to

If you routinely expect errors, then I wouldn't use exceptions,
instead I would check a return code.

Another example is opening a file. This commonly fails, so I check
the return code rather than throw an exception.

Cheers,
Mark.

White Wolf

unread,
Feb 20, 2005, 4:37:03 PM2/20/05
to
James Kanze wrote:
[SNIP]

> Using RAII is quite frequent in the Java world:-)?

Possibly as a curse-word. It is 4 letters. :-)

[SNIP]


> means are necessary. Calling the destructor before commit has
> finished causes a roll-back, and you'be better make sure that
> you can roll-back without any errors if you want to ensure
> transactional integrity.)

[SNIP]

I think this can only be done if either your commit or your rollback is an
"unlikely to fail" operation. In case both of them can fail easily, you
will have trouble, C++ or not, exceptions or not.

--
WW aka Attila
:::
Despite the cost of living, have you noticed how it remains so popular?

David B. Held

unread,
Feb 20, 2005, 5:47:12 PM2/20/05
to
Scott Meyers wrote:
> [...]

> There are a whole host of things we COULD do about combining RAII with
> resource release functions that can throw, but I'm interested in what
> people actually DO do. FWIW, I checked Herb's library, but I didn't see
> any definitive advice on this topic, and some quick googling around didn't
> reveal anything, either, but I'm sure this has been hashed over many times.
> Feel free to respond with reference to a publication or a URL.
> [...]

Well, the main problem is stated in this link:

http://kolpackov.net/projects/c++/eh/dtor-1.xhtml

The C++ exception-handling and destruction mechanism is 1-fault
failfast for dense faults. However, a failing close operation
that must follow a failing member operation can only be gracefully
handled by a 2-fault or better failfast system. Basically, we
would need to be allowed to have two active d'tors at once. If
the close failed after all the other operations succeeded, all is
well. You could throw from the d'tor because it would be the only
active exception. However, if the resource was dynamically allocated,
you have the little problem of its storage not being properly
reclaimed. In a first-order RAII scenario, that's not a problem,
because it is stack-allocated. In a second-order RAII, where the
resource spans multiple block scopes and is managed by a smart
pointer, you have a problem.

You probably don't want to require your RAII object to always
be stack-allocated, so any kind of d'tor-throwing scenario can
be eliminated right away. But that still doesn't address the
fundamental issue, which is that you want to have two active
exceptions at the same time. It would be interesting to contemplate
what C++ would look like if it supported such a thing, but since
it doesn't, we have to decide what behavior we want as the way
C++ is now. That means that when a connection operation fails,
causing close() to fail, we have to decide whether we want to
propagate the original exception or the close() exception, or
create a new exception that merges information about both of them.

Now, if we choose the first option, we can just fail to propagate
close() exceptions (after logging them), but that still leaves us
with the case where only close() itself fails. There you probably
want to propagate the exception, but you don't want to do it from
a d'tor. The second case is pretty much impossible to implement
in C++. The third option is as hard as the second, which means
that we can effectively eliminate it. Which means that if we
really really want an exception to be reported when close() fails
by itself, we have to provide a member function that can be invoked
manually. The d'tor can call that function and suppress the
exception, but I don't see any other logical response to this
problem.

That mean's that James Kanze's answer seems like the only sane
one, except that transactional behavior really has nothing to
do with close() failing. If you are at the end of doing a commit,
and the commit process itself fails at the very end when you close()
the connection, odds are that it is not going to be possible to
rollback anyway. The best you can do is let the close() exception
propagate up the stack and hope for the best. The interesting
thing is that a failure in close() might not represent a failure
of the operation as a whole. It could be that the operation
succeeded, and a network connection to the db was broken
unexpectedly before close() was called, and close() reports an
unexpected connection state as an exception. In that case, you
wouldn't even want to do a rollback even if you still could. So
it really depends on how close() can fail, and what that means
for your application.

Even though it seems like you lose the benefit of RAII by having
to manually call close(), I see it as merely registering that
you want the result of an operation performed by the d'tor.
Since the d'tor will call close() anyway, you still get the EH
benefits of RAII, up to the point that the system is able to
provide.

Dave

Victoria Dassen

unread,
Feb 20, 2005, 5:47:34 PM2/20/05
to
Andreas Huber wrote:
> Michael Pryhodko wrote:
>
>>>What if the destruction of the resource requires some exception
>>>handling, such as logging the exception, translating the exception
>>>to app-specific exception and returns to the caller, etc.
>>
>>You should do smth like this:
>>
>>{
>>Auto_DB_connection db_conn = ...;
>>
>>// do anything you need
>>
>>// everything went fine -- try to close connection explicitly
>>// could throw
>>db_conn.close();
>>}
>
>
> Shouldn't that rather read:
>
> {
> Auto_DB_connection db_conn = ...;
> try
> {
> // do anything you need
>
> // everything went fine -- try to close connection explicitly
> // could throw
> db_conn.close();
> }
> catch ( ... )
> {
> db_conn.close();
> }
> }
>
> ?
>
> Regards,
>
Better yet, try ---> (Rigor Mortis Saloon "Come in and get stiff")[1]
Auto_DB_connection::~Auto_DB_connection()
{
close(); // won't throw
release(); // same idea, or rely on auto_ptr, shared_ptr
// or some other RAII class to delete or otherwise
// give back resources used by m_connection and get
// its (non-throwing) destructor to run
// do whatever else that doesn't throw, too
// my data member dtors better not throw, either :)
}

void
Auto_DB_connection::close() throw ()
{
try
{
// preamble
m_connection.close();
// postamble ( or what-*ever*! )
}
catch ( const DBException& error )
{
// handle it
}
// ...more handlers as needed
catch ( ... )
{
// handle anything
}
}

[1] I'll be real impressed by anyone who gets *that* reference :),
Shania Twain not withstanding :)

Victoria

Trevor L. Jackson, III

unread,
Feb 20, 2005, 5:46:40 PM2/20/05
to
Dietmar Kuehl wrote:

> I just want to comment on the non-technical part of this article:

[snip]

> We definitely agree on this part. However, the general statement that
> destructors cannot throw would indeed be a strong limitation of the language
> if it were true (which it is not: the key to use 'uncaught_exception()' to
> detect whether it is safe to throw a new exception or whether another
> exception also needs to be taken care of): it is not just a plain complaint
> about something but it is rooted in a technical concern. Of course, deducing
> that exceptions are unusable in C++ due to such a limitation, even if it
> were true, is a little bit strong but not necessarily unreasonable.

The paragraph above makes certain statmes that are fundamentally
technical rather than non-technical. So I am taking the libery of
taking them out of the original context set by the introductory statement.

I dispute the claim that uncaught_exception() is a sufficient foundation
on which to solve (in some sense of the term "solve") the issue of
colliding exceptions.

For example, given an exception from a low level function, say bad_alloc
or something else that is exceptional, but not fatal, the superposed
layers of software would be unwound until some layer is reached that has
declared its intention to deal with a set of exceptions including
bad_alloc (or whatever).

The problem appears when an intermediate layer of the software detects
an exceptional condition that is of higher importance than the exception
currently pending. In that situation the intervening layer can either
ignore the important exception or force termination. That is usually an
unacceptable choice.

Would anyone care to work on a system where the interrupts were arranged
such that a higher priority interrupt was faced with the choice of being
ignored to forcing a reset (and thus a reboot) of the machine? Of
course not. Analogies are slippery, but I don't think this one is a
distortion. From the perspective of the higher level software an
exception is a kind of interruption in the normal flow of control, but
it gets handled and the normal flow of control resumes.

Interrupts achieve this transparency by nesting or queueing. Exceptions
don't. In the absence of neither nesting nor queueing I suggest that
replacement based on a precedence hierarchy is the least functionality
on which a robust system could depend.

[snip]

>>From the above discussion it should be clear that we disagree on this
> assessment: there is a statement at the root (that exceptions cannot be
> thrown from destructors) which is considered to be a fact. It is wrong but
> this is exactly the start for the discussion (or, if it is indeed correct
> and I'm wrong about the possibilities based on 'uncaught_exception()' we
> might discuss what we should do about the restriction; actually, even if
> 'uncaught_exception()' can be used as I think it can, it may be reasonable
> to discuss whether there should be some mechanism allowing exception
> chaining).

Clearly an application could build an exception framework that would
allow chaining or queueing and even impose a priority or filtering
scheme upon the collection of pending exceptions. But the only argument
against moving that capability out of the application and into the
language (or libraries) would be an overhead issue (probably both space
and time). But in almost every case that overhead is avoidable. In the
few cases where it is unavoidable it can probably (more theory than fact
here) be justified on the basis of reliability.

/tj3

Andreas Huber

unread,
Feb 20, 2005, 5:48:52 PM2/20/05
to
Andreas Huber wrote:
[snip]

> Shouldn't that rather read:
[snip]

I hit the send button too soon, please disregard this message...

Michael Pryhodko

unread,
Feb 21, 2005, 2:17:02 PM2/21/05
to
> Better yet, try ---> (Rigor Mortis Saloon "Come in and get stiff")[1]

[skip]


Wrong. Should be smth like that:

Auto_DB_connection::~Auto_DB_connection() throw()
{
. try
. {
. close();
. }
. catch(std::exception const& x)
. {
. // log: "during stack unwinding due to early exit (because "
. // "of 'exception' or 'return any_error') error happened"
. }
}

void
Auto_DB_connection::close()
{
. // could throw
. // you could replace some exceptions with your own
. m_connection.close();
}


and code should look like


void doSomething (Database& db)
{
. // local objects for rollbacks
. Auto_DB_connection db_conn (db);
. ///////////////////////////////////////////////////////
. // actual work that could fail at any point
. // ... do something
. // if you are using more than one object you should do first phase
. // of two-phase commit here
. // should not be more than one object that can not be committed
. db_conn.close(); // could throw
. // do second phase of two-phase commit -- it should not throw!
. // end of actual work
. ///////////////////////////////////////////////////////
. // switch off auto-rollback objects
. // auto_smth.everything_is_fine(); // should not throw
}


If you can not do like this consider revising your architecture -- you
can not create 100% relliable application with current architecture.


:)
Personally I'd vote for beating every person who will write throwing
destructor, one hit in the head for every such destructor.
I wonder why this question always raised along with database-related
code? Maybe because people who write such libraries "came" from DB
world and are not competent enough to understand? I remember OTL (C++
DB access library) has issues with throwing destructors.

Bye.
Sincerely yours, Michael.

White Wolf

unread,
Feb 21, 2005, 2:18:47 PM2/21/05
to
Dietmar Kuehl wrote:
> This is not at all the case: although I detect many false statements in
> articles, I don't reject them and this is definitely not only true for me
> but also for the other moderators! The fact that an article is approved
> has nothing to do with the correctness of the statements in the article.
> All we judge is whether the article is about C++, is a flame, and a few
> rather technical things (we also reject duplicates, trivial or FAQ
> stuff, excessive quoting, huge code examples, non-English language,
> etc.). For the given article I considered whether it is a flame but
> decided that there is enough technical background (by the context given
> by Scott; as a stand-alone article without the quoting it would have
> been rejected as flamebait).

I still consider the original article an outburst with absolutely no attempt
in proving its statements. Count it as the sign of how unreasonable I am.

As for the moderation. I did not mean to request or even to imply that you
need to reject articles based on their content being false or otherwise.
Although I do recall times when absolutely unfounded statements have been
corrected by the moderators, so that no fallacies will be spread even during
the short time between two moderations (so until an answer can arrive).

I strongly believe that articles must not be judged based on assessing the
quality of their content or message. I do believe however that in a
moderated technical newsgroup, articles with zero technical content, but
clear negative remarks should be requested to be reworded to contain some
sort of technical reasoning. Otherwise the group can slowly shift into
debating opinions, which can render it useless just as the slow shifting to
the wrong letter of the alphabet did it for over a year.

Of course, it is your call. I have made my point, if you still chose to
accept articles that have only opinions in them, all I can and will do is to
stay away from them. :-)

--
WW aka Attila
:::
Once we've got the bugs ironed out, we'll be running on flat bugs.

Peter C. Chapin

unread,
Feb 21, 2005, 3:32:28 PM2/21/05
to
"Trevor L. Jackson, III" <tl...@comcast.net> wrote in
news:fbydnbztcv0...@comcast.com:

> The problem appears when an intermediate layer of the software detects
> an exceptional condition that is of higher importance than the
> exception currently pending. In that situation the intervening layer
> can either ignore the important exception or force termination. That
> is usually an unacceptable choice.

Why can't the intermediate layer throw a new exception of a different
type to indicate the more serious situation? For example

try {
low_level();
}
catch (...) { // Or whatever
// Something VERY bad happens here.
throw VeryBadThing();
}

It is true that the first exception is lost, but one imagines that it
becomes irrelevant once the very bad thing happens. In any event the
function containing the code above does not have to ignore the very bad
event nor force termination. The event of lesser importance, however,
does get ignored.

> Would anyone care to work on a system where the interrupts were
> arranged such that a higher priority interrupt was faced with the
> choice of being ignored to forcing a reset (and thus a reboot) of the
> machine?

Your analogy with interrupts has a flaw. Exceptions are not like
interrupts because the code that throws the exception can't be
(directly) resumed. When the handler completes, the flow of control
continues from a different place than from where the exception was
thrown. The notion of "nested" exceptions doesn't seem to quite make
sense the way the notion of nested interrupts does. The second exception
simply subsumes the first in its effect.

Peter

stork

unread,
Feb 21, 2005, 3:36:19 PM2/21/05
to

TJB replied to:

> What if the destruction of the resource requires some exception
handling,

In general, you should not be acquiring resources in a destructor! The
invocation of a destructor is expression of developer's intent to no
longer process with that object.

What do you fundamentally do when an object's destruction fails? Not
destroy it? But what if you were originally destroying the object
because of another resource failure? This is a conflicting case and so
to avoid that case I would argue that exceptions in destructors should
be eaten in production and assert'd or __asm { int 3 } in development.


It would seem that that there would be an exception to this rule: if
the failure of the destruction left the application in a state where
the resource could not be reaquired, then the destructor should throw
an exception. But, even then, the worst thing that would happen would
be a subsequent throw on the resource acquisition side, which is
clearly more manageable. In your case of a failed "close", then, the
subsequent open would have to manage it. Either the open could get the
resource, or it could not.

foo::~foo()
{
try {
blah;
}
catch (...)
{
#ifdef DEBUG
__asm { int 3 }
#endif

Roland Pibinger

unread,
Feb 21, 2005, 4:04:36 PM2/21/05
to
On 20 Feb 2005 17:47:12 -0500, "David B. Held"
<dh...@codelogicconsulting.com> wrote:

>Well, the main problem is stated in this link:
>
>http://kolpackov.net/projects/c++/eh/dtor-1.xhtml
>
>The C++ exception-handling and destruction mechanism is 1-fault
>failfast for dense faults. However, a failing close operation
>that must follow a failing member operation can only be gracefully
>handled by a 2-fault or better failfast system. Basically, we
>would need to be allowed to have two active d'tors at once.

If this were so one could not write functions with transactional
semantics (commit, rollback) in C++.

Best regards,
Roland Pibinger

adeht

unread,
Feb 21, 2005, 4:04:58 PM2/21/05
to
Michael Pryhodko wrote:
[snip]

> Auto_DB_connection::~Auto_DB_connection() throw()
> {
> . try
> . {
> . close();
> . }
> . catch(std::exception const& x)
> . {
> . // log: "during stack unwinding due to early exit (because "
> . // "of 'exception' or 'return any_error') error happened"
> . }
> }
[snip]

> Personally I'd vote for beating every person who will write throwing
> destructor, one hit in the head for every such destructor.
[snip]

*thump*

You might get an exception thrown in the destructor. The fact that it
will catch it has nothing to do with it. If this happens and another
exception is already active, welcome to the Rigor Mortis Salloon.

Same goes for Victoria Dassen's code.

Trevor L. Jackson, III

unread,
Feb 21, 2005, 4:53:18 PM2/21/05
to
Peter C. Chapin wrote:
> "Trevor L. Jackson, III" <tl...@comcast.net> wrote in
> news:fbydnbztcv0...@comcast.com:
>
>
>>The problem appears when an intermediate layer of the software detects
>>an exceptional condition that is of higher importance than the
>>exception currently pending. In that situation the intervening layer
>>can either ignore the important exception or force termination. That
>>is usually an unacceptable choice.
>
>
> Why can't the intermediate layer throw a new exception of a different
> type to indicate the more serious situation? For example
>
> try {
> low_level();
> }
> catch (...) { // Or whatever
> // Something VERY bad happens here.
> throw VeryBadThing();
> }
>
> It is true that the first exception is lost, but one imagines that it
> becomes irrelevant once the very bad thing happens. In any event the
> function containing the code above does not have to ignore the very bad
> event nor force termination. The event of lesser importance, however,
> does get ignored.

This technique requires a universal catch mechanism. That means it
intercepts and discards _all_ lower level exceptions, which is
unreasonable -- the lower level exception might be the more important
one. This can be addressed by having every[*] throw point know about
the precedence of all possible exceptions, but that's very complex and
essentially infeasible in a large program -- i.e., the kind of program
that needs rigorous exception handling in the first place.

[*]every means all of them. In a large program it can be very difficult
to determine what might be called by a destructor.

The problem is not deciding what to catch but what to discard. You have
to discard everything in order to replace it. Hiding errors is never a
good idea. And having every layer of software decide what should or
should not be forwarded to the upper code defeats the transparency that
exceptions with selective catching were designed to provide.

>
>
>>Would anyone care to work on a system where the interrupts were
>>arranged such that a higher priority interrupt was faced with the
>>choice of being ignored to forcing a reset (and thus a reboot) of the
>>machine?
>
>
> Your analogy with interrupts has a flaw. Exceptions are not like
> interrupts because the code that throws the exception can't be
> (directly) resumed. When the handler completes, the flow of control
> continues from a different place than from where the exception was
> thrown. The notion of "nested" exceptions doesn't seem to quite make
> sense the way the notion of nested interrupts does. The second exception
> simply subsumes the first in its effect.

The term nesting may be misleading. Both exceptions have to be handled.
So both have to propagate upward in search of a handler. Should
applications be throwing containers of exceptions and have the catchs
filter them selectively? Standard containers are some of the most
exception-sensitive code around. This complexity explodes!

Exceptions are only useful if they _simplify_ the handling of special
conditions. AFAICT they currently do not do that.

/tj3

Ariel Badichi

unread,
Feb 21, 2005, 4:52:56 PM2/21/05
to
adeht wrote:

> Michael Pryhodko wrote:
> [snip]


>
>> Auto_DB_connection::~Auto_DB_connection() throw()
>> {
>> . try
>> . {
>> . close();
>> . }
>> . catch(std::exception const& x)
>> . {
>> . // log: "during stack unwinding due to early exit (because "
>> . // "of 'exception' or 'return any_error') error happened"
>> . }
>> }
>

> [snip]


>
>> Personally I'd vote for beating every person who will write throwing
>> destructor, one hit in the head for every such destructor.
>

> [snip]
>
> *thump*
>
> You might get an exception thrown in the destructor. The fact that it
> will catch it has nothing to do with it. If this happens and another
> exception is already active, welcome to the Rigor Mortis Salloon.
>
> Same goes for Victoria Dassen's code.
>

Arf. I realize my mistake, sadly after posting.
We only reach the Rigor Mortis Salloon if we exit the destructor
with an exception.

Sorry.

White Wolf

unread,
Feb 21, 2005, 6:29:40 PM2/21/05
to
Trevor L. Jackson, III wrote:
[SNIP]
> The term nesting may be misleading. Both exceptions have to be handled.
> So both have to propagate upward in search of a handler. Should
> applications be throwing containers of exceptions and have the catchs
> filter them selectively? Standard containers are some of the most
> exception-sensitive code around. This complexity explodes!
>
> Exceptions are only useful if they _simplify_ the handling of special
> conditions. AFAICT they currently do not do that.

I still do not get the idea. Why is it more reasonable to accept that
handling errors is difficult (and exceptions only reflect that) than to ask
the language to provide several threads of execution (remember, exceptions
in C++ *are* terminating and for good reasons [D&E]16.6) to handle several
errors at the same time?

How could handling two errors be done? How can you make a failsafe system,
where doing A and undoing A is equally likely to fail?

[D&E] Bjarne Stroustrup: The Design and Evolution of C++
ISBN: 0-201-54330-3

--
WW aka Attila
:::
It is nice to be important, but it's more important to be nice.

White Wolf

unread,
Feb 21, 2005, 6:30:56 PM2/21/05
to
adeht wrote:
[SNIP]

> *thump*
>
> You might get an exception thrown in the destructor. The fact that it
> will catch it has nothing to do with it. If this happens and another
> exception is already active, welcome to the Rigor Mortis Salloon.

Call me a cuckoo, but I still think that my solution was the best so far.
;-)

And call me acrimonious (please do, I have no idea how that is
pronounced;-)), but I really think that whatever magic we will try C++ won't
be able to do two threads of execution in one thread of execution.

Let me say it another way, using a metaphors, as Steve McConnell has thaught
me that it is the easiest way to explain things:

C++ is a male programming language. Although Boost has made a definite step
to introduce female qualities with the tribool library, C++ is still unable
to do 7 things at the same time. So we have no choice, but to use male
strategy and tactics with C++, namely: postpone commitment to the handling
of subsequent errors, once we have started to handle one.

Another post suggested replacing the exception thrown with a "more
important" exception when that happens during stack unwinding. I cannot fit
that idea into my Vulcan logic. Why? In case the "more important" error
happens, we have just ignored/hidden the original error - the error, which
may have caused the "more important error" to happen. Ignoring errors is
bad, it results in way too long, way too frustrated and way too expensive
debugging sessions. Now I hear someone saying: but that first error is not
so important. OK, I ask then: So why did you throw that exception in the
first place, if you could just ignore that error?

I think there is not more magic to exception handling (in general) than
already said by Sutter (at least I think I did read it from him):

0.) Protect your resources/transaction with RAII, where destructor may not
fail

1.) First do you stuff which can fail

2.) Commit using operations, which cannot fail

(Committing may very well be dismissing a ScopeGuard, so it does not mean
any actual work done at that point.)

I sincerely believe that there is not more to it than the above. Into this
bag the problem should fit. So, either

- closing the database connection is an operation which can fail (in which
case it has no place to be in a destructor) and belongs to the first point

or

- not being able to close the database on stack unwinding indicates a
serious enough problem that it requires immediate aborting of the process
(terminate)

or

- you can later retry the closing of the database connection with a chance
that it will work

or

- you had exception safety in place, so when you are unwinding the stack you
could not care less, that you database connection cannot be closed

Can the latter be a valid choice? Of course. Your operation (using the
database) has *already* failed. If not being able to close the connection
does not mean any state corruption to your process (like resource leak) then
it is a sensible thing to ignore the error. I could say that this is the
case when the second error is "a consequence without consequences".

--
WW aka Attila
:::
Back when I was a boy, we carved our own IC's out of wood.

Gabriel Dos Reis

unread,
Feb 21, 2005, 6:32:55 PM2/21/05
to
"White Wolf" <wo...@freemail.hu> writes:

[...]

| I strongly believe that articles must not be judged based on assessing the
| quality of their content or message. I do believe however that in a
| moderated technical newsgroup, articles with zero technical content, but
| clear negative remarks should be requested to be reworded to contain some
| sort of technical reasoning. Otherwise the group can slowly shift into
| debating opinions, which can render it useless just as the slow shifting to
| the wrong letter of the alphabet did it for over a year.

That is a reasonable opinion. However, I'm not sure that if I were in
the shoes of clmc++ moderators and I had to apply the very idea you
just expressed, how I could have approved the very messages I'm replying
to -- or this one for that matter :-)

The point being I see no problem with the original message.
(I've read messages with *far more* inflamatory or negative contents
approved, but on average, our moderators are doing a pretty good
job).

| Of course, it is your call. I have made my point, if you still chose to
| accept articles that have only opinions in them, all I can and will do is to
| stay away from them. :-)

I believe they did it again, which gave me the opportunity to have a
better shape of the opinon your expressed earlier :-)

--
Gabriel Dos Reis
g...@integrable-solutions.net

David Abrahams

unread,
Feb 21, 2005, 6:31:40 PM2/21/05
to
"Trevor L. Jackson, III" <tl...@comcast.net> writes:

> The problem appears when an intermediate layer of the software detects
> an exceptional condition that is of higher importance than the exception
> currently pending. In that situation the intervening layer can either
> ignore the important exception or force termination.

Or it can handle the higher-importance exception immediately and allow
unwinding to continue for the one that's "currently pending."

I don't see how not using exceptions can possibly help solve this sort
of problem. There is a trivial mapping between using exceptions and
using other error-reporting mechanisms such as return codes. Anything
that's a _fundamental_ problem for a system with exceptions is going
to be a fundamental problem for a system using return codes.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

David Abrahams

unread,
Feb 21, 2005, 6:35:54 PM2/21/05
to
Scott Meyers <Use...@aristeia.com> writes:

> What if the destruction of the resource requires some exception handling,

> such as logging the exception, translating the exception to app-specific

> exception and returns to the caller, etc. For example, for a db
> connection, I might want to do: (quite frequently found in similar JDBC
> codes in Java world.)
>
> void doSomething()
> {
> Connection* conn = NULL;
> conn* = createConnection();
> // ... do something
> try {
> if (conn != NULL) {
> conn.close();
> }
> delete conn;
> conn = NULL;
> } catch (SQLException sqle) {
> // 1. log the exception in some log file.
> // 2. throw some app-specific exception or warning based on SQLException.
> }
> }
>
> Adding RAII for the DB connection is easy, and taking care of logging in
> the dtor is equally easy. But dealing with an exception arising from close
> is not easy. The connection-managing object could swallow the exception,
> but that doesn't seem right. It could translate the exception into a
> different error-reporting strategy (e.g., errno), but that loses the
> advantages of using exceptions for error reporting (e.g, they can't be
> ignored).

I don't buy that as an important advantage of using exceptions, and I
never have. Exceptions can be ignored just as easily as return codes,
and if ignored will just as quickly lead to incorrect code. You might
find out about it sooner with exceptions if you've ignored the
possibility of errors, but it's by no means guaranteed, especially if
you're not inclined to stimulate errors in your testing... which of
course is likely for anyone who's not paying attention to the
possibility of errors in the first place.

The big advantages of using exceptions are in expressivity,
cleanliness of code, and the ability to maintain strong invariants.

> We could move the call to close out of the dtor and into a different
> function that clients would be expected to call manually, but that
> would lose the advantages of RAII.

I have only a little to add to what Attila posted; it's a fairly
complete answer, with which I basically agree. There are,
unfortunately, no simple prescriptions and "it depends."

In this case you have to decide whether you think the call to close is
serious enough that it should cause stack unwinding. One simple test
is to ask, "should operations relying on doSomething (usually) be
aborted if close fails?"

If the answer is yes (unlikely IMO, but whatever) then it's reasonable
to use uncaught_exception in the destructor of the RAII object, and
*only* throw if you're not doing stack unwinding. Note that if you do
this you have to document that your RAII class does *not* have a
general nothrow destructor, but only an unwind-nothrow destructor.

I don't love uncaught_exception because it doesn't return true when
the exception is already being handled from within a catch block, even
if that catch block will rethrow:

catch( whatever )
{
do_something();
throw; // or throw something_else;
}

So if do_something is using one of these RAII objects, it might throw
an exception. But it's not as bad as it looks: we already knew
do_something to be a throwing function, so it needs attention in this
context just like any other throwing function would.

If you still don't like uncaught_exception, or it just makes you
nervous, *and* you think the close operation should cause unwinding,
another reasonable approach is to use an explicit close call for the
case where everything else has worked out okay, and save a
logging/swallowing RAII for "insurance" against errors (and
thoughtless programmers who forget to call close):


void doSomething()
{
Connection conn();
// ... do something
conn.close();
}

If the commented code causes unwinding, the connection will be closed
by RAII, and if there's an error doing the closing *in that case only*
you get logging and swallowing.

Still a thousand percent cleaner than the Java code you showed.

HTH,

White Wolf

unread,
Feb 21, 2005, 6:34:54 PM2/21/05
to
Roland Pibinger wrote:
> On 20 Feb 2005 17:47:12 -0500, "David B. Held"
> <dh...@codelogicconsulting.com> wrote:
[SNIP]

>> handled by a 2-fault or better failfast system. Basically, we
>> would need to be allowed to have two active d'tors at once.
>
> If this were so one could not write functions with transactional
> semantics (commit, rollback) in C++.

Furthermore if it was so, C++ would need to provide more than one thread of
execution, and magically so.

Also, I would not like to be a fly on the wall when someone finds out that
his system is crashing because during stack unwinding object A (in its
destructor) calls virtual member functions of object B when object B happens
to be already half destroyed (its type is one of its abstract bases), and
the functions being called happen to be pure virtuals in that very moment.
But only on Fridays when the Moon is full...

--
WW aka Attila
:::
Think twice before you speak, especially if you intend to say what you
think.

Dietmar Kuehl

unread,
Feb 22, 2005, 9:35:31 AM2/22/05
to
Trevor L. Jackson, III wrote:
> Dietmar Kuehl wrote:
>
>> I just want to comment on the non-technical part of this article:

>> We definitely agree on this part. However, the general statement that


>> destructors cannot throw would indeed be a strong limitation of the
>> language if it were true (which it is not: the key to use
>> 'uncaught_exception()' to detect whether it is safe to throw a new
>> exception or whether another exception also needs to be taken care
>> of):
>> it is not just a plain complaint about something but it is rooted in a
>> technical concern. Of course, deducing that exceptions are unusable in
>> C++ due to such a limitation, even if it were true, is a little bit
>> strong but not necessarily unreasonable.
>
> The paragraph above makes certain statmes that are fundamentally
> technical rather than non-technical.

Yes, I needed to add some technical stuff to the explain the
non-technical
issues. It is, however, not a full-blown technical discussion. However:

> So I am taking the libery of
> taking them out of the original context set by the introductory
> statement.

I have no problems with this and discussing the technical issues, of
course.

> I dispute the claim that uncaught_exception() is a sufficient
> foundation
> on which to solve (in some sense of the term "solve") the issue of
> colliding exceptions.

It does not on its own offer a solution to the problems involved but I
think it provides the necessary foundation to create a mechanism for
exception chaining. That is, it is no sufficient but provides the
necessary parts on side of the language definition. Whether the language
should offer the infrastructure you can create upon the existing tools
is another issue, though. Of course, the standardization committee
should
not do inventive work and create such an infrastructure but should,
eventually, standardize the common approach :-)

> For example, given an exception from a low level function, say
> bad_alloc
> or something else that is exceptional, but not fatal, the superposed
> layers of software would be unwound until some layer is reached that
> has
> declared its intention to deal with a set of exceptions including
> bad_alloc (or whatever).
>
> The problem appears when an intermediate layer of the software detects
> an exceptional condition that is of higher importance than the
> exception
> currently pending. In that situation the intervening layer can either
> ignore the important exception or force termination. That is usually
> an
> unacceptable choice.

These two options are, of course, not exhaustive and this is where
'uncaught_exception()' enters the picture. First off, note that we are
only discussing exceptions propagating from destructors: this is the
only case where multiple exceptions may be on their way. The number of
simultaneous exceptions currently being unhandled is actually unbounded
but except for the first exception being thrown all other exception are
thrown from within a destructor [and would cause program termination if
they escape their respective destructor]. We are also not discussing the
two trivial cases for exceptions thrown from within a destructor (i.e.
the ones we can safely ignore or the ones which should case program
termination; these two cases can be simply handled using
'uncaught_exception()' and taking appropriate action in case this
function yields 'true' or even taking appropriate action
unconditionally).
That is, we are only discussing the cases where an interesting and
possibly important exception should be propagated despite another
exception already being propagated.

The destructor has no option to change the exception currently being
propagated nor has it any access to this exception. The only information
the destructor gets is whether another exception is currently being
unwound or not, i.e. the result of 'uncaught_exception()'. The exception
being thrown with the destructor can without limiting it to special
cases
be assumed to be derived from a suitable class, e.g. something like this
(possibly also adding more information, e.g. some form of "priority" or
a
function throwing the object):

namespace err {
struct exception: std::exception {
virtual std::auto_ptr<err::exception> clone() const = 0;
};
}

(exceptions not derived from this class can be handled in suitable
function called from a 'catch(...)' which converts at least all
interesting exceptions and probably handles all others in an appropriate
form; if you need more details on it, I can explain it but I think it
just distracts from the actual discussion).

Any destructor thrown a possibly interesting exception which should
neither be ignored nor cause program termination would be written like
this:

T::~T()
{
try { /* clean up */ }
catch (err::exception const& ex)
{
if (!std::uncaught_exception())
throw; // it is the only exception on the way: just rethrow
err::queue.push_back(ex.clone());
}
}

where 'err::queue' would be a declaration like this:

namespace err {
typedef std::list<std::tr1::shared_ptr<err:exception>::iterator
iterator;
std::list<std::tr1::shared_ptr<err::exception> > queue;
}

This global variable would be located in thread local storage on in a
multi-threaded system and probably be accessed using a function but to
keep the discussion focused a simple global variable should do.

Now, any intermediate layer which wants to handle some exceptions which
might have been thrown during destruction cause by an uncaught exception
can catch exceptions and deal with appropriate exceptions from the
queue:

try { /*...*/ }
catch (...)
{
for (err::iterator it = err::queue.begin(), end = err::queue.end();
it != end; ++it)
if (std::tr1::shared_ptr<interesting_exception const& ex =
std::tr1::dynamic_pointer_cast<interesting_exception>(*it))
{
// handle the exception and probably remove it from the queue
// afterwards
}
throw; // ... and there was another exception on its way: rethrow
}

The details of the exception handling can be tailored to the needs of
the application. For example, I can imagine situations where the current
exception is not rethrown or where a more efficient approach to locating
interesting exceptions in the list would be desirable (of course, since
exceptions should be exceptional performance should not be a critical
aspect but mediocre performance is also undesirable).

> Would anyone care to work on a system where the interrupts were
> arranged
> such that a higher priority interrupt was faced with the choice of
> being
> ignored to forcing a reset (and thus a reboot) of the machine? Of
> course not. Analogies are slippery, but I don't think this one is a
> distortion.

... but it is based on wrong premises, namely that you can only either
ignore the exception or terminate the application. The above sketch of
an error handling framework provides a third option. It is surely not
really convenient but it is also only rarely necessary (e.g. in the
applications I have programmed I never had the need to do something like
this).

> Interrupts achieve this transparency by nesting or queueing.
> Exceptions
> don't.

If you want nesting or queuing, you can have it: see above.

> In the absence of neither nesting nor queueing I suggest that
> replacement based on a precedence hierarchy is the least functionality
> on which a robust system could depend.

... and it is your option to do so!

> Clearly an application could build an exception framework that would
> allow chaining or queueing and even impose a priority or filtering
> scheme upon the collection of pending exceptions. But the only
> argument
> against moving that capability out of the application and into the
> language (or libraries) would be an overhead issue (probably both space
> and time). But in almost every case that overhead is avoidable. In
> the
> few cases where it is unavoidable it can probably (more theory than
> fact
> here) be justified on the basis of reliability.

Let me quote a certain '"Trevor L. Jackson, III" <tl...@comcast.net>' in
this same thread (message ID: <WYWdnS0DeOT...@comcast.com>):

But a lot of it is due to the fact that the language is not defined as

a standardaization of existing practice. It is more of a language


design exercise, and parts of that design are simply non-functional.

This quote seems to be a criticism of the standardization committee,
i.e. that it invents things not in common use. It looks to me like there
are conflicting opinions held by the same person with respect to what
the
standardization committee does: a little bit of consistency would, IMO,
be in order, i.e. you can either wish for standardizing existing
practice
or you can wish for inventions addressing problems but not for both (of
course, it is likely that the committee goes down the latter path anyway
but this is a different issue).


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

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

Dietmar Kuehl

unread,
Feb 22, 2005, 9:36:41 AM2/22/05
to
adeht wrote:
> You might get an exception thrown in the destructor. The fact that it
> will catch it has nothing to do with it. If this happens and another
> exception is already active, welcome to the Rigor Mortis Salloon.

You seem to imply that throwing an exception within a destructor, even
if it is caught before leaving the destructor, causes the program to
terminate. This implication is wrong: the program is only terminated
if an exception escapes the destructor! You can freely throw exceptions
within the destructor without any problems as long as they don't escape
from the destructor. See 15.5.1 (except.terminate) paragraph 1, third
bullet and/or the note in 15.2 (except.ctor) paragraph 3 for details.


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

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

Dietmar Kuehl

unread,
Feb 22, 2005, 9:37:36 AM2/22/05
to
Peter C. Chapin wrote:
> Why can't the intermediate layer throw a new exception of a different
> type to indicate the more serious situation?

Because the intermediate layer consists of a destructor called during
stack unwinding! In this case, you have no access to the exception being
thrown except that you can detect that stack unwinding is in progress by
calling 'std::uncaught_exception()'. The destructor cannot catch the
current exception.


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

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

White Wolf

unread,
Feb 22, 2005, 3:47:51 PM2/22/05
to
David Abrahams wrote:
[SNIP]

> I don't buy that as an important advantage of using exceptions, and I
> never have. Exceptions can be ignored just as easily as return codes,
> and if ignored will just as quickly lead to incorrect code.
[SNIP]

I believe that what Scott meant was that return codes can be ignored
/implicitly/, while to ignore exceptions you have to write /explicit/ code.

> I have only a little to add to what Attila posted; it's a fairly
> complete answer, with which I basically agree. There are,
> unfortunately, no simple prescriptions and "it depends."

[SNIP]

Huh. I have to update my CV! :-)

[SNIP]


> If the answer is yes (unlikely IMO, but whatever) then it's reasonable
> to use uncaught_exception in the destructor of the RAII object, and
> *only* throw if you're not doing stack unwinding.

I see Herb Sutter half waking up in his first class seat en-route to the
next Acme (kidding!) meeting, and mutter 'Pick a lane, *any* lane!'

My belief is that if you cannot ignore that error on close in "success"
case, then you cannot fully ignore it in failure case either. I mean not if
RAII does both close operations.

I cannot imagine a situation where close can fail, and this whole situation
can be solved by simple means. By simple I mean language level. If close
can fail then rollback *and* commit can *both* fail(*), and in that case one
*must* have some local emergency non-volatile storage where the
rollback/commit request can survive the process.

(*) Please remember, we are not talking about the database handle being
overwritten by off-by-one errors because that case should abort, but we talk
about errors in the close operation.

I for one (even with that local storage) won't like to be on an aircraft
handled by that software setup. You *must* to have enough logic also on the
other end, so that recovery will be possible. That requires tedious design.
I know. Been there, done that. And my case was simple.

> Note that if you do
> this you have to document that your RAII class does *not* have a
> general nothrow destructor, but only an unwind-nothrow destructor.

Herb is now fully awake. :-)

[SNIP]


> If you still don't like uncaught_exception, or it just makes you
> nervous, *and* you think the close operation should cause unwinding,
> another reasonable approach is to use an explicit close call for the
> case where everything else has worked out okay, and save a
> logging/swallowing RAII for "insurance" against errors (and
> thoughtless programmers who forget to call close):

[SNIP]

I like that one for some reason.

> Still a thousand percent cleaner than the Java code you showed.

(Homer Simpson's voice:)
Hmmmm. Language war.

I think I fail my own tagline:

--
WW aka Attila
:::
Everything should be made as simple as possible, but no simpler.

Ben Hutchings

unread,
Feb 22, 2005, 3:50:26 PM2/22/05
to
Trevor L. Jackson, III wrote:
<snip>
> I dispute the claim that uncaught_exception() is a sufficient foundation
> on which to solve (in some sense of the term "solve") the issue of
> colliding exceptions.
>
> For example, given an exception from a low level function, say bad_alloc
> or something else that is exceptional, but not fatal, the superposed
> layers of software would be unwound until some layer is reached that has
> declared its intention to deal with a set of exceptions including
> bad_alloc (or whatever).
>
> The problem appears when an intermediate layer of the software detects
> an exceptional condition that is of higher importance than the exception
> currently pending. In that situation the intervening layer can either
> ignore the important exception or force termination. That is usually an
> unacceptable choice.

Cleanup code generally shouldn't be doing anything so risky that that
can happen. On the rare occasion that it must, the cleanup code can
be called from a catch block and not a destructor:

try
{
...
}
catch (const important_exception &)
{
try
{
cleanup();
}
catch (const std::exception & e)
{
LOG_EXCEPTION(e);
}
throw;
}
catch (const std::exception &)
{
cleanup();
}

Admittedly that's a fair bit of code to write, but I'm unconvinced
that it is commonly needed.

> Would anyone care to work on a system where the interrupts were arranged
> such that a higher priority interrupt was faced with the choice of being
> ignored to forcing a reset (and thus a reboot) of the machine? Of
> course not. Analogies are slippery, but I don't think this one is a
> distortion.

<snip>

I think it is. Interrupts are asynchronous and return to the original
thread of execution that they interrupted. Exceptions actually modify
the thread of execution. Perhaps you're misapplying exceptions because
you see a false analogy.

--
Ben Hutchings
Having problems with C++ templates? Your questions may be answered by
<http://womble.decadentplace.org.uk/c++/template-faq.html>.

White Wolf

unread,
Feb 22, 2005, 3:52:15 PM2/22/05
to
Gabriel Dos Reis wrote:
> "White Wolf" <wo...@freemail.hu> writes:
>
> [...]
>
>> I strongly believe that articles must not be judged based on assessing
>> the quality of their content or message. I do believe however that in a
>> moderated technical newsgroup, articles with zero technical content, but
>> clear negative remarks should be requested to be reworded to contain
>> some sort of technical reasoning. Otherwise the group can slowly shift
>> into debating opinions, which can render it useless just as the slow
>> shifting to the wrong letter of the alphabet did it for over a year.
>
> That is a reasonable opinion. However, I'm not sure that if I were in
> the shoes of clmc++ moderators and I had to apply the very idea you
> just expressed, how I could have approved the very messages I'm replying
> to -- or this one for that matter :-)

We completely agree on that. So if what I ask in my first message is true,
then it means we are not talking now, since it has never exited. But if it
has never exited, we cannot know if it is true or not, so it cannot effect
us... Uhoh, someone has opened the door and I have died. ;-)

> The point being I see no problem with the original message.
> (I've read messages with *far more* inflamatory or negative contents
> approved, but on average, our moderators are doing a pretty good
> job).

My problem was not the inflamatory nature per se, but that it has stated
many "conclusions" based on nothing. I mean nothing in the post itself. I
am sure the one who has posted it has his reasons why he has written what he
did. I just would like to get those reasons *in* the posts containing the
conclusions. Otherwise there is no technical discussion.

Or we can do it the other way: if someone has to do it and nobody does it
everybody will do it..., so a post like that appears and then the next day
you will have 42 identical followups saying: "Why do you say that?" I would
prefer the moderators asking this question, once. :-)

>> Of course, it is your call. I have made my point, if you still chose to
>> accept articles that have only opinions in them, all I can and will do
>> is to stay away from them. :-)
>
> I believe they did it again, which gave me the opportunity to have a
> better shape of the opinon your expressed earlier :-)

It is 4 dimensional now, so I cannot understand it anymore. Now I go and
look for something else to get fixated on. ;-)

--
WW aka Attila
:::
Unix is user friendly - it's just picky about it's friends.

White Wolf

unread,
Feb 22, 2005, 3:50:49 PM2/22/05
to
David Abrahams wrote:
> "Trevor L. Jackson, III" <tl...@comcast.net> writes:
>
>> The problem appears when an intermediate layer of the software detects
>> an exceptional condition that is of higher importance than the exception
>> currently pending. In that situation the intervening layer can either
>> ignore the important exception or force termination.
>
> Or it can handle the higher-importance exception immediately and allow
> unwinding to continue for the one that's "currently pending."
>
> I don't see how not using exceptions can possibly help solve this sort
> of problem. There is a trivial mapping between using exceptions and
> using other error-reporting mechanisms such as return codes. Anything
> that's a _fundamental_ problem for a system with exceptions is going
> to be a fundamental problem for a system using return codes.

I am very happy to read this, because before your post I have not seen
anyone else (but me) stating this. (Or I have managed to miss it.)

I just think that while I am convinced this is true (and so seem you) we
need to come up with some sort of formal proof of this. So far I see people
not buying into the idea. But I am stuck on where to go, since as long as I
am not presented a fully known case to solve, I cannot tell more then I did
(twice already).

I assume that Scott's problem is solved by one or the combination of the 3
ways I have presented (die,ignore,postpone). Yet, I saw people insisting
that there are serious problems with exception handling, and we shouöd be
able to handle two exceptions at the same time. But they have failed to
present any such example, which would have demonstrated this.

So my suggestion is that we cross the river when we get there. Since it is
close to impossible to prove that something does not exist (although in
this case I think it could, but probbaly would take better part of a book) I
have another suggestion. If someone posts here an error handling problem,
which cannot be solved satisfactorily using C++ exceptions but can be fully
solved using exceptions in another language, or by other error handling
mechanisms in C++ or another language, then, and only then, we shall look
into this any further.

But as long as there is no problem to solve, let's not solve it. :-)

Again, since I have not received any followups to my first post into this
thread (Message-ID: <cv92g1$4tv$1...@phys-news1.kolumbus.fi>) I assume that my
post has solved Scott's original problem, so we have no more problems to
solve. If my post did not solve it, now is the time to state why do you, I
mean any of you, thinks so.

--
WW aka Attila
:::
Nothing in life is to be feared. It is only to be understood. - Marie Curie

Gabriel Dos Reis

unread,
Feb 22, 2005, 3:53:44 PM2/22/05
to
David Abrahams <da...@boost-consulting.com> writes:

| "Trevor L. Jackson, III" <tl...@comcast.net> writes:
|
| > The problem appears when an intermediate layer of the software detects
| > an exceptional condition that is of higher importance than the exception
| > currently pending. In that situation the intervening layer can either
| > ignore the important exception or force termination.
|
| Or it can handle the higher-importance exception immediately and allow
| unwinding to continue for the one that's "currently pending."
|
| I don't see how not using exceptions can possibly help solve this sort
| of problem. There is a trivial mapping between using exceptions and
| using other error-reporting mechanisms such as return codes. Anything
| that's a _fundamental_ problem for a system with exceptions is going
| to be a fundamental problem for a system using return codes.

That probably needs qualification.

A distinctive property (feature?) of system usiing return codes is
that a return code can be ignored

--
Gabriel Dos Reis
g...@integrable-solutions.net

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

White Wolf

unread,
Feb 22, 2005, 3:50:04 PM2/22/05
to
stork wrote:
[SNIP]

> #ifdef DEBUG
> __asm { int 3 }
> #endif
[SNIP]

I assume the above was not meant to be example of standard or even portable
C++ code? :-)

For those who may not know (well, I guess there is nobody, but...) int 3 is
the assembler for the debugger-invoking-soft-interrupt of Intel systems. At
least as I recall. :-)

--
WW aka Attila
:::
My operat~1 system unders~1 long filena~1 , does yours?

Trevor L. Jackson, III

unread,
Feb 22, 2005, 3:56:35 PM2/22/05
to
White Wolf wrote:

I didn't. Somebody else did. Have you ever worked on a project large
enough that there were developers whose name you did not know? I
suspect you might find it enlightening.

/tj3

Trevor L. Jackson, III

unread,
Feb 22, 2005, 3:57:22 PM2/22/05
to
White Wolf wrote:
> Trevor L. Jackson, III wrote:
> [SNIP]
>
>>The term nesting may be misleading. Both exceptions have to be handled.
>> So both have to propagate upward in search of a handler. Should
>>applications be throwing containers of exceptions and have the catchs
>>filter them selectively? Standard containers are some of the most
>>exception-sensitive code around. This complexity explodes!
>>
>>Exceptions are only useful if they _simplify_ the handling of special
>>conditions. AFAICT they currently do not do that.
>
>
> I still do not get the idea. Why is it more reasonable to accept that
> handling errors is difficult (and exceptions only reflect that)

no exceptions complicate that. Cpde that might be called from a
destructor executes in two distinct modes: the normal mode and the
unwind mode. In both modes special conditions can arise that
could/should be handled by throwing an exception. but in the unwind
mode exceptions are not available. So that code has to use some other
mechanism to handle those special conditions.

Given thatit can be very difficult to determine where the boundary
between bimodal and normal code lies, it is easier to avoid exceptions
entirely than it is to avoid colliding exceptions.


> than to ask
> the language to provide several threads of execution (remember, exceptions
> in C++ *are* terminating

No they are not.

> and for good reasons [D&E]16.6) to handle several
> errors at the same time?

I did not ask for multiple threads of execution. I asked for exeptions
to be universally available. I.e., a fundamental of the language,
rather than a frill suitable only for programs small enough to fint into
one person's head.

The reason we need to handle several errors at the same time is that our
error handling code might encounter an error. If your error handling
code is trivial this is not an issue. But if your error handling code
is non-trivial it is a very real issue.

>
> How could handling two errors be done?

Is that a serious question? There are dozens of possbible approaches to
the issue.

? How can you make a failsafe system,
> where doing A and undoing A is equally likely to fail?

You cannot. So what?

/tj3

Trevor L. Jackson, III

unread,
Feb 22, 2005, 3:55:47 PM2/22/05
to
David Abrahams wrote:

> "Trevor L. Jackson, III" <tl...@comcast.net> writes:
>
>
>>The problem appears when an intermediate layer of the software detects
>>an exceptional condition that is of higher importance than the exception
>>currently pending. In that situation the intervening layer can either
>>ignore the important exception or force termination.
>
>
> Or it can handle the higher-importance exception immediately and allow
> unwinding to continue for the one that's "currently pending."

OK, maybe I am missing something. How do I negate the unwinding mode
long enough to call a routine that might throw so I can catch it, handle
it, and then resume unwinding?

My understaning is that uncaught_exception() is a read-only predicate.

/tj3

David B. Held

unread,
Feb 22, 2005, 3:58:35 PM2/22/05
to
Roland Pibinger wrote:
> On 20 Feb 2005 17:47:12 -0500, "David B. Held"
> <dh...@codelogicconsulting.com> wrote:
>>[...]

>>The C++ exception-handling and destruction mechanism is 1-fault
>>failfast for dense faults. However, a failing close operation
>>that must follow a failing member operation can only be gracefully
>>handled by a 2-fault or better failfast system. Basically, we
>>would need to be allowed to have two active d'tors at once.
>
> If this were so one could not write functions with transactional
> semantics (commit, rollback) in C++.

Obviously, I meant "two active exceptions", not d'tors.

Dave

ka...@gabi-soft.fr

unread,
Feb 22, 2005, 4:11:55 PM2/22/05
to
adeht wrote:
> Michael Pryhodko wrote:
> [snip]
> > Auto_DB_connection::~Auto_DB_connection() throw()
> > {
> > . try
> > . {
> > . close();
> > . }
> > . catch(std::exception const& x)
> > . {
> > . // log: "during stack unwinding due to early exit (because
"
> > . // "of 'exception' or 'return any_error') error
happened"
> > . }
> > }
> [snip]
> > Personally I'd vote for beating every person who will write
> > throwing destructor, one hit in the head for every such
> > destructor.
> [snip]

> *thump*

> You might get an exception thrown in the destructor. The fact
> that it will catch it has nothing to do with it. If this
> happens and another exception is already active, welcome to
> the Rigor Mortis Salloon.

Not according to the standard. Nor the compilers I've tried it
with. Terminate is only called if the destructor is exited via
an exception.

I happen to think that Michael's code is right on target.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Nicola Musatti

unread,
Feb 22, 2005, 4:19:39 PM2/22/05
to

Scott Meyers wrote:
[...]
> C++ uses RAII to avoid leaking resources, and destructors don't throw
> because it makes things all icky if they do. No problem, I'm on
board.

No. Well designed C++ uses RAII to provide a complete, consistent
abstraction over a lower level resource handling mechanism.

> But then I get questions like this:


>
> What if the destruction of the resource requires some exception
handling,
> such as logging the exception, translating the exception to
app-specific
> exception and returns to the caller, etc.

You have to decide which guarantees your abstraction is going to
promise. The kind of problem you describe is usually related to
resources that cache requests and process them in a different point in
time. In a similar situation there are basically two alternatives you
can choose from: you either accept that your requests have a small
probability of failing or you guarantee that your clients will be
notified upon failure. In the first case you implement your requests in
a straightforward way and throw exceptions only when you are directly
notified of failure conditions by the underlying resource; the resource
will be released in the destructor at which point any errors will be
ignored. In the second case your only strategy is to acquire and
release your resource before and after each request, so as to ensure
that you get to know if your request was really processed successfully
or not.

The great thing about RAII is the ability of switching between the two
strategies without changing your interface. To notify your clients of
release errors is really a breach of encapsulation.

Cheers,
Nicola Musatti

Bob Bell

unread,
Feb 22, 2005, 4:45:39 PM2/22/05
to
Trevor L. Jackson, III wrote:
> The term nesting may be misleading. Both exceptions have to be
handled.
> So both have to propagate upward in search of a handler. Should
> applications be throwing containers of exceptions and have the catchs

> filter them selectively? Standard containers are some of the most
> exception-sensitive code around. This complexity explodes!

I'm very interested in your response to the question posed by David
Abrahams. How do you handle this situation when you're not using
exceptions?

Bob

David Abrahams

unread,
Feb 22, 2005, 6:09:25 PM2/22/05
to
"Trevor L. Jackson, III" <tl...@comcast.net> writes:

> David Abrahams wrote:
>
>> "Trevor L. Jackson, III" <tl...@comcast.net> writes:
>>
>>
>>>The problem appears when an intermediate layer of the software detects
>>>an exceptional condition that is of higher importance than the exception
>>>currently pending. In that situation the intervening layer can either
>>>ignore the important exception or force termination.
>>
>>
>> Or it can handle the higher-importance exception immediately and allow
>> unwinding to continue for the one that's "currently pending."
>
> OK, maybe I am missing something. How do I negate the unwinding mode
> long enough to call a routine that might throw so I can catch it, handle
> it, and then resume unwinding?

I don't know what "negate the unwinding mode" means. You can enter a
try/catch block from anywhere, including a destructor that is being
called as part of stack unwinding. Any exceptions thrown from within
the try clause can be handled in the catch clause. When the
destructor completes, unwinding continues.

> My understaning is that uncaught_exception() is a read-only predicate.

Not sure what that has to do with anything, but you could say
uncaught_exception is "written" by throwing (which sets it to true)
and catching (which sets it to false). But again, I don't think it's
relevant.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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

David Abrahams

unread,
Feb 22, 2005, 6:28:51 PM2/22/05
to
Gabriel Dos Reis <g...@integrable-solutions.net> writes:

> David Abrahams <da...@boost-consulting.com> writes:
>
> | "Trevor L. Jackson, III" <tl...@comcast.net> writes:
> |
> | > The problem appears when an intermediate layer of the software detects
> | > an exceptional condition that is of higher importance than the exception
> | > currently pending. In that situation the intervening layer can either
> | > ignore the important exception or force termination.
> |
> | Or it can handle the higher-importance exception immediately and allow
> | unwinding to continue for the one that's "currently pending."
> |
> | I don't see how not using exceptions can possibly help solve this sort
> | of problem. There is a trivial mapping between using exceptions and
> | using other error-reporting mechanisms such as return codes. Anything
> | that's a _fundamental_ problem for a system with exceptions is going
> | to be a fundamental problem for a system using return codes.
>
> That probably needs qualification.
>
> A distinctive property (feature?) of system usiing return codes is
> that a return code can be ignored

As I have posted elsewhere in this thread, you can ignore exceptions
too. The behavior of exceptions when ignored doesn't change the
picture very much for writing correct code, especially in a large
system.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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

David Abrahams

unread,
Feb 22, 2005, 6:29:13 PM2/22/05
to
"White Wolf" <wo...@freemail.hu> writes:

> David Abrahams wrote:
> [SNIP]
>> I don't buy that as an important advantage of using exceptions, and I
>> never have. Exceptions can be ignored just as easily as return codes,
>> and if ignored will just as quickly lead to incorrect code.
> [SNIP]
>
> I believe that what Scott meant was that return codes can be ignored
> /implicitly/, while to ignore exceptions you have to write /explicit/ code.

Not really. If you ignore exceptions when writing code, your program
may terminate unexpectedly. But that could happen if you ignore error
conditions reported in other ways just as easily:

int* p = new (nothrow) int;
*p = 42; // boom?

In fact, a completely ignored exception at least leads to a kind of
orderly shutdown, so in some ways it's less obtrusive than what could
happen if you ignore other kinds of error conditions.

>> I have only a little to add to what Attila posted; it's a fairly
>> complete answer, with which I basically agree. There are,
>> unfortunately, no simple prescriptions and "it depends."
> [SNIP]
>
> Huh. I have to update my CV! :-)

By all means.

> [SNIP]
>> If the answer is yes (unlikely IMO, but whatever) then it's reasonable
>> to use uncaught_exception in the destructor of the RAII object, and
>> *only* throw if you're not doing stack unwinding.
>
> I see Herb Sutter half waking up in his first class seat en-route to
> the next Acme (kidding!) meeting, and mutter 'Pick a lane, *any*
> lane!'

Driving all over the road without respect for lane markers is a
characteristic of Boston drivers that I try hard to avoid. Is Herb
dreaming about driving behind me on his way to a meeting with Bill,
for which he is late??

> My belief is that if you cannot ignore that error on close in
> "success" case, then you cannot fully ignore it in failure case
> either.

Note that I never suggested _ignoring_ the condition.

> I mean not if RAII does both close operations.
>
> I cannot imagine a situation where close can fail, and this whole
> situation can be solved by simple means. By simple I mean language
> level. If close can fail then rollback *and* commit can *both*
> fail(*),

For the record: my answer was not tuned to the specific problem of
database accesses. In that particular case, I probably agree with
you (depending on the characteristics of the database system).

> and in that case one *must* have some local emergency non-volatile
> storage where the rollback/commit request can survive the process.

Yes, and building a database on a system like that -- where close can
fail for reasons other than programmer error or gamma rays changing
the bits in memory -- is a tricky business indeed.

> (*) Please remember,

To be fair, there's nothing to remember. You're setting these
parameters right here...

> we are not talking about the database handle
> being overwritten by off-by-one errors because that case should
> abort,

Whether or not that should abort is a decision that has to be made on
an application-by-application basis... but abort is a very good
default. The class of applications where it makes sense to try to
muddle on even after a programmer error has messed things up is very
small.

> but we talk about errors in the close operation.

> I for one (even with that local storage) won't like to be on an
> aircraft handled by that software setup. You *must* to have enough
> logic also on the other end, so that recovery will be possible.
> That requires tedious design. I know. Been there, done that. And
> my case was simple.

I believe you. Sounds messy.

>> Note that if you do this you have to document that your RAII class
>> does *not* have a general nothrow destructor, but only an
>> unwind-nothrow destructor.
>
> Herb is now fully awake. :-)

I don't really feel bad about disturbing Herb's rest, especially if he
is flying in first class.

> [SNIP]
>> If you still don't like uncaught_exception, or it just makes you
>> nervous, *and* you think the close operation should cause unwinding,
>> another reasonable approach is to use an explicit close call for the
>> case where everything else has worked out okay, and save a
>> logging/swallowing RAII for "insurance" against errors (and
>> thoughtless programmers who forget to call close):
> [SNIP]
>
> I like that one for some reason.

That's interesting; it should be equivalent to my first suggestion.

>> Still a thousand percent cleaner than the Java code you showed.
>
> (Homer Simpson's voice:)
> Hmmmm. Language war.

Naw; I was just trying to say that if you do this you don't really
give up the main advantages of RAII and C++ exceptions.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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

Scott Meyers

unread,
Feb 22, 2005, 9:26:06 PM2/22/05
to
On 22 Feb 2005 16:19:39 -0500, Nicola Musatti wrote:
> Scott Meyers wrote:
> [...]
> > C++ uses RAII to avoid leaking resources, and destructors don't throw
> > because it makes things all icky if they do. No problem, I'm on
> board.
>
> No. Well designed C++ uses RAII to provide a complete, consistent
> abstraction over a lower level resource handling mechanism.

I disagree. The primary motivation for RAII is to make sure that something
gets done when a scope is exited, regardless of how it is exited. Usually
(but not always), the thing to be done is release a resource.

My feeling is that RAII is *not* an abstraction mechanism, because usually
it's used only to wrap a resource whose release we want to ensure. The
resource might be memory, a font, a brush, a database connection, etc., but
code using RAII will often want to get at the underlying resource so that
it can use it with other APIs. That is, the user of an RAII-wrapped font
will need access to the font, the user of an RAII-wrapped brush will need
access to the brush, the user of an RAII wrapped pointer to dynnamically
allocated memory may need access to the pointer, etc. RAII doesn't
encapsulate or abstract, it just automates. At least that's my view.

Scott

Peter C. Chapin

unread,
Feb 22, 2005, 9:25:22 PM2/22/05
to
Dietmar Kuehl <dietma...@yahoo.com> wrote in news:37v1sdF5fv0ivU2
@individual.net:

>> Why can't the intermediate layer throw a new exception of a different
>> type to indicate the more serious situation?
>
> Because the intermediate layer consists of a destructor called during
> stack unwinding!

Yes, I think I was half asleep when I read the earlier postings in this
thread. I apologize for increasing the noise level in the group.

Peter

Gabriel Dos Reis

unread,
Feb 23, 2005, 3:26:31 PM2/23/05
to
David Abrahams <da...@boost-consulting.com> writes:

| Gabriel Dos Reis <g...@integrable-solutions.net> writes:
|
| > David Abrahams <da...@boost-consulting.com> writes:
| >
| > | "Trevor L. Jackson, III" <tl...@comcast.net> writes:
| > |
| > | > The problem appears when an intermediate layer of the software detects
| > | > an exceptional condition that is of higher importance than the exception
| > | > currently pending. In that situation the intervening layer can either
| > | > ignore the important exception or force termination.
| > |
| > | Or it can handle the higher-importance exception immediately and allow
| > | unwinding to continue for the one that's "currently pending."
| > |
| > | I don't see how not using exceptions can possibly help solve this sort
| > | of problem. There is a trivial mapping between using exceptions and
| > | using other error-reporting mechanisms such as return codes. Anything
| > | that's a _fundamental_ problem for a system with exceptions is going
| > | to be a fundamental problem for a system using return codes.
| >
| > That probably needs qualification.
| >
| > A distinctive property (feature?) of system usiing return codes is
| > that a return code can be ignored
|
| As I have posted elsewhere in this thread, you can ignore exceptions
| too.

Which terminates the execution of the program. That is a key difference.
(Unless by ignore the exceptions you mean an empty catch-all block;
but I don't consider that ignoring the exception).

| The behavior of exceptions when ignored doesn't change the
| picture very much for writing correct code, especially in a large
| system.

I disagree.

A system with return code can be flexible with respect to "severity"
of the state of computation being signaled -- e.g. a return code can
be ignored if the situation is not too severe and the flow of
computation continues until some checking point.

To see what I mean, consider the example (from existing practice) of
floating point systems with NaNs vs. floating point systems with
exceptions. You can argue for the merits of each systems but the
picture does change with respect to writing correct code with either
of them

--
Gabriel Dos Reis
g...@integrable-solutions.net

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

David Abrahams

unread,
Feb 23, 2005, 3:27:23 PM2/23/05
to
Scott Meyers <Use...@aristeia.com> writes:

> My feeling is that RAII is *not* an abstraction mechanism, because usually
> it's used only to wrap a resource whose release we want to ensure. The
> resource might be memory, a font, a brush, a database connection, etc., but
> code using RAII will often want to get at the underlying resource so that
> it can use it with other APIs. That is, the user of an RAII-wrapped font
> will need access to the font, the user of an RAII-wrapped brush will need
> access to the brush, the user of an RAII wrapped pointer to dynnamically
> allocated memory may need access to the pointer, etc. RAII doesn't
> encapsulate or abstract, it just automates. At least that's my view.

Well, now you're getting into a very fuzzy area: "what's the real
meaning of RAII?" We've had that debate here in the past
(http://tinyurl.com/4alvy). Apparently I held a minority view...

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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

Attila Feher

unread,
Feb 23, 2005, 3:38:42 PM2/23/05
to
Trevor L. Jackson, III wrote:
> White Wolf wrote:
>> adeht wrote:
[SNIP]
>> then: So why did you throw that exception in the first place, if
>> you could just ignore that error?
>
> I didn't. Somebody else did.

May that be the reason that I did not answer *you*, but someone else?

> Have you ever worked on a project large
> enough that there were developers whose name you did not know?

Only for about 7-8 years.

> I suspect you might find it enlightening.

I find information enlightening. However I do not find indulging in
personalities enlightening.

--
Attila aka WW

Attila Feher

unread,
Feb 23, 2005, 3:43:22 PM2/23/05
to
Trevor L. Jackson, III wrote:
> White Wolf wrote:
>> Trevor L. Jackson, III wrote:
[SNIP]
>>> Exceptions are only useful if they _simplify_ the handling of
>>> special conditions. AFAICT they currently do not do that.
>>
>>
>> I still do not get the idea. Why is it more reasonable to accept
>> that handling errors is difficult (and exceptions only reflect that)
>
> no exceptions complicate that.

No, they don't. (I start to like the idea of your way of technical
conversation).

> Cpde that might be called from a
> destructor executes in two distinct modes:

No, it does not.

> the normal mode and the unwind mode.

No, it does not. The code you place into a destructor is _always_ running
in unwind mode. It is higly irrelevant if it is running because of a return
statement or because of an exception.

> In both modes special conditions can arise that
> could/should be handled by throwing an exception.

Not unless you know how to code in C++.

> but in the unwind mode exceptions are not available.

They are. Actually the C++ code is just handling one.

> So that code has to use some other
> mechanism to handle those special conditions.

Just like it would need to do the same no matter what ways you report
errors.

> Given thatit can be very difficult to determine where the boundary
> between bimodal and normal code lies, it is easier to avoid exceptions
> entirely than it is to avoid colliding exceptions.

I strongly disagree. Have you read *any* of the available literature on
exceptions?

>> than to ask
>> the language to provide several threads of execution (remember,
>> exceptions in C++ *are* terminating
>
> No they are not.

Yes, they are. Forgive me, for believing Bjarne Stroustrup and the
standard.

>> and for good reasons [D&E]16.6) to handle several
>> errors at the same time?
>
> I did not ask for multiple threads of execution.

Of course you did. You have asked for two or more exceptions to be handled
at the same time. That is two or more threads of execution.

> I asked for exeptions to be universally available.

They are.

> I.e., a fundamental of the language,

Exceptions *are* fundamental part of the C++ langauge.

> rather than a frill suitable only for programs small enough
> to fint into one person's head.

I must say it seems that you have serious misconceptions about exceptions.
Exceptions are basicaly useless/overkill for any program, which can fit into
one person's head.

> The reason we need to handle several errors at the same time is that
> our error handling code might encounter an error.

Then
a.) your error handling code is not written properly.

b.) your program is already dead, since it has failed to handle the original
error

> If your error
> handling code is trivial this is not an issue.

And it is not an issue either, when it is not trivial, but written properly

> But if your error
> handling code is non-trivial it is a very real issue.

Not unless you have no idea how to write error handling code. Whatever
error handling cannot be done properly using the current C++ exceptions
cannot be done properly by any other means either. That is my statement.

>> How could handling two errors be done?
>
> Is that a serious question?

Yes. How can you handle two errors, *at* *the* *same* *time* with one
thread of execution?

> There are dozens of possbible approaches
> to the issue.

Yet, you have failed so far to present even one example.

> ? How can you make a failsafe system,
>> where doing A and undoing A is equally likely to fail?
>
> You cannot. So what?

That is what you are asking for.

--
Attila aka WW

ka...@gabi-soft.fr

unread,
Feb 23, 2005, 3:40:23 PM2/23/05
to
Gabriel Dos Reis wrote:
> David Abrahams <da...@boost-consulting.com> writes:

> | "Trevor L. Jackson, III" <tl...@comcast.net> writes:

> | > The problem appears when an intermediate layer of the
> | > software detects an exceptional condition that is of
> | > higher importance than the exception currently pending. In
> | > that situation the intervening layer can either ignore the
> | > important exception or force termination.

> | Or it can handle the higher-importance exception immediately
> | and allow unwinding to continue for the one that's
> | "currently pending."

> | I don't see how not using exceptions can possibly help solve
> | this sort of problem. There is a trivial mapping between
> | using exceptions and using other error-reporting mechanisms
> | such as return codes. Anything that's a _fundamental_
> | problem for a system with exceptions is going to be a
> | fundamental problem for a system using return codes.

> That probably needs qualification.

> A distinctive property (feature?) of system usiing return
> codes is that a return code can be ignored

That's not unique to using return codes -- you can ignore an
exception too. And it's possible to design return code types
that are just as difficult to ignore as exceptions -- even more
difficult, perhaps.

It is true that if you don't care about writing correct code,
and just ignore the issues, the errors will be less blatant with
return codes. I, for one, consider that a disadvantage of
return codes -- I like my errors to be blatant. (Of course,
it's possible to design a return code class which makes the
error blatant as well.)

The basic problem isn't simple, and it has nothing to do with
exceptions or RAII, per se. The basic problem is what do you do
if an error occurs in your error recovery. Until you've defined
what you want to do, trying to determine how to implement it, be
it with exceptions, or return codes, or anything else, is
fruitless. If the decision is to ignore the new error, or to
abort the process (both correct in certain circumstances), then
the question of how to propagate two errors is moot. If the
decision is that you need to propagate both errors, then you've
got a certain amount of work cut out for you; I suspect off hand
that it is probably easier to manage with exceptions than with
return codes, but I've no experience with the issue to be sure.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Nicola Musatti

unread,
Feb 23, 2005, 4:31:33 PM2/23/05
to

Scott Meyers wrote:
> On 22 Feb 2005 16:19:39 -0500, Nicola Musatti wrote:
[...]

> > No. Well designed C++ uses RAII to provide a complete, consistent
> > abstraction over a lower level resource handling mechanism.
>
> I disagree. The primary motivation for RAII is to make sure that
something
> gets done when a scope is exited, regardless of how it is exited.
Usually
> (but not always), the thing to be done is release a resource.

I know that this is how RAII is most often used, but I'm not convinced
that it is always a good thing, that is I'm not sure that the gain in
safety (nothing gets forgotten) always compensates the loss of
communication (you have to checkout distructors to know what happens).
I have to recognize, however, that the use of specific constructs such
as Andrei's ScopeGuard does communicate the presence of a RAII
controlled scope.

> My feeling is that RAII is *not* an abstraction mechanism, because
usually
> it's used only to wrap a resource whose release we want to ensure.
The
> resource might be memory, a font, a brush, a database connection,
etc., but
> code using RAII will often want to get at the underlying resource so
that
> it can use it with other APIs. That is, the user of an RAII-wrapped
font
> will need access to the font, the user of an RAII-wrapped brush will
need
> access to the brush, the user of an RAII wrapped pointer to
dynnamically
> allocated memory may need access to the pointer, etc. RAII doesn't
> encapsulate or abstract, it just automates. At least that's my view.

I agree that RAII by itself doesn't provide abstraction, but by
forsaking the kind of abstraction I described in my previous post you
lose a way out of your predicament: RAII by itself may only be used
when resource release can never go wrong or you know for sure that you
can afford to ignore error conditions; not necessarily because throwing
from destructors is inherently evil, but because the intricacies of
dealing with it far outweight the convenience of RAII.

Cheers,
Nicola Musatti

Bo Persson

unread,
Feb 23, 2005, 4:32:16 PM2/23/05
to

"Trevor L. Jackson, III" <tl...@comcast.net> skrev i meddelandet
news:5sSdnRsb-9Z...@comcast.com...

> White Wolf wrote:
>> Trevor L. Jackson, III wrote:
>> [SNIP]
>>
>>>The term nesting may be misleading. Both exceptions have to be
>>>handled.
>>> So both have to propagate upward in search of a handler. Should
>>>applications be throwing containers of exceptions and have the catchs
>>>filter them selectively? Standard containers are some of the most
>>>exception-sensitive code around. This complexity explodes!
>>>
>>>Exceptions are only useful if they _simplify_ the handling of special
>>>conditions. AFAICT they currently do not do that.
>>
>>
>> I still do not get the idea. Why is it more reasonable to accept
>> that
>> handling errors is difficult (and exceptions only reflect that)
>
> no exceptions complicate that. Cpde that might be called from a
> destructor executes in two distinct modes: the normal mode and the
> unwind mode. In both modes special conditions can arise that
> could/should be handled by throwing an exception. but in the unwind
> mode exceptions are not available. So that code has to use some other
> mechanism to handle those special conditions.
>

But exceptions are for problems that you cannot handle locally. They
allow code at a higher level, that might know what to do, to have a go
at the problem.

If you have a problem that can be fixed directly and locally, why would
you ever want to throw an exception for that? And how would my code
handle a function that sometimes throws an exception, and sometimes does
not, for the same condition?! Totally useless!


Bo Persson

w...@seed.net.tw

unread,
Feb 23, 2005, 4:40:12 PM2/23/05
to
Thanks for the reference you provided [D&E]16.6. The termination model
is mentioned there, solved my confusion of C++.

White Wolf

unread,
Feb 23, 2005, 4:41:59 PM2/23/05
to
David Abrahams wrote:
> "White Wolf" <wo...@freemail.hu> writes:
>
>> David Abrahams wrote:
>> [SNIP]
>>> I don't buy that as an important advantage of using exceptions, and I
>>> never have. Exceptions can be ignored just as easily as return codes,
>>> and if ignored will just as quickly lead to incorrect code.
>> [SNIP]
>>
>> I believe that what Scott meant was that return codes can be ignored
>> /implicitly/, while to ignore exceptions you have to write /explicit/
>> code.
>
> Not really. If you ignore exceptions when writing code, your program
> may terminate unexpectedly. But that could happen if you ignore error
> conditions reported in other ways just as easily:
>
> int* p = new (nothrow) int;
> *p = 42; // boom?
>
> In fact, a completely ignored exception at least leads to a kind of
> orderly shutdown, so in some ways it's less obtrusive than what could
> happen if you ignore other kinds of error conditions.

Are you implying that all ignored error return codes lead to shutdown?

[SNIP]
>>> If the answer is yes (unlikely IMO, but whatever) then it's reasonable
>>> to use uncaught_exception in the destructor of the RAII object, and
>>> *only* throw if you're not doing stack unwinding.
>>
>> I see Herb Sutter half waking up in his first class seat en-route to
>> the next Acme (kidding!) meeting, and mutter 'Pick a lane, *any*
>> lane!'
>
> Driving all over the road without respect for lane markers is a
> characteristic of Boston drivers that I try hard to avoid. Is Herb
> dreaming about driving behind me on his way to a meeting with Bill,
> for which he is late??

Exceptional C++ (IIRC). Herb compares destructors which either throw or
not, not depending on the error but if there is another exception being
processed, to drivers who drive between lanes on the highway.

>> My belief is that if you cannot ignore that error on close in
>> "success" case, then you cannot fully ignore it in failure case
>> either.
>
> Note that I never suggested _ignoring_ the condition.

You as anyone. I was thinking aloud. Although there may be cases (when the
error condition causes no corruption IOW leakage) in which you can "eat" or
"ignore" the rror on operations such as closing a database connection, in
case your database operation has falied anyway.

>> I mean not if RAII does both close operations.
>>
>> I cannot imagine a situation where close can fail, and this whole
>> situation can be solved by simple means. By simple I mean language
>> level. If close can fail then rollback *and* commit can *both*
>> fail(*),
>
> For the record: my answer was not tuned to the specific problem of
> database accesses. In that particular case, I probably agree with
> you (depending on the characteristics of the database system).

It is not necessarily a database. I am talking about any operation where do
and undo can both fail, and I mean not in theory but in real life.

[SNIP]


>> (*) Please remember,
>
> To be fair, there's nothing to remember. You're setting these
> parameters right here...
>
>> we are not talking about the database handle
>> being overwritten by off-by-one errors because that case should
>> abort,
>
> Whether or not that should abort is a decision that has to be made on
> an application-by-application basis...

The only situation in which I can imagine it not to abort is if you have a
highly effective CRC on all objects memory and check it, so you can and do
catch if anything else (in the memory) has been corrupted.

> but abort is a very good default.

And I find it unlikely that (in case of memory corruption) there really is
any other feasible solution.

> The class of applications where it makes sense to try to
> muddle on even after a programmer error has messed things
> up is very small.

It is possible, but it seems to be "cheaper" to make an OS which is able to
start up to start up and shut down little processes very fast.

>> but we talk about errors in the close operation.
>
>> I for one (even with that local storage) won't like to be on an
>> aircraft handled by that software setup. You *must* to have enough
>> logic also on the other end, so that recovery will be possible.
>> That requires tedious design. I know. Been there, done that. And
>> my case was simple.
>
> I believe you. Sounds messy.

Yeah. And in my case all the simple solution would have needed was some
thought by the big blue, before they get the firmware of an ECR legally
frozen. All the clean solution needed was one command in the protocol to be
able to ask: what are you doing now. :-)

>>> Note that if you do this you have to document that your RAII class
>>> does *not* have a general nothrow destructor, but only an
>>> unwind-nothrow destructor.
>>
>> Herb is now fully awake. :-)
>
> I don't really feel bad about disturbing Herb's rest, especially if he
> is flying in first class.

He is, in my imagination. People say it must be a good place. :-)

>> [SNIP]
>>> If you still don't like uncaught_exception, or it just makes you
>>> nervous, *and* you think the close operation should cause unwinding,
>>> another reasonable approach is to use an explicit close call for the
>>> case where everything else has worked out okay, and save a
>>> logging/swallowing RAII for "insurance" against errors (and
>>> thoughtless programmers who forget to call close):
>> [SNIP]
>>
>> I like that one for some reason.
>
> That's interesting; it should be equivalent to my first suggestion.

Yeah. I cannot really explain why... I think probably because of the
database example we started up with. It does seem to be a conceptually a
completely different operation if we close the database because we are done,
or because we have failed. I guess this brings us to an interesting beast,
a database-connection-representing class, which, in addition, is also a
guard (*). So U

(*) guard, a'la "Change the Way You Write Exception-Safe Code — Forever" by
Andrei Alexandrescu and Petru Marginean
http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/alexandr.htm
http://tinyurl.com/26vzp

>>> Still a thousand percent cleaner than the Java code you showed.
>>
>> (Homer Simpson's voice:)
>> Hmmmm. Language war.
>
> Naw; I was just trying to say that if you do this you don't really
> give up the main advantages of RAII and C++ exceptions.

Yep. I must have more things common with Homer Simpson than the voice. ;-)

--
WW aka Attila
:::
Everything should be made as simple as possible, but no simpler.

White Wolf

unread,
Feb 23, 2005, 5:28:14 PM2/23/05
to
David Abrahams wrote:
[SNIP]

>> A distinctive property (feature?) of system usiing return codes is
>> that a return code can be ignored
>
> As I have posted elsewhere in this thread, you can ignore exceptions
> too. The behavior of exceptions when ignored doesn't change the
> picture very much for writing correct code, especially in a large
> system.

Except for the fact that ignoring error codes may lead to the system
operating (and doing something else than it should), while (implicitly)
ignoring exceptions the system dies at the moment of the unhandled error. I
have seen systems running happily for quite some time after ignoring a
return code. For 2-3 months to be correct.

--
WW aka Attila
:::
End User (V): An act the Customer Support Staff wishes to carry out.
(Known variant is "Disastrously End User" depending upon the magnitude of
the stupidity.) - Anis Shiekh

Emil

unread,
Feb 23, 2005, 5:34:36 PM2/23/05
to
I have acquired a resource successfully. I have used it. Later I no
longer need the resource -- in other words, I no longer care about it.
Why would something I don't care about bother me with anything, much
less throwing exceptions?

I believe this holds true for any type of error reporting. Functions
that release resources must not fail, even if failure is reported
through something other than throwing an exception.

Note that I am not arguing that resource release functions never
encounter problems, only that the proper post condition for a resource
release function should be that the resource is released, even if there
were problems in the process. If there were errors -- such as failures
to commit buffered data, etc. -- they do not concern the user. If such
errors were a concern for the user, they should have used some other
operation with the resource instead of releasing it!

--Emil

White Wolf

unread,
Feb 23, 2005, 6:03:11 PM2/23/05
to
w...@seed.net.tw wrote:
> Thanks for the reference you provided [D&E]16.6. The termination model
> is mentioned there, solved my confusion of C++.

Great news! I am happy I have helped.

I wish that others could also read it (instead of stating exceptions aren't
terminating). An awful lot of confusion can be cleared up by actually
learning when and how to use things before one make a decision that they
cannot be used.

--
WW aka Attila
:::
The difference between ordinary and extraordinary is that little extra.

David Abrahams

unread,
Feb 23, 2005, 6:04:14 PM2/23/05
to
"White Wolf" <wo...@freemail.hu> writes:

>> Whether or not that should abort is a decision that has to be made on
>> an application-by-application basis...
>
> The only situation in which I can imagine it not to abort is if you have a
> highly effective CRC on all objects memory and check it, so you can and do
> catch if anything else (in the memory) has been corrupted.
>
>> but abort is a very good default.
>
> And I find it unlikely that (in case of memory corruption) there really is
> any other feasible solution.

My favorite example was pointed out to me by Paul DeRocco, who worked
on a concert lighting controller. In his case it was better to try to
keep flashing the lights, even if incorrectly, than to have the stage
go dark. I suggest googling for the threads.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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

Gabriel Dos Reis

unread,
Feb 23, 2005, 7:14:04 PM2/23/05
to
ka...@gabi-soft.fr writes:

| Gabriel Dos Reis wrote:
| > David Abrahams <da...@boost-consulting.com> writes:
|
| > | "Trevor L. Jackson, III" <tl...@comcast.net> writes:
|
| > | > The problem appears when an intermediate layer of the
| > | > software detects an exceptional condition that is of
| > | > higher importance than the exception currently pending. In
| > | > that situation the intervening layer can either ignore the
| > | > important exception or force termination.
|
| > | Or it can handle the higher-importance exception immediately
| > | and allow unwinding to continue for the one that's
| > | "currently pending."
|
| > | I don't see how not using exceptions can possibly help solve
| > | this sort of problem. There is a trivial mapping between
| > | using exceptions and using other error-reporting mechanisms
| > | such as return codes. Anything that's a _fundamental_
| > | problem for a system with exceptions is going to be a
| > | fundamental problem for a system using return codes.
|
| > That probably needs qualification.
|
| > A distinctive property (feature?) of system usiing return
| > codes is that a return code can be ignored
|
| That's not unique to using return codes -- you can ignore an
| exception too.

And look at the resulting programs in both style.
If you ignore an exception, is terminated no matter how the issue
was.

| And it's possible to design return code types
| that are just as difficult to ignore as exceptions -- even more
| difficult, perhaps.

The claim was not whether one can design a return code system that is
more ackward than exception systems,

| It is true that if you don't care about writing correct code,

The issue is not whether one don't care about wirtig correct code.
The issue is whether what is a fundamental problem with a system with
exceptions is going to be a fundamental problem with a system with
return code.

For exemple, look at floating point system with NaNs/denormals and
floaying point system with exceptions.

[...]

| The basic problem isn't simple, and it has nothing to do with
| exceptions or RAII, per se. The basic problem is what do you do
| if an error occurs in your error recovery. Until you've defined
| what you want to do, trying to determine how to implement it, be
| it with exceptions, or return codes, or anything else, is
| fruitless.

Please re-read the claim I was replying to. It makes a postulate
regardless of what is defined.

--
Gabriel Dos Reis
g...@integrable-solutions.net

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

Dietmar Kuehl

unread,
Feb 24, 2005, 10:13:49 AM2/24/05
to
Trevor L. Jackson, III wrote:
> OK, maybe I am missing something. How do I negate the unwinding mode
> long enough to call a routine that might throw so I can catch it,
> handle
> it, and then resume unwinding?

Within the destructor you can throw exceptions without taking any
further
action. The only thing which is not allowed is having an exception
escape
a destructor called due to stack unwinding. That is, if you simply catch
all exceptions thrown within the destructor and handle them
appropriately
(as e.g. line out in another article I posted to this thread), you have
no problems.
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

ka...@gabi-soft.fr

unread,
Feb 24, 2005, 4:33:28 PM2/24/05
to
Attila Feher wrote:
> Trevor L. Jackson, III wrote:

[...]


> >> than to ask the language to provide several threads of
> >> execution (remember, exceptions in C++ *are* terminating

> > No they are not.

> Yes, they are. Forgive me, for believing Bjarne Stroustrup
> and the standard.

Excuse me for butting into your "technical" discussion, but I
think you two are using "terminating" is different senses. In
literature about exceptions (in general, not in C++),
terminating has a very special meaning: a terminating exception
is one which doesn't allow continuation from the point where the
exception was raised. In this sense, C++ exceptions definitly
are terminating. Intentionally; I seem to remember reading
about discussions in the committee on the issue, and it was
decided that the added complexity necessary to support
continuation wasn't worth the cost.

> >> and for good reasons [D&E]16.6) to handle several errors at
> >> the same time?

> > I did not ask for multiple threads of execution.

> Of course you did. You have asked for two or more exceptions
> to be handled at the same time. That is two or more threads
> of execution.

Be careful, Attila. C++ is capable of handling several
different bits of information, and acting on it, in a single
thread. It would certainly be possible to specify a model where
several exceptions were active at the same time. Of course, if
you use the rule, the first catch block to match either wins,
you'll often end up treating the wrong one first, and if you
introduce "priorities", it becomes even more complicated -- how
do you "rewind" the stack you've unwound to handle the lower
priority exception, after having handled the higher one?

What might be useful in the context of RAII is the possibility
of "replacing" an exception. If clean up is handled in a catch
block, this is trivial, just throw the new exception, instead of
the old one. If clean up is handled in a destructor, it would
be nice to be able to do the same thing. (Nice, but not
essential.)

[...]


> >> How could handling two errors be done?

> > Is that a serious question?

> Yes. How can you handle two errors, *at* *the* *same* *time*
> with one thread of execution?

It isn't so much a question of handling two errors -- exceptions
don't handle errors any more than return codes do. It's a
question of propagating two errors (which isn't really a
problem), and matching said errors to their handlers (which is a
very complex problem; you have to choose which one to handle
first).

> > There are dozens of possbible approaches to the issue.

> Yet, you have failed so far to present even one example.

That's true, but the real problem is precisely that there are
dozens of possible approaches. No one of which would be
appropriate for even a majority of the applications.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Attila Feher

unread,
Feb 24, 2005, 4:36:54 PM2/24/05
to
David Abrahams wrote:
[SNIP]

>> And I find it unlikely that (in case of memory corruption) there
>> really is any other feasible solution.
>
> My favorite example was pointed out to me by Paul DeRocco, who worked
> on a concert lighting controller. In his case it was better to try to
> keep flashing the lights, even if incorrectly, than to have the stage
> go dark. I suggest googling for the threads.

:-) Well, I think that this can be regarded as a very unique situation. :-)
Most of the systems do not run on dedicated HW, and do not have the luxury
of allowing data corruption, since they may corrput (stored) data belonging
to something else.

BTW one thing I cannot really understand is that why would/should a software
failure cause the stage go dark. That strikes me as a very interesting
design. Especially that the described solution (keep flashing light
incorrectly) may be life threatening to epileptic people. What I would do
is have a (electro-mechanical) fallback to emergency lighting, rather than
hoping that the flashes will not bring someone in the audience into shock...

--
Attila aka WW

David Abrahams

unread,
Feb 24, 2005, 4:40:21 PM2/24/05
to
Gabriel Dos Reis <g...@integrable-solutions.net> writes:

Not true. It only happens if you ignore exceptions at all lavels.
Suppose you are writing an interactive program with a main event loop.
In that event loop it is common practice to use a try/catch block that
is used to report and recover from all failures. So if you do that,
no exceptions are "ignored?" If so, that is not much of an advantage.

It's easy enough to write a component that uses a throwing operation
in a region of code that, to be correct, ought to be no-throw:

void foo::mutating_operation()
{
int x = operation1();
this->x = x; // invariant is now broken
int x2 = operation2();
this->x2 = x2; // invariant is now fixed
}

If I ignored the fact that operation2 might throw an exception, no
amount of "not ignoring" at the level of my main event loop can save
my program from continuing to muddle on as though all invariants are
intact, when in fact they are broken.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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

David Abrahams

unread,
Feb 24, 2005, 4:42:13 PM2/24/05
to
"White Wolf" <wo...@freemail.hu> writes:

> David Abrahams wrote:
> [SNIP]
>>> A distinctive property (feature?) of system usiing return codes is
>>> that a return code can be ignored
>>
>> As I have posted elsewhere in this thread, you can ignore exceptions
>> too. The behavior of exceptions when ignored doesn't change the
>> picture very much for writing correct code, especially in a large
>> system.
>
> Except for the fact that ignoring error codes may lead to the system
> operating (and doing something else than it should), while (implicitly)
> ignoring exceptions the system dies at the moment of the unhandled
> error.

Not at all; see my response to Gaby.

> I
> have seen systems running happily for quite some time after ignoring a
> return code. For 2-3 months to be correct.

It's just as easy to see the same thing when exceptions are involved.
Especially (as is often the case) if the failure condition never gets
triggered.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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

ka...@gabi-soft.fr

unread,
Feb 24, 2005, 4:38:51 PM2/24/05
to

> | > That probably needs qualification.

If the program is terminated, the exception wasn't ignored.

The default action for exceptions and return codes is
different. No one questions that. The default action for a
return code is to ignore it; the default action for an exception
is to terminate the program. But you can ignore either, and you
can terminate the program with either, if that be your wish.

> | And it's possible to design return code types that are just
> | as difficult to ignore as exceptions -- even more difficult,
> | perhaps.

> The claim was not whether one can design a return code system
> that is more ackward than exception systems,

I don't consider having to explicitly ignore an error an
awkwardness. I rather consider it an aid to good programming.

> | It is true that if you don't care about writing correct
> | code,

> The issue is not whether one don't care about wirtig correct
> code. The issue is whether what is a fundamental problem with
> a system with exceptions is going to be a fundamental problem
> with a system with return code.

The issue is whether there is a "fundamental problem" with
either. Both work, when used correctly. Neither works, when
misused. The only question is which one requires less work to
use correctly. And the only absolute answer one can give is: it
depends.

> For exemple, look at floating point system with NaNs/denormals

> and floating point system with exceptions.

Nan's, of course, introduces a third model. That used in
iostream. There are also cases where it is the appropriate
solution.

> [...]

> | The basic problem isn't simple, and it has nothing to do
> | with exceptions or RAII, per se. The basic problem is what
> | do you do if an error occurs in your error recovery. Until
> | you've defined what you want to do, trying to determine how
> | to implement it, be it with exceptions, or return codes, or
> | anything else, is fruitless.

> Please re-read the claim I was replying to. It makes a
> postulate regardless of what is defined.

The immediate claim was:

> | > | I don't see how not using exceptions can possibly help
> | > | solve this sort of problem. There is a trivial mapping
> | > | between using exceptions and using other error-reporting
> | > | mechanisms such as return codes. Anything that's a
> | > | _fundamental_ problem for a system with exceptions is
> | > | going to be a fundamental problem for a system using
> | > | return codes.

More immediate was the claim that you can ignore exceptions.

Both claims seem trivially true to me. About all that one can
say is, with regards to the first, that exceptions and return
codes aren't the only possibilities for reporting errors. But
that isn't what you said. What you said was:

> | > A distinctive property (feature?) of system usiing return
> | > codes is that a return code can be ignored

Which is false in that the property isn't distinctive, you can
ignore exceptions, too. For that matter, you can also ignore
delayed error codes, like NaN's or failbit. It seems to be an
almost universal characteristic of error reporting mechanisms
that you can ignore the error if you want to.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

ka...@gabi-soft.fr

unread,
Feb 24, 2005, 4:41:05 PM2/24/05
to
Gabriel Dos Reis wrote:
> David Abrahams <da...@boost-consulting.com> writes:

> | Gabriel Dos Reis <g...@integrable-solutions.net> writes:

[...]


> | > A distinctive property (feature?) of system usiing return
> | > codes is that a return code can be ignored

> | As I have posted elsewhere in this thread, you can ignore
> | exceptions too.

> Which terminates the execution of the program. That is a key
> difference. (Unless by ignore the exceptions you mean an
> empty catch-all block; but I don't consider that ignoring the
> exception).

And how else would you go about ignoring an exception? Unless
I've misunderstood greatly, we're talking here about
implementing a conscious decision, not something that
accidentally happens. While it's true that it requires a few
more lines of code to ignore an exception than to ignore a
return code (although that depends on the implementation of the
return code's type), it shouldn't be an issue when you want to
intentionally ignore it, and you want the fact that it was
intentionally ignored to be visible to anyone who reads your
code.

> | The behavior of exceptions when ignored doesn't change the
> | picture very much for writing correct code, especially in a
> | large system.

> I disagree.

> A system with return code can be flexible with respect to
> "severity" of the state of computation being signaled --
> e.g. a return code can be ignored if the situation is not too
> severe and the flow of computation continues until some
> checking point.

At which point the fact that the return code signaled an error
is lost.

> To see what I mean, consider the example (from existing
> practice) of floating point systems with NaNs vs. floating
> point systems with exceptions. You can argue for the merits
> of each systems but the picture does change with respect to
> writing correct code with either of them

You've introduced yet another option. The one which is used in
iostream, in fact.

There's a time and place for all of them.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Trevor L. Jackson, III

unread,
Feb 24, 2005, 4:52:11 PM2/24/05
to
Bo Persson wrote:

The issue I'm trying to describe is that the path from the lower to the
higher level is not universally available. If getting to that higher
level code (detroying the context of the detection site) encounters an
error that would merit anothe throw(), then you risk termination. In
large applications that path may be long, and thus the risk may be high.

>
> If you have a problem that can be fixed directly and locally, why would
> you ever want to throw an exception for that? And how would my code
> handle a function that sometimes throws an exception, and sometimes does
> not, for the same condition?! Totally useless!

Exactly. Yet it appears that intermediate code has to have that dual
personality. After all, there any other reason for uncaught_execption()
to exist.

/tj3

Trevor L. Jackson, III

unread,
Feb 24, 2005, 4:53:48 PM2/24/05
to
Attila Feher wrote:

> Trevor L. Jackson, III wrote:

>>Cpde that might be called from a
>>destructor executes in two distinct modes:
>
>
> No, it does not.
>
>
>>the normal mode and the unwind mode.
>
>
> No, it does not. The code you place into a destructor is _always_ running
> in unwind mode. It is higly irrelevant if it is running because of a return
> statement or because of an exception.

This is false. The system is clearly modal and uncaught_exception()
disambiguates the two states. Perhaps you did not comprehend the
meaning of "unwind mode". It means that destructors cannot leak
exceptions without triggering termination.

/tj3

Trevor L. Jackson, III

unread,
Feb 24, 2005, 4:53:26 PM2/24/05
to
ka...@gabi-soft.fr wrote:

> The basic problem isn't simple, and it has nothing to do with
> exceptions or RAII, per se. The basic problem is what do you do
> if an error occurs in your error recovery. Until you've defined
> what you want to do, trying to determine how to implement it, be
> it with exceptions, or return codes, or anything else, is
> fruitless. If the decision is to ignore the new error, or to
> abort the process (both correct in certain circumstances), then
> the question of how to propagate two errors is moot. If the
> decision is that you need to propagate both errors, then you've
> got a certain amount of work cut out for you; I suspect off hand
> that it is probably easier to manage with exceptions than with
> return codes, but I've no experience with the issue to be sure.

Did you leave out the possibility of applying discretion to the issue of
which of the multiple outstanding exceptions gets propagated?

/tj3

Trevor L. Jackson, III

unread,
Feb 24, 2005, 4:52:54 PM2/24/05
to
Attila Feher wrote:

> Trevor L. Jackson, III wrote:
>

>>White Wolf (AKA Attila Feher) wrote:

>>
>>>Trevor L. Jackson, III wrote:
>
>>>than to ask
>>>the language to provide several threads of execution (remember,
>>>exceptions in C++ *are* terminating
>>
>>No they are not.
>
>
> Yes, they are. Forgive me, for believing Bjarne Stroustrup and the
> standard.

You missed the point. *Uncaught* execeptions are terminating. If
"exceptions are teminating" was true we would not need all of the
complexity they bring because we could just call abort().

/tj3

Trevor L. Jackson, III

unread,
Feb 24, 2005, 4:49:32 PM2/24/05
to
[comment to moderator: in the interest of providing a standalone
expression of the issue I left a lot of text is that is not strictly
necessary to a reader with the full thread context in mind. I.e., I
wasn't being lazy, I was trying to be careful]

Dietmar Kuehl wrote:
> Trevor L. Jackson, III wrote:
>

>>I dispute the claim that uncaught_exception() is a sufficient
>>foundation
>>on which to solve (in some sense of the term "solve") the issue of
>>colliding exceptions.
>
>
> It does not on its own offer a solution to the problems involved but I
> think it provides the necessary foundation to create a mechanism for
> exception chaining. That is, it is no sufficient but provides the
> necessary parts on side of the language definition. Whether the language
> should offer the infrastructure you can create upon the existing tools
> is another issue, though. Of course, the standardization committee
> should
> not do inventive work and create such an infrastructure but should,
> eventually, standardize the common approach :-)

In general yes. But what is the appropriate principle that should guide
the Committee once they have embarked on a design exercise rather than a
standardization exercise? No sane vendor is going to invest time in
fixing exceptions. So it is unreasonable to expect vendors to complete
the design process (and make something that works in a more robust manner).

Yet due to the fact that the existing design is part of the standard a
vendor that invested effort in replacing the existing design with a
different one would be criticized for being non-standard.

So IMHO it is up to the Committee to either complete the design process
themselves or deprecate the existing design to signal vendors that their
effort would not be wasted or criticized.

>
>
>>For example, given an exception from a low level function, say
>>bad_alloc
>> or something else that is exceptional, but not fatal, the superposed
>>layers of software would be unwound until some layer is reached that
>>has
>>declared its intention to deal with a set of exceptions including
>>bad_alloc (or whatever).


>>
>>The problem appears when an intermediate layer of the software detects
>>an exceptional condition that is of higher importance than the
>>exception
>>currently pending. In that situation the intervening layer can either

>>ignore the important exception or force termination. That is usually
>>an
>>unacceptable choice.
>
>
> These two options are, of course, not exhaustive and this is where
> 'uncaught_exception()' enters the picture. First off, note that we are
> only discussing exceptions propagating from destructors: this is the
> only case where multiple exceptions may be on their way.

By "propagating from destructors" I assume you mean that with
uncaught_exception() true an exception raised within the execution
history of a destructor was not caught within that history (i.e. the
destructor leaked a new exception that was raised in one of the
functions the destructor called.).

> The number of
> simultaneous exceptions currently being unhandled is actually unbounded
> but except for the first exception being thrown all other exception are
> thrown from within a destructor [and would cause program termination if
> they escape their respective destructor].

That description leaves the status of exceptions thrown from within a
function called by a destructor in limbo. Did you intend that it be
treated as part of "within a destructor" or not?

> We are also not discussing the
> two trivial cases for exceptions thrown from within a destructor (i.e.
> the ones we can safely ignore or the ones which should case program
> termination; these two cases can be simply handled using
> 'uncaught_exception()' and taking appropriate action in case this
> function yields 'true' or even taking appropriate action
> unconditionally).

Of course.

> That is, we are only discussing the cases where an interesting and
> possibly important exception should be propagated despite another
> exception already being propagated.
>
> The destructor has no option to change the exception currently being
> propagated nor has it any access to this exception.

Key point. I do not comprehend why that it is useful for that statement
to be true. Do you care to suggest why that is useful?

[snip example code for queuing exceptions]

> Now, any intermediate layer which wants to handle some exceptions which
> might have been thrown during destruction cause by an uncaught exception
> can catch exceptions and deal with appropriate exceptions from the
> queue:
>
> try { /*...*/ }
> catch (...)
> {
> for (err::iterator it = err::queue.begin(), end = err::queue.end();
> it != end; ++it)
> if (std::tr1::shared_ptr<interesting_exception const& ex =
> std::tr1::dynamic_pointer_cast<interesting_exception>(*it))
> {
> // handle the exception and probably remove it from the queue
> // afterwards
> }
> throw; // ... and there was another exception on its way: rethrow
> }

Key point.

The intermediate layer has to manage the exception context inline.
This defeats the transparency of the exception model and eliminates the
potential gains in code simplification that are one of the strongest
reasons why exceptions have potential value.

>
> The details of the exception handling can be tailored to the needs of
> the application. For example, I can imagine situations where the current
> exception is not rethrown or where a more efficient approach to locating
> interesting exceptions in the list would be desirable (of course, since
> exceptions should be exceptional performance should not be a critical
> aspect but mediocre performance is also undesirable).
>
>
>>Would anyone care to work on a system where the interrupts were
>>arranged
>>such that a higher priority interrupt was faced with the choice of
>>being
>>ignored to forcing a reset (and thus a reboot) of the machine? Of
>>course not. Analogies are slippery, but I don't think this one is a
>>distortion.
>
>
> ... but it is based on wrong premises, namely that you can only either
> ignore the exception or terminate the application. The above sketch of
> an error handling framework provides a third option. It is surely not
> really convenient but it is also only rarely necessary (e.g. in the
> applications I have programmed I never had the need to do something like
> this).
>
>
>>Interrupts achieve this transparency by nesting or queueing.
>>Exceptions
>>don't.
>
>
> If you want nesting or queuing, you can have it: see above.
>
>
>>In the absence of neither nesting nor queueing I suggest that
>>replacement based on a precedence hierarchy is the least functionality
>>on which a robust system could depend.
>
>
> ... and it is your option to do so!

But in the wrong place. The replacement needs to happen at the site
where the second exception would normally be handled, not at the upper
site (such as an entry point to an intermediate layer). Any other
approach, including the one you outlined above, distributes the
error/exception handling context throughout the application code. Yes,
you can make it work for some non-large applications. No, you cannot
maintain it at anything near a reasonable cost.

>
>
>>Clearly an application could build an exception framework that would
>>allow chaining or queueing and even impose a priority or filtering
>>scheme upon the collection of pending exceptions. But the only
>>argument
>>against moving that capability out of the application and into the
>>language (or libraries) would be an overhead issue (probably both space
>>and time). But in almost every case that overhead is avoidable. In
>>the
>>few cases where it is unavoidable it can probably (more theory than
>>fact
>>here) be justified on the basis of reliability.
>
>
> Let me quote a certain '"Trevor L. Jackson, III" <tl...@comcast.net>' in
> this same thread (message ID: <WYWdnS0DeOT...@comcast.com>):
>
> But a lot of it is due to the fact that the language is not defined as
> a standardaization of existing practice. It is more of a language
> design exercise, and parts of that design are simply non-functional.
>
> This quote seems to be a criticism of the standardization committee,
> i.e. that it invents things not in common use. It looks to me like there
> are conflicting opinions held by the same person with respect to what
> the
> standardization committee does: a little bit of consistency would, IMO,
> be in order, i.e. you can either wish for standardizing existing
> practice
> or you can wish for inventions addressing problems but not for both (of
> course, it is likely that the committee goes down the latter path anyway
> but this is a different issue).

I agree that there is an apparent conflict here. But I tried to address
the standardization process issues in the beginning of this message.

Check the final phrase that you quoted above. "... parts of the design
are simply non-functional". But they are already in the standard! IOW
I am not criticizing the Committee for inventing rather than
standardizing. First, that is a separate debate. Secondly, I wasn't
there when the decision was made so I can't complain about it. Thirdly,
I think that overall they did a very good job, and fourth my perception
of the (then) need for invention is that it was pretty dire.

I can, however complain about this: given that the committee has decided
to invent rather than standardize, they have to deal with the
consequences of that (courageous/foolish, your choice) decision. If
they had chosen to standardize existing practice and that practice was
whacked, the fact that the prevailing practice was whacked would be an
adequate affirmative defense to criticism of the resulting whacked
standard and no further committee action would be called for until
prevailing practices changed.[*digression]

However, that excuse is not available in the situation where the
decision is to invent rather than standardize. By inventing the
Committee implicitly assumes responsibility for the successes and
failures of the features they incorporate into the standard. Perhaps a
more useful criticism of the existing exception mechanism is not that it
is non-functional, but that it is incomplete -- the job of designing the
mechanism is not done.

IMO a complete mechanism would have two additional valuable properties.
1) exception specifications would be capable of supporting
compile-time analysis (even of separate translation units; c.f. export)
to detect incompatibilities between caller and callee, including
including exception specs in typedefs, and 2) exceptions raised in an
execution history for which a compatible catch was available would never
terminate() -- there would be no obstructions between the throw() and
the catch{}.

So the thrust of my complaint is that I have an (IMHO reasonable)
expectation that the Committee will continue to invent in order to
achieve the goals initially set for the exception mechanism.

/tj3

[*digression] Because standards are often perceived as a form of
approval or blessing of practices, it behooves standards committees to
described whacked practices as whacked when they are incorporated and
thus implicitly approved or blessed. And IMHO that responsibility also
exists when field experience suggests that a feature once thought useful
is determined to be whacked in practice.

Trevor L. Jackson, III

unread,
Feb 24, 2005, 4:55:07 PM2/24/05
to
Bob Bell wrote:

> Trevor L. Jackson, III wrote:
>

>>The term nesting may be misleading. Both exceptions have to be
>
> handled.
>
>> So both have to propagate upward in search of a handler. Should
>>applications be throwing containers of exceptions and have the catchs
>
>
>>filter them selectively? Standard containers are some of the most
>>exception-sensitive code around. This complexity explodes!
>
>

> I'm very interested in your response to the question posed by David
> Abrahams.

Did I miss a question? Sorry.

> How do you handle this situation when you're not using
> exceptions?

<rant>

The fundamental error handling problem is the distance between the site
at which an error is detected and the site at which the error handling
policy is established. In large applications I've had reasonable
success by preemptively closing that gap.

Consider an error handling context stack onto which a policy making
function can push an error handling mechanism. The interface to the
mechanism has to change slightly depending on the application, so I
don't have a truly universal design available. But the utility of this
approach is that at the point where an error is detected the detecting
code can establish a bi-directional communication path with the policy
making code. They jointly negotiate an appropriate response. This
gives error handling the capability to automatically retry and allows
for the construction of a comprehensive error reporting/logging context.

As a trivial example, when you open a file you can establish an error
handler that provides all of the information about the file including
application context (intentions), so that error reports are
comprehensive and error messages are comprehensible.

Note that because the top of the error context stack has access to the
previous/outer/higher context the communication and negotiation may
involved more than just the try{} and catch{} sites.

A simple example of this context management technique in a different
area is providing adequate help for GUI displays. One does not want to
design a help structure in parallel with the application structure
because it is effectively impossible to maintain consistency between the
two structures. So a lot of GUI apps just have no response when someone
hits the help key -- the user's only option is to go into the help
system from the top.

A technique that avoids that muteness is to establish a help context
stack. Any time the help key is used it activates the top of the stack.
GUI elements that have associated help info get an extra field
containing the help tag, and when they gain focus they push their help
context tag onto the stack (and remove it when they lose focus). This
makes it possible for a GUI element without an associated help text to
respond with the text of the nearest enclosing help context. It
maximizes locality.

Similarly, error handling contexts are a mechanism for providing locality.

</rant>

/tj3

Trevor L. Jackson, III

unread,
Feb 24, 2005, 4:55:42 PM2/24/05
to
Ben Hutchings wrote:

> Trevor L. Jackson, III wrote:

> <snip>

>
>>I dispute the claim that uncaught_exception() is a sufficient foundation
>>on which to solve (in some sense of the term "solve") the issue of
>>colliding exceptions.
>>

>>For example, given an exception from a low level function, say bad_alloc
>> or something else that is exceptional, but not fatal, the superposed
>>layers of software would be unwound until some layer is reached that has
>>declared its intention to deal with a set of exceptions including
>>bad_alloc (or whatever).
>>
>>The problem appears when an intermediate layer of the software detects
>>an exceptional condition that is of higher importance than the exception
>>currently pending. In that situation the intervening layer can either
>>ignore the important exception or force termination. That is usually an
>>unacceptable choice.
>
>

> Cleanup code generally shouldn't be doing anything so risky that that
> can happen.

Cleanup code needs to everything that needs to be done. In a large
application an upper layer can indirectly invoke almost any portion of
layers below the layer immediately below it.

> On the rare occasion that it must, the cleanup code can
> be called from a catch block and not a destructor:

[snip example code]

> Admittedly that's a fair bit of code to write, but I'm unconvinced
> that it is commonly needed.

That violates the symmetry and completeness of the
constructor/destructor model. Anything a constructor can build its
destructor _must_ be able to tear down. Safely.

>
>
>>Would anyone care to work on a system where the interrupts were arranged
>>such that a higher priority interrupt was faced with the choice of being
>>ignored to forcing a reset (and thus a reboot) of the machine? Of
>>course not. Analogies are slippery, but I don't think this one is a
>>distortion.
>

> <snip>
>
> I think it is. Interrupts are asynchronous and return to the original
> thread of execution that they interrupted. Exceptions actually modify
> the thread of execution.

Not from the perspective of software above the catch{}.

> Perhaps you're misapplying exceptions because
> you see a false analogy.

It was an analogy based on transparency. Your argument iimplies that
exceptions are not intended to be transparent. If that implication was
accurate it would vitiate most of the potential value of exceptions.

Trevor L. Jackson, III

unread,
Feb 24, 2005, 5:02:48 PM2/24/05
to
Emil wrote:

> I have acquired a resource successfully. I have used it. Later I no
> longer need the resource -- in other words, I no longer care about it.
> Why would something I don't care about bother me with anything, much
> less throwing exceptions?
>
> I believe this holds true for any type of error reporting. Functions
> that release resources must not fail, even if failure is reported
> through something other than throwing an exception.
>
> Note that I am not arguing that resource release functions never
> encounter problems, only that the proper post condition for a resource
> release function should be that the resource is released, even if there
> were problems in the process. If there were errors -- such as failures
> to commit buffered data, etc. -- they do not concern the user. If such
> errors were a concern for the user, they should have used some other
> operation with the resource instead of releasing it!

This assumes that release is _only_ release. Often it is much more.
Flushing buffers is an easy example. But i many instances, particularly
lazy evaluation, release does all the work! Deferred processing is
quite common in both system and application software.

In many cases, e.g. free() one can assume that release is "clean". But
in more complicated situations it is unacceptable to assume that release
errors are ignorable.

/tj3

Trevor L. Jackson, III

unread,
Feb 24, 2005, 5:00:25 PM2/24/05
to
Dietmar Kuehl wrote:

> Trevor L. Jackson, III wrote:
>
>>OK, maybe I am missing something. How do I negate the unwinding mode
>>long enough to call a routine that might throw so I can catch it,
>>handle
>>it, and then resume unwinding?
>
>
> Within the destructor you can throw exceptions without taking any
> further
> action. The only thing which is not allowed is having an exception
> escape
> a destructor called due to stack unwinding. That is, if you simply catch
> all exceptions thrown within the destructor and handle them
> appropriately
> (as e.g. line out in another article I posted to this thread), you have
> no problems.

That's what I thought. Restating that means that destructors that might
be executed during unwinding may not emit exceptions. Everything has to
be handled within the destructor.

From that we conclude that in a large application, wherein it is
difficult or impossible to determine what code might be called during
unwinding (i.e., by destructors), it is unwise to use exceptions without
universal catch{} guards in the destructors.

From that we conclude that the existing standard for exception handling
is either very burdensome (every high level destructor has to catch{}
everything) or not universally available (not in destructors or any
lower level code that might be called during unwinding, which is most if
not all of the lower level code).

What have I missed?

/tj3

White Wolf

unread,
Feb 24, 2005, 8:14:42 PM2/24/05
to
Trevor L. Jackson, III wrote:
[SNIP]

> This assumes that release is _only_ release. Often it is much more.
> Flushing buffers is an easy example. But i many instances, particularly
> lazy evaluation, release does all the work! Deferred processing is
> quite common in both system and application software.
>
> In many cases, e.g. free() one can assume that release is "clean". But
> in more complicated situations it is unacceptable to assume that release
> errors are ignorable.

You really want to read Exceptional C++.

What you have written above only shows one thing, and one thing only:
flushing buffers is not an operation, which belongs to the job a RAII guard
should do. I put here again the little poem of writing exception safe code:


1.) Do *all* the operations which can throw

2.) commit your work using operations which cannot throw

Flush (if it can fail) *clearly* belongs into the #1, so it *cannot* be part
of any RAII guard.

#2 also means, that either you did nothing in #1 to really change states,
*or* you have set up guards, which can undo those changes in case of failure
of any step. This means guards, this means RAII, and this means destructors
of those are not allowed to throw. Not only because the software will
crash, but because one of your operations (do or undo) *must* be one which
cannot fail! For database systems that is the rollback operation. For
other situations it may be something else.

--
WW aka Attila
:::
Promises are like babies: fun to make, but hell to deliver.

White Wolf

unread,
Feb 24, 2005, 8:17:25 PM2/24/05
to
Trevor L. Jackson, III wrote:
>> Yes, they are. Forgive me, for believing Bjarne Stroustrup and the
>> standard.
>
> You missed the point. *Uncaught* execeptions are terminating. If
> "exceptions are teminating" was true we would not need all of the
> complexity they bring because we could just call abort().

Did it ever occured to you, that I might be using that word in the sense
(meaning, as the term) what it actually *means* in C++ terminology? Please
be very careful. I did not say, exceptions terminate the process. I did
not say, exceptions call terminate. I have said: C++ exceptions are
terminating. And gave you a reference to read so that you will know what it
means. So please either:

a.) read the reference

or

b.) *ask* a question, if you do not understand something.

Both of the above can make technical discussions much more profitable.

Disclaimer: my taglines *are* randomly selected, independent of me.

--
WW aka Attila
:::
There is always one more imbecile than you counted on.

Bob Bell

unread,
Feb 24, 2005, 8:16:02 PM2/24/05
to
Trevor L. Jackson, III wrote:
> Bob Bell wrote:
>
> > I'm very interested in your response to the question posed by David
> > Abrahams.
>
> Did I miss a question? Sorry.

Nope; my bad. He didn't really ask a question, he just made a comment.

In any case, I'm not sure you answered the question I was asking. In
hindsight I think it was unclear what the question was, so I'll try to
restate it.

You had previously indicated an objection you had to exceptions: they
are not universally available because destructors cannot be allowed to
emit exceptions. The reason this is the case is because if a destructor
that executes during stack unwinding emits a new exception, the new
exception causes the program to terminate.

Restated without using exception terminology:

An error is detected in some low-level function. While transmitting
information about the error to a high-level function which can recover
from it, a second error is detected.

The question is, how does not using exceptions make this situation
easier to deal with? Isn't this hard to deal with no matter what you
do?

Your response had some interesting ideas, although as I said it's not
clear if it really answered my question. However, there was one
paragraph I wanted to specifically respond to:

> Consider an error handling context stack onto which a policy making
> function can push an error handling mechanism. The interface to the
> mechanism has to change slightly depending on the application, so I
> don't have a truly universal design available. But the utility of
this
> approach is that at the point where an error is detected the
detecting
> code can establish a bi-directional communication path with the
policy
> making code. They jointly negotiate an appropriate response. This
> gives error handling the capability to automatically retry and allows

> for the construction of a comprehensive error reporting/logging
context.

It's interesting to read D&E about the design of the exception
mechanism of C++. Two models were proposed:

-- the resuming model: after an exception is processed in a catch
block, the catch block could optionally cause the original operation
that threw to be retried. The code would "resume" where it left off as
if the exception was never thrown.

-- the terminating model: when an exception is thrown, the operation
that threw the exception is terminated; that is, there is no provision
for transferring control back to the original throw point, as in the
resuming model. After an exception is processed in a catch block,
execution continues with the first statement after the catch block.

There were supporters for both models, and both models had prior art.
What was interesting was that, despite the passion with which the
resuming model advocates argued, there was little evidence that it was
actually useful. In particular, there was one data point where a large
system had been designed with resuming exception handling. However,
resuming after an exception was found to be very complex and difficult
to get right (i.e., it was a source of bugs). Over time, all of the
resumption was removed from the system, and terminating exception
handling was used instead.

The upshot was that the ability to automatically retry failed
operations was seen as having little utility in the real world.

The paragraph I'm responding to seems to suggest something like a
resumption model; an error is detected, some kind of error handler is
called, and if the error handler can correct the situation, the
operation can be retried.

However, the strategy you describe doesn't seem to address a problem
that exceptions solve quite well: how to transfer control from a
low-level function that has detected an error (and which presumably
cannot continue) to a (possibly much higher-level) calling function
which can continue in the face of the error. Without an automated
solution like exceptions, you are left to implement that mechanism
yourself, e.g., by returning error codes up the stack.

Further, there doesn't seem to be any reason why you can't use both;
have your error context stack as you describe. When an error is
detected, invoke the handler on the top of the stack. It has the same
set of available options as before, but it can also decide to throw an
exception if the original operation is not retried.

Bob

White Wolf

unread,
Feb 24, 2005, 8:17:46 PM2/24/05
to
Trevor L. Jackson, III wrote:
[SNIP]
> Exactly. Yet it appears that intermediate code has to have that dual
> personality. After all, there any other reason for uncaught_execption()
> to exist.

You would _really_ need to read Exceptional C++. :-)

Let's just say so, that sprintf, strlen etc. is in the language and not that
there is no reason why they are there, but there is no *excuse* for them
being there. And yet, they are there. And yet, professionals will tell you
that you must keep away from them as you keep away from plague.

--
WW aka Attila
:::
It has recently been discovered that research causes cancer in rats.

White Wolf

unread,
Feb 24, 2005, 8:17:02 PM2/24/05
to
David Abrahams wrote:
[NYISSZ]

>> I
>> have seen systems running happily for quite some time after ignoring a
>> return code. For 2-3 months to be correct.
>
> It's just as easy to see the same thing when exceptions are involved.
> Especially (as is often the case) if the failure condition never gets
> triggered.

Yeah. But this did get triggered. That is the key issue here.

--
WW aka Attila
:::
Add life to your years, instead of years to your life.

White Wolf

unread,
Feb 24, 2005, 8:18:31 PM2/24/05
to
ka...@gabi-soft.fr wrote:
> Attila Feher wrote:
>> Trevor L. Jackson, III wrote:
>
> [...]
>>>> than to ask the language to provide several threads of
>>>> execution (remember, exceptions in C++ *are* terminating
>
>>> No they are not.
>
>> Yes, they are. Forgive me, for believing Bjarne Stroustrup
>> and the standard.
>
> Excuse me for butting into your "technical" discussion, but I
> think you two are using "terminating" is different senses. In
> literature about exceptions (in general, not in C++),
> terminating has a very special meaning: a terminating exception
> is one which doesn't allow continuation from the point where the
> exception was raised. In this sense, C++ exceptions definitly
> are terminating. Intentionally; I seem to remember reading
> about discussions in the committee on the issue, and it was
> decided that the added complexity necessary to support
> continuation wasn't worth the cost.

And I meant that terminating. I even gave reference to the right chapter of
D&E.

>>>> and for good reasons [D&E]16.6) to handle several errors at
>>>> the same time?
>
>>> I did not ask for multiple threads of execution.
>
>> Of course you did. You have asked for two or more exceptions
>> to be handled at the same time. That is two or more threads
>> of execution.
>
> Be careful, Attila. C++ is capable of handling several
> different bits of information, and acting on it, in a single
> thread. It would certainly be possible to specify a model where
> several exceptions were active at the same time. Of course, if
> you use the rule, the first catch block to match either wins,
> you'll often end up treating the wrong one first, and if you
> introduce "priorities", it becomes even more complicated -- how
> do you "rewind" the stack you've unwound to handle the lower
> priority exception, after having handled the higher one?

So basically as you went on thinking about it, you have realized, that what
I was saying (that the OP-requested feature would need more threads) seems
to be true. To achieve that (non-complicated) scenario what the OP was
asking for (handling two exceptions at the same time) needs two threads. :-)

> What might be useful in the context of RAII is the possibility
> of "replacing" an exception. If clean up is handled in a catch
> block, this is trivial, just throw the new exception, instead of
> the old one. If clean up is handled in a destructor, it would
> be nice to be able to do the same thing. (Nice, but not
> essential.)

It does not make any sense to me. No offense meant. But if the first
exception was not important enough to handle, then why the heck did you
throw it in the first place??? The first error *is* nearly always the most
importsnt, and the rest are (usually) consequnces. So ignoring the first
error is something I strongly disagree with. I mean what makes a code,
which I did not write, is not a handler, to overrule the exception I have
thrown? Or as Winston said it once: Don't iterrupt me when I am
interrupting! :-)

OTOH providing access to the exception being thrown might make sense. Right
now, one may place a try-catch somewhere and catch the exception and *add'
info to it. But not in a destructor! If I say throw; and there is no
exception, I get a crash.

No what I was thinking about lately was two things, which *seems* to be a
good idea at the first sight. First is this:

if throw;

We can compilcate that with a (bool) function name, but basically what it
says is: if there is an exception, rethrown it. Then, you can catch it, and
fill in more info:

try {
if throw;
}
catch(ExcT &e){
a.addinfo(info);
throw;
}


Now the same could be done like this:

if catch(ExcT &e) {
a.addinfo(info);
throw;
}

What does the above mean? It would give the opportunity to the writers of
the RAII destructors to examine and/or extend exceptions of _known_ types!
Not just any! Known ones. At least I would not allow catch(...) there, as
you cannot examone or extend that exception, since you cannot refer to it.
BTW *known* exception types may be changed with the construct, but I for one
think that the need to do that must really very rare.

The above construct would also allow one to add a "stack dump" into an
exception etc.

> [...]
>>>> How could handling two errors be done?
>
>>> Is that a serious question?
>
>> Yes. How can you handle two errors, *at* *the* *same* *time*
>> with one thread of execution?
>
> It isn't so much a question of handling two errors -- exceptions
> don't handle errors any more than return codes do. It's a
> question of propagating two errors (which isn't really a
> problem), and matching said errors to their handlers (which is a
> very complex problem; you have to choose which one to handle
> first).

Right. That is what I meant. But reading it from you it even makes sense.
:-)

>>> There are dozens of possbible approaches to the issue.
>
>> Yet, you have failed so far to present even one example.
>
> That's true, but the real problem is precisely that there are
> dozens of possible approaches. No one of which would be
> appropriate for even a majority of the applications.

Yep, but my request for example was referring to another issue as well (I
have mentioned in another post), that so far we have failed to see *any*
example of a situation which cannot be handled using exceptions but can be
in other ways. And I was also frustratedly pointing out, that we are
discussing the huge nothing, as there are only statements and counter
statements, but not much to tie it to reality.

--
WW aka Attila
:::
If you think nobody cares if you're alive, try missing a couple of car
payments.

White Wolf

unread,
Feb 24, 2005, 8:18:08 PM2/24/05
to
Trevor L. Jackson, III wrote:
> Attila Feher wrote:
>
>> Trevor L. Jackson, III wrote:
>
>>> Cpde that might be called from a
>>> destructor executes in two distinct modes:
>>
>> No, it does not.
>>
>>> the normal mode and the unwind mode.
>>
>> No, it does not. The code you place into a destructor is _always_
>> running in unwind mode. It is higly irrelevant if it is running
>> because of a return statement or because of an exception.
>
> This is false. The system is clearly modal and uncaught_exception()
> disambiguates the two states. Perhaps you did not comprehend the
> meaning of "unwind mode". It means that destructors cannot leak
> exceptions without triggering termination.

It is a term you made up, so I cannot miss its meaning, since there is no
public meaning of it.

But anyway: I have told you my opinion, that RAII guards must assume (at all
times) that they are working in unwind mode. That is my opinion. I have
explained its reasons all over this topic. I can understand if you do not
share this opinion. Then let's see your specific, technical, real-life
examples of showing why do you think that you should assume that destructors
must have two modes of operation. Then we will have something to discuss
about.

I have repeated my request (I have made already in another sub-thread) and
will refrain from ping-ponging opinions any further. I have mine, I like
it, I can be convinced to change to a better one. Convinced is being the
key.

--
WW aka Attila
:::
Teach a man to make fire, and he will be warm for a day. Set a man on
fire, and he will be warm for the rest of his life. -- John A. Hrastar

Emil

unread,
Feb 24, 2005, 8:43:21 PM2/24/05
to
> In many cases, e.g. free() one can assume that release is "clean".

If I understand you correctly, by "clean" you mean that it can't
possibly encounter any problems.

What if each call to free() is logged in a file? Clearly, that could
fail. This leads to the following questions:

1) In your mind, is this good enough reason for free() to report the
failure to the caller?

2) In your opinion, what is a reasonable action the caller can take in
response (other than ignoring the failure)?

--Emil

Nicola Musatti

unread,
Feb 26, 2005, 12:12:17 AM2/26/05
to
The fact that there is seldom reason to call functions that could throw
in a destructor. An exception reaching the destructor's top level is an
indication that the class invariant did not hold and that notification
about it came too late; after all, even if you could remove the error
condition, you cannot undestroy the object and try again, can you?
Different real world examples have been posted to this thread: rollback
failure in a transactional system, or a failure in closing a file,
indicating that cached writes weren't performed properly.

When all's said and done, I consider the guideline not to throw from
destructors to be an aid to proper layering and don't see the need for
ubiquitous try/catch blocks in destructors.

Cheers,
Nicola Musatti

Nicola Musatti

unread,
Feb 26, 2005, 12:17:13 AM2/26/05
to

ka...@gabi-soft.fr wrote:
[...]

> It isn't so much a question of handling two errors -- exceptions
> don't handle errors any more than return codes do. It's a
> question of propagating two errors (which isn't really a
> problem), and matching said errors to their handlers (which is a
> very complex problem; you have to choose which one to handle
> first).

>From the language design / runtime support point of view one could
envision multiple argument catch clauses:

try {
} catch ( A &, B & ) { // matches according to throwing order
}

try {
} catch ( A &, ... ) { // matches A and swallows other exceptions
}

try {
} catch ( ... ) { // swallows any number of active exceptions
}

Cheers,
Nicola Musatti

ka...@gabi-soft.fr

unread,
Feb 26, 2005, 12:18:12 AM2/26/05
to
White Wolf wrote:
> ka...@gabi-soft.fr wrote:
> > Attila Feher wrote:
> >> Trevor L. Jackson, III wrote:

> > [...]
> >>>> than to ask the language to provide several threads of
> >>>> execution (remember, exceptions in C++ *are* terminating

> >>> No they are not.

> >> Yes, they are. Forgive me, for believing Bjarne Stroustrup
> >> and the standard.

> > Excuse me for butting into your "technical" discussion, but
> > I think you two are using "terminating" is different senses.
> > In literature about exceptions (in general, not in C++),
> > terminating has a very special meaning: a terminating
> > exception is one which doesn't allow continuation from the
> > point where the exception was raised. In this sense, C++
> > exceptions definitly are terminating. Intentionally; I seem
> > to remember reading about discussions in the committee on
> > the issue, and it was decided that the added complexity
> > necessary to support continuation wasn't worth the cost.

> And I meant that terminating. I even gave reference to the
> right chapter of D&E.

Sorry. I think I mixed up my attributions. Whoever said that
C++ exceptions are terminating was right, supposing this meaning
of terminating (which IMHO is the only one relevant when the
adjective applies to exceptions).

> >>>> and for good reasons [D&E]16.6) to handle several errors
> >>>> at the same time?

> >>> I did not ask for multiple threads of execution.

> >> Of course you did. You have asked for two or more
> >> exceptions to be handled at the same time. That is two or
> >> more threads of execution.

> > Be careful, Attila. C++ is capable of handling several
> > different bits of information, and acting on it, in a single
> > thread. It would certainly be possible to specify a model
> > where several exceptions were active at the same time. Of
> > course, if you use the rule, the first catch block to match
> > either wins, you'll often end up treating the wrong one
> > first, and if you introduce "priorities", it becomes even
> > more complicated -- how do you "rewind" the stack you've
> > unwound to handle the lower priority exception, after having
> > handled the higher one?

> So basically as you went on thinking about it, you have
> realized, that what I was saying (that the OP-requested
> feature would need more threads) seems to be true. To achieve
> that (non-complicated) scenario what the OP was asking for
> (handling two exceptions at the same time) needs two
> threads. :-)

Not at all. You have two (or more) pieces of information. You
treat them at two (or more) different places in the code. That
doesn't require multi-threading.

Note that the problem, as I see it, isn't so much one of
implementation; I'm pretty sure that whatever is wanted is
implementable. The problem is trying to define what is wanted.
It's nice to say that you want to be able to propagate two
exceptions at once, but there's no point discussing it too much
until you've defined exactly what you mean by it. And that's
where the real problems start.

> > What might be useful in the context of RAII is the
> > possibility of "replacing" an exception. If clean up is
> > handled in a catch block, this is trivial, just throw the
> > new exception, instead of the old one. If clean up is
> > handled in a destructor, it would be nice to be able to do
> > the same thing. (Nice, but not essential.)

> It does not make any sense to me. No offense meant. But if
> the first exception was not important enough to handle, then
> why the heck did you throw it in the first place???

The first exception is important. The second could be more
important.

> The first error *is* nearly always the most important, and the


> rest are (usually) consequnces. So ignoring the first error
> is something I strongly disagree with. I mean what makes a
> code, which I did not write, is not a handler, to overrule the
> exception I have thrown? Or as Winston said it once: Don't
> iterrupt me when I am interrupting! :-)

As you say, the first error is *nearly* always the most
important. Not always, but nearly always. I've worked on some
systems where "best effort", rather than full transaction
integrity (and atomicity) was all that was required. In case of
an error during some "sub-transaction", all that was required
was to roll-back that sub-transaction, and continue the main
transaction. Conceivably, some types of errors during the
partial rollback could require aborting the entire transaction.

> OTOH providing access to the exception being thrown might make
> sense. Right now, one may place a try-catch somewhere and
> catch the exception and *add' info to it. But not in a
> destructor! If I say throw; and there is no exception, I get
> a crash.

Good point. It would be very difficult to find a way to permit
using such a mechanism (either mine, or what you are suggesting)
safely in a destructor.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

pier...@hotmail.com

unread,
Feb 26, 2005, 12:42:44 AM2/26/05
to
White Wolf wrote:
> Trevor L. Jackson, III wrote:
> [SNIP]
> 1.) Do *all* the operations which can throw
>
> 2.) commit your work using operations which cannot throw

Well, if only the standard provided a can-I-throw-without-termination
function, we could have the desired option:

1.) Do your work. Use RAII everywhere you desire.

2.) Commit in destructor if it is safe to throw. Otherwise either not
commit or commit hiding the errors in a try block.

Note that is equivalent to what must be done now since when an
exception is thrown, you either not do the subsequent work (if it was
non-essential) or do it in a no-throw context.

Of course, once we have that function, it is a trivial step to simply
go to making the compiler and runtime do the
swallow-exception-if-needed step. At least as an option by annotating
the destructor in some way.

I currently do as I say, but it requires tooling the run-time and
destructors.

stork

unread,
Feb 26, 2005, 12:48:13 AM2/26/05
to
TJB replied to:

>
> I assume the above was not meant to be example of standard or even
portable
> C++ code? :-)

It's even worse, it's not even intel compiler portable. :-)

>
> For those who may not know (well, I guess there is nobody, but...)
int 3 is
> the assembler for the debugger-invoking-soft-interrupt of Intel
systems. At
> least as I recall. :-)

It does bring up a nice question though, is there a portable invoking
soft interrupt in C++? I would assume not because the language doesn't
have to require the existence of a debugger....

dietma...@yahoo.com

unread,
Feb 26, 2005, 6:40:28 AM2/26/05
to
Trevor L. Jackson, III wrote:
> So it is unreasonable to expect vendors to complete
> the design process (and make something that works in a more robust
manner).

You realize that the C++ committee consists mostly of compiler
vendors, don't you? ... and, though in parenthesis, you realize
that the current model works in a robust manner. It may not
cover directly all conceivable approaches to error handling but
it provides the necessary infrastructure. It also covers a
sufficient approach for most applications. What else should a
standardization committee do?

> Yet due to the fact that the existing design is part of the standard
a
> vendor that invested effort in replacing the existing design with a
> different one would be criticized for being non-standard.

It would depend on how they integrate their exception model.
However, I think everybody agreed that the current rule for
terminating the program if an exception leaves a destructor
called as part of stack unwinding due to an exception is the
right approach. The only controversial discussion about the
details of exceptions was whether they should be terminating
(which is not related to terminating the process) or should
allow continuation: the choosen model does not allow
continuation, i.e. once you throw an exception you really bail
out of the context you are currently in. You cannot continue
processing immediately at the place you were at after handling
the problematic situation in some form.

> So IMHO it is up to the Committee to either complete the design
process
> themselves or deprecate the existing design to signal vendors that
their
> effort would not be wasted or criticized.

... and IMO it completed the design process! The decision to
do exception handling the way it is done is probably also
guided by some practical considerations like "where to I put my
exception?" You cannot put it on the stack because this is what
you are currently unwinding. You cannot put it on heap either
because there may be no space. You can set aside a
corresponding area of stack during program start-up when you
can tell how big the biggest possibly thrown exception object
is (each throw statement copies the exception object and it can
be recorded how big the objects thrown are). Now, there are
three possibilities to how many exceptions can be on the way at
any particular time:

- None, i.e. exception handling is not supported. Once the
decision was made to provide exception handling, this is
removed immediately.
- One, i.e. the model we currently got.
- An infinite amount: limiting the number of exceptions to a
different number than 1 would clearly raise the question why
this is the number to cut off at? ... and it will always be
just one exception to few for some application. Of course,
an infinite amount of space cannot be set aside during
start-up, essentially prohibiting this option, too.

It is a long time since I read the D&E (roughly ten years) but
I seem to remember that the D&E has some discussion on this
issue.

> By "propagating from destructors" I assume you mean that with
> uncaught_exception() true an exception raised within the execution
> history of a destructor was not caught within that history (i.e. the
> destructor leaked a new exception that was raised in one of the
> functions the destructor called.).

Yes.

> > The number of
> > simultaneous exceptions currently being unhandled is actually
unbounded
> > but except for the first exception being thrown all other exception
are
> > thrown from within a destructor [and would cause program
termination if
> > they escape their respective destructor].
>
> That description leaves the status of exceptions thrown from within a

> function called by a destructor in limbo. Did you intend that it be
> treated as part of "within a destructor" or not?

Sure: just what the standard says on this issue.

> > The destructor has no option to change the exception currently
being
> > propagated nor has it any access to this exception.
>
> Key point. I do not comprehend why that it is useful for that
statement
> to be true. Do you care to suggest why that is useful?

I made this statement because it highlights that it is
impossible from within a destructor to

- stop the unwinding process, i.e. catch and handle the exception
somehow, e.g. to throw a different, more important one.
- use a member on the exception currently being thrown to add new
exceptions to the exception object.

Both of these features could in principle be implemented but
are not available. A standard conforming extension could
provide access to the exception object, though, e.g. with a
function like '_Current_exception()' returning a 'void*'. This
statement is mostly made to clarify why I need to use a global
variable for exception queuing.

> > Now, any intermediate layer which wants to handle some exceptions
which
> > might have been thrown during destruction cause by an uncaught
exception
> > can catch exceptions and deal with appropriate exceptions from the
> > queue:
> >
> > try { /*...*/ }
> > catch (...)
> > {
> > for (err::iterator it = err::queue.begin(), end =
err::queue.end();
> > it != end; ++it)
> > if (std::tr1::shared_ptr<interesting_exception const& ex =
> >
std::tr1::dynamic_pointer_cast<interesting_exception>(*it))
> > {
> > // handle the exception and probably remove it from the
queue
> > // afterwards
> > }
> > throw; // ... and there was another exception on its way:
rethrow
> > }
>
> Key point.
>
> The intermediate layer has to manage the exception context inline.

Actually, it does not! It is fairly easy to write fancy
functions detecting whether a certain exception is part of the
queue or to delegate processing of the queue to some entity
where handlers can be registered. In particular, it is possible
do something like this:

try { /*...*/ }
catch (...)
{
if (exception_ptr<some_exception> ex
= get_exception<some_exception>())
// handle the given exception
}

where 'some_exception' can be taken from the queue or be the
exception currently being thrown. Still, the destructor of
'exception_ptr<>' could arrange for the next exception in the
code to be thrown: if 'some_exception' is the type of the
currently thrown exception, it throws the next exception in
the queue, otherwise it rethrows the current exception.

> This defeats the transparency of the exception model and eliminates
the
> potential gains in code simplification that are one of the strongest
> reasons why exceptions have potential value.

I have no idea how you arrive at this conclusion!

> But in the wrong place. The replacement needs to happen at the site
> where the second exception would normally be handled, not at the
upper
> site (such as an entry point to an intermediate layer). Any other
> approach, including the one you outlined above, distributes the
> error/exception handling context throughout the application code.

I disagree: the only thing is the requirement for exception
queuing. This needs special attendance in destructors which
are rather involved and it assumes that exceptions are derived
from a suitable base class. I think both are reasonable
restrictions, even or more especially in huge applications: you
will almost certainly need more information than a "what()"
string to recover from errors, i.e. it uses a different base
class anyway. The restriction on destructors is worse but still
viable, especially as the number of destructors needing special
attendance should be rather small!

> Yes,
> you can make it work for some non-large applications. No, you cannot

> maintain it at anything near a reasonable cost.

You cannot retrofit such an error handling model. This is a
different cost from maintaining it: I think it is maintainable
since except for the common base class allowing exception
cloning (for exception which can potentially require queuing,
i.e. are possibly thrown from function called from destructors)
everything is rather local. Even if you need to convert
exceptions, this can be factored into local functions (although
probably at a high cost on performance).

> IMO a complete mechanism would have two additional valuable
properties.
> 1) exception specifications would be capable of supporting
> compile-time analysis (even of separate translation units; c.f.
export)
> to detect incompatibilities between caller and callee, including
> including exception specs in typedefs,

Exception specifications are dead in the water! They are more
trouble than they are worth. In fact, I have argued in the past
that exception specification defeat the whole purpose of
exception handling, i.e. allowing propagation of error
information over code which is entirely unware of these errors
to be handled at some other level. I think I have made my
position clear in the past and I'm too lazy to repeat it. The
only reasonable exception specification is that a function does
not throw: this information can be put to good use.

> and 2) exceptions raised in an
> execution history for which a compatible catch was available would
never
> terminate() -- there would be no obstructions between the throw() and

> the catch{}.

I think you can implement this need with reasonable effort and
this need is rather rare. The common case is well covered.

> So the thrust of my complaint is that I have an (IMHO reasonable)
> expectation that the Committee will continue to invent in order to
> achieve the goals initially set for the exception mechanism.

I strongly disagree: unless you (or someone else sharing your
position) work out how this can be implemented, how the
specification needs to be changed, and what impliciations it
has (e.g. on current code), no one will do the work. The
current exception handling mechanism may not be optimal but it
is not broken and I don't see any intention to change it. Since
the committee consists of volunteers, you should expect that
all participants just do what they are interested in.
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

dietma...@yahoo.com

unread,
Feb 26, 2005, 6:40:58 AM2/26/05
to
Trevor L. Jackson, III wrote:
> What have I missed?

That it is a bad idea to do excessive work in destructors!
Destructors are for cleaning up resources. Period. Nothing else,
no fancy computation, no unnecessary resource allocation (it may
be necessary to aquire e.g. a mutex lock for a shared resource).
Actually, the higher-level the classes I have, the rarer are
destructors (and copy ctor and copy assignment): There are
essentially no higher-level classes needing any destructors!
Those classes needing destructors are very low-level and have a
good idea on whether a function they call throws or not. Of
course, structuring an application like this assumes that you
think about error handling early on but this should be done for
large applications anyway: it is necessary if you need some form
of exception queuing to set up the infrastructure right at the
start of a project.


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

White Wolf

unread,
Feb 26, 2005, 10:11:30 AM2/26/05
to
stork wrote:
> TJB replied to:
>
>>
>> I assume the above was not meant to be example of standard or even
>> portable C++ code? :-)
>
> It's even worse, it's not even intel compiler portable. :-)

Actually int3 would look a bit better. :-) Although the surrounding part
would still be non-portable.

>> For those who may not know (well, I guess there is nobody, but...) int
>> 3 is the assembler for the debugger-invoking-soft-interrupt of Intel
>> systems. At least as I recall. :-)
>
> It does bring up a nice question though, is there a portable invoking
> soft interrupt in C++? I would assume not because the language doesn't
> have to require the existence of a debugger....

Or software interrupts: ;-)


--
WW aka Attila
:::
Two things that are essential to life is WD 40 and duct tape. If it moves
and it isn't supposed to use the duct tape. If it doesn't move and it's
supposed to use the WD 40.

White Wolf

unread,
Feb 26, 2005, 10:12:08 AM2/26/05
to
ka...@gabi-soft.fr wrote:
[SNIP]

> Good point. It would be very difficult to find a way to permit
> using such a mechanism (either mine, or what you are suggesting)
> safely in a destructor.

I think mine is fairly simple. To define as well as to implement. The
question is, do we really need it, or it only looks good. :-)

--
WW aka Attila
:::
Does the walker choose the path, or the path, the walker? - Sabriel, by
Garth Nix

Trevor L. Jackson, III

unread,
Feb 27, 2005, 7:53:08 PM2/27/05
to
Emil wrote:

>>In many cases, e.g. free() one can assume that release is "clean".
>
>
> If I understand you correctly, by "clean" you mean that it can't
> possibly encounter any problems.

That was my meaning. But free was just an example. It might be a bad
example because it is provided by the system. A UDF might be a better
example because one could be certain that it was exception free.

>
> What if each call to free() is logged in a file? Clearly, that could
> fail. This leads to the following questions:
>
> 1) In your mind, is this good enough reason for free() to report the
> failure to the caller?

Depends on the purpose of the logging. If it is a component of the
deliverable software it has to report the failure. If it is an effort
to instrument the software for the purpose of measuring or monitoring
it, than the failure should not be reported (because it would make the
measurement process intrusive).

>
> 2) In your opinion, what is a reasonable action the caller can take in
> response (other than ignoring the failure)?

In the case of free() with a log? If the log is part of a formal audit
abort() might be the proper response. A better response would be to
track the failures internally and use opportunistic logging, or to defer
reporting the failure (e.g., until program exit/cleanup).

I find it hard to conceive of an application with a logging free() where
the log was part of the app feature set. Perhaps we should pick a
better example to make your point?

/tj3

Trevor L. Jackson, III

unread,
Feb 27, 2005, 7:51:20 PM2/27/05
to
Bob Bell wrote:

> It's interesting to read D&E about the design of the exception
> mechanism of C++. Two models were proposed:
>
> -- the resuming model: after an exception is processed in a catch
> block, the catch block could optionally cause the original operation
> that threw to be retried. The code would "resume" where it left off as
> if the exception was never thrown.
>
> -- the terminating model: when an exception is thrown, the operation
> that threw the exception is terminated; that is, there is no provision
> for transferring control back to the original throw point, as in the
> resuming model. After an exception is processed in a catch block,
> execution continues with the first statement after the catch block.
>
> There were supporters for both models, and both models had prior art.
> What was interesting was that, despite the passion with which the
> resuming model advocates argued, there was little evidence that it was
> actually useful. In particular, there was one data point where a large
> system had been designed with resuming exception handling. However,
> resuming after an exception was found to be very complex and difficult
> to get right (i.e., it was a source of bugs). Over time, all of the
> resumption was removed from the system, and terminating exception
> handling was used instead.

Retry can be badly implemented. That should purpose no one. Reaching
the conclusion that retry is never useful appears to me to be
unjustified. After all that is the approach taken by set_new_handler().
Why should that capability be restricted to memory allocation?

>
> The upshot was that the ability to automatically retry failed
> operations was seen as having little utility in the real world.

That may have been the opinion of those who removed it, but it is not my
opinion. I have used it extensively and successfully in database
applications.

>
> The paragraph I'm responding to seems to suggest something like a
> resumption model; an error is detected, some kind of error handler is
> called, and if the error handler can correct the situation, the
> operation can be retried.

Yes.

>
> However, the strategy you describe doesn't seem to address a problem
> that exceptions solve quite well: how to transfer control from a
> low-level function that has detected an error (and which presumably
> cannot continue) to a (possibly much higher-level) calling function
> which can continue in the face of the error. Without an automated
> solution like exceptions, you are left to implement that mechanism
> yourself, e.g., by returning error codes up the stack.

If exceptions were only a transport mechanism there would not be a
problem. But it is more than that. It is an unwinding mechanism. I
have successfully used such mechanisms based on signals and on
longjmp(). The weakness of those systems was the registration and
maintenance of the todo list for the unwind mechanism. Lacking language
support it had to be done inline.

Exceptions have the potential to be vastly superior to hand-crafted
solutions because they offer hooks for automated registration of
resources (RAII is this thread's topic). The issue I have is that the
side effects of the design and implementation of exception handling have
a large negative value (a cost) that dilutes their potential value to
the point that it is hard to justify the widespread adoption of
exceptions as a fundamental for the development of robust software.

>
> Further, there doesn't seem to be any reason why you can't use both;
> have your error context stack as you describe. When an error is
> detected, invoke the handler on the top of the stack. It has the same
> set of available options as before, but it can also decide to throw an
> exception if the original operation is not retried.

Yes. Certainly. But If I am doing all the hard work in the application
code why do I need exceptions? Only if exception specifications become
useful does it make sense to build an application error handling system
on exceptions. Consider than in some environments language agility is
important. Exceptions prevent that.

/tj3

Trevor L. Jackson, III

unread,
Feb 27, 2005, 7:52:23 PM2/27/05
to
Nicola Musatti wrote:

> The fact that there is seldom reason to call functions that could throw
> in a destructor.

Agreed. But in a large application it is hard for the upper layers to
tell what they might be calling from a destructor. This why the
destructor leak issue is connected to the exception specification issue.

/tj3

It is loading more messages.
0 new messages