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

C++0x: Proposal for an additonal exception handling construct

0 views
Skip to first unread message

Alf P. Steinbach

unread,
Sep 8, 2002, 12:13:35 PM9/8/02
to

This proposal is based on a [comp.lang.c++.moderated]
discussion thread entitled "C++0x -- what's really
needed (LONG)", but differs substantially in details
from that thread's original concrete idea regarding
exception handling.


1 META DISCUSSION ISSUES

In contrast to the earlier idea this proposal involves
new keywords.

To avoid this thread degenerating into a debate about
new keywords (i.e., how difficult the keyword problem
is to solve), I suggest


* Please start a new thread if you feel like tackling
the topic of how to (not) introduce new keywords.

* In this thread, pro- and counter-arguments ASSUME
some solution to the problem of new keywords.


In other words, much like discussing spacesuit design
before anyone had entered space -- but in the hope or
assurance that that little practical problem ;-) would
be conquered.


2 BACKGROUND

Current C++ has two cases of "fallthrough" in control
structures, namely in "switch" and in "catch". The
name "catch" aptly reflects the common usage of that
clause: to catch an exception and preferably swallow it,
by falling through via a normal return path, which is
the default. Consequently the compiler has no way of
checking that e.g. a rethrow is missing, which easily
leads to oversights where a function might return
normally when in fact it failed.

In contrast, Eiffel's exception handling enforces that
if an exception occurs, then the function (in C++ the
analogous construct would be "try-catch") must either try
something else to establish its normal case contract, or
else it must throw -- by default the original exception
is rethrown.

Basic idea:


either succeed (fulfill contract), or else throw.


Elaboration: in the case of an exception, either try again
(the contents of the whole try-block), or try something else,
or throw.

This proposal is one way of adding *some* compiler support
for that concept; and to the degree possible enforce it.

The earlier thread's proposal was modelled more or less
directly on the Eiffel solution, but met with some heavy
criticism. In particular, critique was levelled at that
proposal's use of or implicit requirement of a kind of
try-counter, and of bundling together the original "try"
code with later "retry" code. Although I would have
gladly made those sacrifices to obtain enforced succeed-
or-throw, I now think I may have found a better way.

3 NEW CONSTRUCT

The aim of this new construct is to support "succeed-or-
throw" while not changing any of the C++ exception
machinery. It is, in other words, purely a syntactical
construct. But as such, introduces compiler checking.


3.1 Keywords.

The following new keywords are used -- as mentioned
plase debate the problem of introducing new keywords in
the language in a separate (related) thread, to focus
this thread down on issues with the construct:


succeed_or_throw
succeed
fallback_to


Note: I'm sure others can come up with better names.


3.2 Basic usage.

The "succeed_or_throw" corresponds to "try", but starts
a different construct. Instead of "catch" a
"succeed_or_throw" can only be followed by "fallback_to".

Example of typical usage:


succeed_or_throw
{
primaryImplementation();
succeed;
}
fallback_to
{
fallbackImplementation();
succeed;
}
fallback_to
{
fallbacksFallbackImplementation();
succeed;
}

// Hypothetical label for "succeed" here.


If primaryImplementation() throws, then the exception
is caught by the first "fallback_to", and the
fallbackImplementation() is executed. If also that
implementation throws, then the exception is caught
by the second "fallback_to" (and so on). I'd think
in most cases there'd be only one fallback, and
perhaps most commonly, it would be one that cleaned
up and then gave up, simply by throwing -- but if
that was all then we wouldn't need general try-catch.

In the absence of execution of either "succeed"
or a normal function "return", a standard exception
is thrown automatically when execution reaches the
closing brace of either "succeed_or_throw" or
"fallback_to". The "succeed" statement is equivalent
to a goto to a label placed after the whole
construction. If execution reaches that
hypothetical label, then one attempt has succeeded,
i.e. the normal case contract has been fulfilled.

A "succeed_or_throw" or "fallback_to" clause, except
the last "fallback_to", must contain at least one
"succeed" or "return". If the last "fallback_to"
clause doesn't, then it never succeeds, i.e. always
throws, and is then effectively a cleanup-and-throw.

3.3 Obtaining exception information.

A "fallback_to" can include a declaration of an
object, like so:


succeed_or_throw
{
primaryImplementation();
succeed;
}
fallback_to( MyXInfo x )
{
fallbackImplementation( x.someInfo() );
succeed;
}
fallback_to( Whatever x )
{
// Just clean up and give up.
cleanUp();
throw SomeSpecialException( x );
}


Any class is allowed as MyXInfo or Whatever, as
long as it has at least one available constructor of
one argument, and an available destructor. If the
caught exception is of type T, then the first
"fallback_to" clause above is equivalent to


// Equivalence for "fallback_to" construct.

T exception; // Hypothetical variable
MyXInfo x( exception );
fallbackImplementation( x.someInfo() );
succeed;


If dynamically there is no suitable constructor in
the class, then the exception is rethrown immediately
(or simply not caught, which I think is equivalent).
Making use of exception information can therefore
be a risky business when one doesn't control the
called code. That's the same as with "try-catch".

A standard class for this purpose would be nice.

Big if if if: perhaps the above could be extended to
handle the thorny "exception in destructor" case also,
later on, e.g. in C++1x??? Just a thought. May not
be practical.


4 What is achieved?

The goal was to support the "succeed-or-throw" paradigm.

First, the proposed construction has the opposite default
of "catch", namely to throw. Here success is explicit,
and failure implicit. In C++ of today success is implicit
while failure indication must be explicitly propagated.

In other words, the construction will help to have
exceptions properly propagated.

Second, the construction is at a higher level than try-
catch. The try-catch can be used in numerous ways, and
one must inspect the code very very carefully to find out
exactly how it's used in any given case. I think the
above construct has fewer degrees of freedom, and so
is both easier to understand, use and maintain.

In other words, the construction will help to steer
programmers towards more safe, non-arbitrary handling.

Third, unintentional but nice, the new construction
avoids nested control flow for the most common cases.

Fourth, the new construction avoids code redundancy
by making it possible to catch and get information
from a number of different exceptions in one fell
swoop. This can in theory also be done with try-catch,
by rethrowing and catching in a generic catch-clause,
but that's messy and still not supported by at least
one of the most popular compilers (msvc).

Fifth, there is a slight possibility (I don't know)
that it can be extended to deal with "nested" exceptions,
the thorny problem of exceptions in destructors.


Cheers,

- Alf

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Matvei Brodski

unread,
Sep 11, 2002, 6:44:23 PM9/11/02
to


Alf P. Steinbach wrote:

>
> Example of typical usage:
>
>
> succeed_or_throw
> {
> primaryImplementation();
> succeed;
> }
> fallback_to
> {
> fallbackImplementation();
> succeed;
> }
> fallback_to
> {
> fallbacksFallbackImplementation();
> succeed;
> }
>
> // Hypothetical label for "succeed" here.
>
>
> If primaryImplementation() throws, then the exception
> is caught by the first "fallback_to", and the
> fallbackImplementation() is executed. If also that
> implementation throws, then the exception is caught
> by the second "fallback_to" (and so on).

So, it is basically an equivalent of:


try
{
primaryImp();
return;
}
catch(...) {}
try
{
fallbackImp();
return;
}
catch(...) {}

fallbackFallbackImp();
return;


Or, am I missing something?
Matvei.

Bob Bell

unread,
Sep 13, 2002, 10:50:18 AM9/13/02
to

alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message news:<3d7a1ff7....@news.online.no>...

> Basic idea:
>
>
> either succeed (fulfill contract), or else throw.
>
>
> Elaboration: in the case of an exception, either try again
> (the contents of the whole try-block), or try something else,
> or throw.

What's missing for me is in the area of motivation. Why do we need the
mechanism described in this proposal? Specifically:

Your proposal covers the case where a function can try two or more
alternatives to satisfy its contract. (For functions which try only
one, no special handling is required -- the function simply lets the
exception propagate out of it and that's that.)

How common is it to write functions that can try two or more
alternatives? For myself, the answer is "I can't remember the last
time I wrote one." Does anyone have different experiences in this
area? Is there any evidence that these "two alternatives" functions
are both 1) common enough; and 2) problematic enough to warrant adding
language support to make writing them easier and safer?

Bob Bell

Nicola Musatti

unread,
Sep 13, 2002, 3:59:37 PM9/13/02
to


Matvei Brodski wrote:
[...]


> So, it is basically an equivalent of:
>
> try
> {
> primaryImp();
> return;
> }
> catch(...) {}
> try
> {
> fallbackImp();
> return;
> }
> catch(...) {}
>
> fallbackFallbackImp();
> return;

Or even

return primaryImp() || fallbackImp() || fallbackFallbackImp();

return values rule! :-)

Cheers,
Nicola Musatti

Alf P. Steinbach

unread,
Sep 13, 2002, 3:59:36 PM9/13/02
to

On 11 Sep 2002 22:44:23 GMT, Matvei Brodski <mat...@agoron.com>
wrote:

Yes.

I'll post an equivalence scheme later, with some additions & mods
of the scheme (working on that, basic mod is to rename a final
fallback that has no succeed to something else, e.g. clean_up,
which can then benefit from compiler checking), but


* I think I'll post that in [comp.std.c++] only...


For what it's worth, the example above could be mechanically
translated to try-catch based code as follows (off the cuff):


// "succeed_or_throw"
try
{
primaryImpl();
goto succeeded; // succeed;
throw std::default_exception();
}
catch( ... ) // "..." or something, not spec. yet
{
// "fallback_to"
try
{
fallbackImpl();
goto succeeded; // succeed;
throw std::default_exception(); // Or just "throw";
}
catch( ... ) // "..." or something, not spec. yet
{
"fallback_to"
{
fallbacksFallbackImpl();
goto succeeded;
}
throw std::default_exception(); // Or just "throw;"
}
throw std::default_exception(); // Or just "throw;"
}
succeeded:


Note: the "goto" is for presentation clarity only, and unreachable
code, all those "throw"s, included for same reason.

Note also: for the normal case, i.e. no throw executed, this is
a *structured* construct, with one way in and one way out, only.

And please note: I just wrote this off the cuff, right here and
now, so it's not any kind of technical reference to what I meant,
more like the general idea, sans retrieval of exception info.

The "catch(...)"es nag me because (1) I'm all too familiar with the
MSVC compiler, which has non-standard default semantics for generic
catch, and in addition has a bug that calls the destructor of an
exception object twice when the exception is rethrown and caught
within a catch, and (2) because low-level code then has no way of
sort of "pushing" an exception right through the normal exception
handling. But using a restricted catch, as is the usual with
try-catch, would mean that some exceptions could, in theory, slip
through the net.

Do we want a net or do we want a brickwall, that's question #1.
As described in the OP, generic catch, i.e. brickwall.

Do we want some default_exception as default, or rethrow as
default, that's question #2.

There are probably more questions...

Cheers,

- Alf

Alf P. Steinbach

unread,
Sep 14, 2002, 11:59:10 AM9/14/02
to

On 13 Sep 2002 14:50:18 GMT, bel...@pacbell.net (Bob Bell) wrote:

>
>alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message news:<3d7a1ff7....@news.online.no>...
>> Basic idea:
>>
>>
>> either succeed (fulfill contract), or else throw.
>>
>>
>> Elaboration: in the case of an exception, either try again
>> (the contents of the whole try-block), or try something else,
>> or throw.
>
>What's missing for me is in the area of motivation. Why do we need the
>mechanism described in this proposal?

I wrote 5 points about that, "4: What's been achieved", in the
original posting. Is a repost needed?

> Specifically:
>
>Your proposal covers the case where a function can try two or more
>alternatives to satisfy its contract. (For functions which try only
>one, no special handling is required -- the function simply lets the
>exception propagate out of it and that's that.)

Nope, incorrect. It may seem that way *with the new construct*,
which encourages that style. And that's a good reason for having it.

Many people use try-catch as a simple cleanup mechanism even for
a single attempt at something. RAII can alleviate some of that,
but isn't a general solution. The proposed construct helps to
ensure that in the single-attempt case the exception (or at
least, some exception) is actually rethrown after cleanup.

This is in my experience a common problem, both due to bugs in
code and due to bugs in programmer's conception, who mistakenly
often think that the purpose of "catch" is to actually "catch".

>How common is it to write functions that can try two or more
>alternatives? For myself, the answer is "I can't remember the last
>time I wrote one." Does anyone have different experiences in this
>area? Is there any evidence that these "two alternatives" functions
>are both 1) common enough; and 2) problematic enough to warrant adding
>language support to make writing them easier and safer?

First, fallbakcs are but one problem. Whenever you see

a nested try-catch

you see an attempt at realizing more than one alternative, a
fallback strategy. How often do you see a nested try-catch?

Then, keep in mind that nesting is a code structure analogous
to a free, while the proposed construct is linear. Which do
you think is simpler?

Second, the single-attempt case with cleanup is just as important
to support, to the degree possible ensure failure => exception;
and not just exception within the construct, but out of it.

Third, the centralization of exception info gathering is just
as important to support. How often do you see redundant code
in multiple (differently typed) catches for the same try?

Probably many more good reasons / motivation, but I think the
above is more than enough. Exception handling is the greatest
problem of C++ coding today, and it's also the most difficult
to debug or make sense of afterwards. I think any construct
that can help with that should be welcomed with open arms and
shouts of joy; and I think this construct is particularly
likely to provide real help (although I can see improvements).

Cheers,

- Alf

Alf P. Steinbach

unread,
Sep 14, 2002, 11:59:11 AM9/14/02
to

On 13 Sep 2002 19:59:37 GMT, Nicola Musatti
<Nicola....@ObjectWay.it> wrote:

>Matvei Brodski wrote:
>[...]
>> So, it is basically an equivalent of:
>>
>> try
>> {
>> primaryImp();
>> return;
>> }
>> catch(...) {}
>> try
>> {
>> fallbackImp();
>> return;
>> }
>> catch(...) {}
>>
>> fallbackFallbackImp();
>> return;
>
>Or even
>
>return primaryImp() || fallbackImp() || fallbackFallbackImp();

I think this coding style has its place and should not be
belittled, especially in small-scale script code (e.g. Perl).

But it isn't a very general solution; it's a very specific
solution with a limited area of application -- although it
scores high within that limited area.

E.g., there is the problem of closure, passing context
information to the functions; there is the problem of
proliferation of tightly coupled functions in a class
(although I've made a proposal for that, C++0x support
for nested functions sans access to local variables);
there is the problem of failure propagation (which leads
to deeply nested, complex code), and so on and so forth.


>return values rule! :-)

I guess that's a joke, but have to answer seriously: there
are reasons why we have exception handling in the language.
One main reason is that in contrast to ordinary return
values the client code cannot fail to either pass or
handle an exception, i.e. the client code cannot just ignore
an exception; but it's easy to fail to check a return value.

The one place where current C++ doesn't enforce this is
the passing of exceptions out of catch-blocks, and
that problem is (I think adequately) addressed by the
proposed construct -- which correspondingly is a main
argument in favor of adopting it, in C++0x or later.

On the other hand, it stands to reason that if one does not
see any advantage in exceptions, one chooses disbelief, then
it is doubtful whether the cleaner exception handling style
I think the proposed construct would introduce would be
enough to convince that person to start using exceptions...

Hth.,

- Alf

Bob Bell

unread,
Sep 15, 2002, 11:16:41 AM9/15/02
to

alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message news:<3d8223a5....@news.online.no>...

> On 13 Sep 2002 14:50:18 GMT, bel...@pacbell.net (Bob Bell) wrote:
> >What's missing for me is in the area of motivation. Why do we need the
> >mechanism described in this proposal?
>
> I wrote 5 points about that, "4: What's been achieved", in the
> original posting. Is a repost needed?

No. The nature of my question is summed up by the other questions I
asked, which you didn't answer and I repeat near the bottom of this
post.

> > Specifically:
> >
> >Your proposal covers the case where a function can try two or more
> >alternatives to satisfy its contract. (For functions which try only
> >one, no special handling is required -- the function simply lets the
> >exception propagate out of it and that's that.)
>
> Nope, incorrect. It may seem that way *with the new construct*,
> which encourages that style. And that's a good reason for having it.
>
> Many people use try-catch as a simple cleanup mechanism even for
> a single attempt at something.

Many people don't know how to use exceptions. I'd rather educate them
than change the language to suit their faulty expertise.

> RAII can alleviate some of that,
> but isn't a general solution.

That's exactly what RAII is: a general solution to the problem of
cleanup. I'd be curious to hear why you think it's not.

> The proposed construct helps to
> ensure that in the single-attempt case the exception (or at
> least, some exception) is actually rethrown after cleanup.

Anyone using this construct for single attempts is simply writing
error-prone code, due to the risk of failure to clean up properly.

> This is in my experience a common problem, both due to bugs in
> code and due to bugs in programmer's conception, who mistakenly
> often think that the purpose of "catch" is to actually "catch".

I think it's probably a bigger mistake to think that the purpose of
"catch" is to "catch & throw."

Using a try block leads to more problems that have to do with
incorrect clean up than failure of exception transparency. In any
case, RAII is a win on both fronts, and should be preferred over try
blocks.

> >How common is it to write functions that can try two or more
> >alternatives? For myself, the answer is "I can't remember the last
> >time I wrote one." Does anyone have different experiences in this
> >area? Is there any evidence that these "two alternatives" functions
> >are both 1) common enough; and 2) problematic enough to warrant adding
> >language support to make writing them easier and safer?
>
> First, fallbakcs are but one problem. Whenever you see
>
> a nested try-catch
>
> you see an attempt at realizing more than one alternative, a
> fallback strategy. How often do you see a nested try-catch?

Very, very rarely. How often do you see one?

> Then, keep in mind that nesting is a code structure analogous
> to a free, while the proposed construct is linear. Which do
> you think is simpler?

Single attempt functions.

> Second, the single-attempt case with cleanup is just as important
> to support, to the degree possible ensure failure => exception;
> and not just exception within the construct, but out of it.

RAII supports this pretty well already.

> Third, the centralization of exception info gathering is just
> as important to support. How often do you see redundant code
> in multiple (differently typed) catches for the same try?

Very, very rarely.

> Probably many more good reasons / motivation, but I think the
> above is more than enough.

OK, we've just finished with your response to my questions, but I
don't think you answered them, so here they are again:

How common is it to write functions that can try two or more
alternatives?

Is there any evidence that these "two alternatives" functions are both


1) common enough; and 2) problematic enough to warrant adding language
support to make writing them easier and safer?

> Exception handling is the greatest


> problem of C++ coding today,

I don't agree. It's actually a lot more straightforward than most
programmers realize.

> and it's also the most difficult
> to debug or make sense of afterwards. I think any construct
> that can help with that should be welcomed with open arms and
> shouts of joy;

Wrong. We must be careful about what new constructs are welcomed into
the language, and not welcome them willy-nilly because some
programmers are uncomfortable with existing constructs.

> and I think this construct is particularly
> likely to provide real help (although I can see improvements).

I'm not getting this. I see three new keywords and radical new
behavior, all to fix a bug that some programmers create by failing to
write exception-transparent functions, without any indication of how
common this bug really is.

I appreciate all the effort that you have put into thinking about
this. But there are big pieces missing for me, and you have done
nothing to supply them.

Bob Bell

Hyman Rosen

unread,
Sep 16, 2002, 11:05:09 AM9/16/02
to

Alf P. Steinbach wrote:
> For what it's worth, the example above could be mechanically
> translated to try-catch based code as follows (off the cuff):

In your code, all of the throws are unreachable!

I would do it like this - it's much neater, with no nesting:

// "succeed_or_throw"
do
{
try {
primaryImpl();
break; // succeed;


} catch( ... ) { } // "..." or something, not spec. yet

try {
fallbackImpl();
break; // succeed;


} catch( ... ) { } // "..." or something, not spec. yet

fallbacksFallbackImpl();
break; // succeed;
}
while (throw std::default_exception(), false);

You can wrap the idiom up in macros, if you like:

#define succeed_or_throw do
#define attempt(code) try { code; break; } catch (...) { }
#define final(code) code; break;
#define succeeded_or_threw while (throw std::default_exception, false)

succeed_or_throw {
attempt(primaryImpl());
attempt(fallbackImpl());
final(fallbacksFallbackImpl());
} succeeded_or_threw;

All of which points to the fact that C++ is a multi-paradigm language
that lets you do what you want to do, and doesn't need special constructs
to support some particular stylized form of exception handling.

Alf P. Steinbach

unread,
Sep 17, 2002, 4:47:33 PM9/17/02
to
On 16 Sep 2002 15:05:09 GMT, Hyman Rosen <hyr...@mail.com> wrote:

>
>Alf P. Steinbach wrote:
>> For what it's worth, the example above could be mechanically
>> translated to try-catch based code as follows (off the cuff):
>
>In your code, all of the throws are unreachable!

Very true. As I wrote in that posting:


<quote>

Note: the "goto" is for presentation clarity only, and
unreachable code, all those "throw"s, included for same reason.

</quote>


In a corresponding mechanical translation of, e.g., a conditional
"succeed", the throws may (or may not) be reached.

The reason for showing what a mechanical translation w/o optimization
might look like was to show how a compiler could implement it in
general, not how we programmers might implement a particular example
using contextual insights.

>I would do it like this - it's much neater, with no nesting:
>
> // "succeed_or_throw"
> do
> {
> try {
> primaryImpl();
> break; // succeed;
> } catch( ... ) { } // "..." or something, not spec. yet
>
> try {
> fallbackImpl();
> break; // succeed;
> } catch( ... ) { } // "..." or something, not spec. yet
>
> fallbacksFallbackImpl();
> break; // succeed;
> }
> while (throw std::default_exception(), false);

True again, no nesting => seemingly neater code, and even means, as
you show, that it can be "implemented" as macros!


Problem 1: the nesting is *required* in order to access the original
exception information, and in order to rethrow original exception.

Problem 1 is solved by the proposed construct.


Problem 2: no compiler checking of including "succeeded" command;
and if a clean_up clause extension is added, no compiler checking
of not trying to succeed in that clause.

Problem 2 is solved by the proposed construct.


Problem 3: it's up to the programmer to "implement" the construct
correctly, even with the help of macros.

Problem 3 is solved by the proposed construct.

>You can wrap the idiom up in macros, if you like:
>
>#define succeed_or_throw do
>#define attempt(code) try { code; break; } catch (...) { }
>#define final(code) code; break;
>#define succeeded_or_threw while (throw std::default_exception, false)
>
>succeed_or_throw {
> attempt(primaryImpl());
> attempt(fallbackImpl());
> final(fallbacksFallbackImpl());
>} succeeded_or_threw;
>
>All of which points to the fact that C++ is a multi-paradigm language
>that lets you do what you want to do, and doesn't need special constructs
>to support some particular stylized form of exception handling.

Ah, IMHO your example (or rather, the misconception that it does the
same as the original) shows precisely why the language does need some
special construct!

And, again, it's more important what effect the construct has than
whether it's "stylish" or not. Quality, not featurism. But I'm glad
you think the construct is stylish. ;-)

Cheers,

- Alf
---


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Hyman Rosen

unread,
Sep 18, 2002, 3:08:02 AM9/18/02
to
Alf P. Steinbach wrote:
> Ah, IMHO your example (or rather, the misconception that it does the
> same as the original) shows precisely why the language does need some
> special construct!

Your example did not show any use being made of exception information.

From the general response, it appears that this is a design pattern
with a customer of one. I don't expect we'll be seeing this in C++
any time soon.


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]


[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Alf P. Steinbach

unread,
Sep 18, 2002, 3:40:24 AM9/18/02
to
Ah, you wrote so much that again I must use that old trick:

* Please read through to THE END before starting to write
a response (or thinking about doing that).


In particular, at least read the section marked <read>.

On 15 Sep 2002 15:16:41 GMT, bel...@pacbell.net (Bob Bell) wrote:

>alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message news:<3d8223a5....@news.online.no>...
> > On 13 Sep 2002 14:50:18 GMT, bel...@pacbell.net (Bob Bell) wrote:
> > >What's missing for me is in the area of motivation. Why do we need the
> > >mechanism described in this proposal?
> >
> > I wrote 5 points about that, "4: What's been achieved", in the
> > original posting. Is a repost needed?
>
>No. The nature of my question is summed up by the other questions I
>asked, which you didn't answer and I repeat near the bottom of this
>post.
>
> > > Specifically:
> > >
> > >Your proposal covers the case where a function can try two or more
> > >alternatives to satisfy its contract. (For functions which try only
> > >one, no special handling is required -- the function simply lets the
> > >exception propagate out of it and that's that.)
> >
> > Nope, incorrect. It may seem that way *with the new construct*,
> > which encourages that style. And that's a good reason for having it.
> >
> > Many people use try-catch as a simple cleanup mechanism even for
> > a single attempt at something.
>
>Many people don't know how to use exceptions.

In my experience that is literally true. But the implication that
try-catch is mainly used because of ignorance of some better way is
IMO incorrect. What is your basis for that implication?

>I'd rather educate them than change the language to suit their
>faulty expertise.

Faulty expertise, indeed. I did a search for "catch" in the
MSVC 7.0 library files, 105 files found. The very first
..cpp file was [locale.cpp], copyright 1992-2001 by P.J. Plauger,
where the very first catch was in

locale::locale(const locale& loc, const locale& other, category cat)
: _Ptr(_NEW_CRT _Locimp(*loc._Ptr))
{ // construct a locale by copying named facets
_TRY_BEGIN
_Locinfo _Lobj(loc._Ptr->_Catmask, loc._Ptr->_Name.c_str());
_Locimp::_Makeloc(_Lobj._Addcats(cat & other._Ptr->_Catmask,
other._Ptr->_Name.c_str()), cat, _Ptr, &other);
_CATCH_ALL
_DELETE_CRT(_Ptr->_Decref());
_RERAISE;
_CATCH_END
}

Now P.J.Plauger here uses a style that's well suited to
shortening template code, a style some people disagree with,
but you think his expertise is faulty? That you should
educate him?

Not that I know whether or not he'd support the proposed
construct, of course; I'm referring to your statement that
you'd, quote, "rather educate [the programmers who use try-
catch for single-attempts] to suit their faulty expertise".

Following your logic to the end we should throw overboard
every structured control construct (or at least, they should
ideally never have been forced on us) and instead educate
people in correct use of goto -- because only incorrect
usage is troublesome.

> > RAII can alleviate some of that, but isn't a general solution.
>
>That's exactly what RAII is: a general solution to the problem of
>cleanup. I'd be curious to hear why you think it's not.

In a program without at least a top-level try-catch the first
exception will terminate it. So RAII is not a general solution.

There is also the cost, inconvenience and inefficiency of writing a
cleanup class for every little small thing. I guess that was
the main reason for the decision to use try-catch above. So
RAII is not a general solution.

And there is the small problem of exceptions in destructors, which
can in some cases be (partially or completely) solved using try-
catch, so RAII is not a general solution.

And there are many other ways that RAII falls short of being
a general solution, e.g. the cleanup where an exception must
be translated to a return value -- which is not uncommon --
so RAII is not a general solution.

In short, RAII is a technique that is powerful and IMHO should
be used whenever it is (1) theoretically applicable and (2)
the cost is not prohibitive relative to what's to be achieved.
These two criteria are not always both satisfied. So RAII is
not a general solution.

Let me state that one more, next to final time.

RAII is not a general solution.

> > The proposed construct helps to
> > ensure that in the single-attempt case the exception (or at
> > least, some exception) is actually rethrown after cleanup.
>
>Anyone using this construct for single attempts is simply writing
>error-prone code, due to the risk of failure to clean up properly.

With that I must agree to some extent. Using try-catch directly
is IMHO even more error-prone. Yet top-level programmers routinely
choose to do that, for good reasons; and mediocre programmers
(one must assume that 50% of programmers fall below the median)
naturally do so too -- which means current C++ code is risky.

It is a good argument for the construct, not against it.

> > This is in my experience a common problem, both due to bugs in
> > code and due to bugs in programmer's conception, who mistakenly
> > often think that the purpose of "catch" is to actually "catch".
>
>I think it's probably a bigger mistake to think that the purpose of
>"catch" is to "catch & throw."

Anyone who thought so would be making a mistake, yes, because a
catch can be used for both purposes, not just one of them. But
it would be *the lesser* mistake to always rethrow or to
inadvertently rethrow, because its effects would be immediately
apparent. It's difficult to ignore an exception, while it's
easy to ignore the absence of an exception -- if you follow me.


>Using a try block leads to more problems that have to do with
>incorrect clean up than failure of exception transparency. In any
>case, RAII is a win on both fronts, and should be preferred over try
>blocks.

Some of the limitations of RAII are listed above. Here's one more.
RAII doesn't support a fallback position.

RAII is not a general solution.

But lest you misunderstand what I'm saying (you probably would;
if I get the gist of your argument you see the new construct as
well as try-catch as being in direct competition with RAII):

<read>

RAII and try-catch (or the new construct) are complementary.

Both techniques are needed in practical C++ programming.

A conflict only exists when one is improperly forced into
solving what would more naturally be expressed with the other,
e.g. by thinking one technique is non-contextually "better".

</read>


> > >How common is it to write functions that can try two or more
> > >alternatives? For myself, the answer is "I can't remember the last
> > >time I wrote one." Does anyone have different experiences in this
> > >area? Is there any evidence that these "two alternatives" functions
> > >are both 1) common enough; and 2) problematic enough to warrant adding
> > >language support to make writing them easier and safer?
> >
> > First, fallbakcs are but one problem. Whenever you see
> >
> > a nested try-catch
> >
> > you see an attempt at realizing more than one alternative, a
> > fallback strategy. How often do you see a nested try-catch?
>
>Very, very rarely. How often do you see one?

Not often, but not very very rarely. During my years as a consultant
(with the largest firm, it shall go unnamed here) improperly designed
and coded nested try-catches did cause some very costly bugs, which
I fixed. But admittedly those bugs were mostly in Java.

> > Then, keep in mind that nesting is a code structure analogous
> > to a free, while the proposed construct is linear. Which do
> > you think is simpler?
>
>Single attempt functions.

Do the logic, if you please: if everything is single attempt, then
the program aborts on the first exception. Thus, having established
that at least one part of the system cannot be single attempt, for
that part or those parts that aren't: which do you think is simpler?

For that matter: which do you think is simpler for single-attempt,
and why? (Keep in mind the situations sketched earlier.)


> > Second, the single-attempt case with cleanup is just as important
> > to support, to the degree possible ensure failure => exception;
> > and not just exception within the construct, but out of it.
>
>RAII supports this pretty well already.

See above.

> > Third, the centralization of exception info gathering is just
> > as important to support. How often do you see redundant code
> > in multiple (differently typed) catches for the same try?
>
>Very, very rarely.

I.e., you and/or your co-workers don't use libraries that throw
something other than standard exceptions, and furthermore you
don't use exception typing to differentiate various errors.
Not that that is necessarily bad; I think it's mainly good.
But it's not always the situation, and that's the reason why
the language supports multiple catches for the same try.

> > Probably many more good reasons / motivation, but I think the
> > above is more than enough.
>
>OK, we've just finished with your response to my questions, but I
>don't think you answered them, so here they are again:

>How common is it to write functions that can try two or more
>alternatives?

See above; using only single-attempt the program must necessarily
abort on the first exception.

>Is there any evidence that these "two alternatives" functions are both
>1) common enough; and 2) problematic enough to warrant adding language
>support to make writing them easier and safer?

See earlier postings; but in short: I think so, based on my own
experience. Your experience doesn't match. But then you seem to
have a lot of misconceptions, e.g. that P.J.Plauger etc. use
try-catch out of "faulty expertise" and need some education.

> > Exception handling is the greatest
> > problem of C++ coding today,
>
>I don't agree. It's actually a lot more straightforward than most
>programmers realize.

That's nice.

> > and it's also the most difficult
> > to debug or make sense of afterwards. I think any construct
> > that can help with that should be welcomed with open arms and
> > shouts of joy;
>
>Wrong. We must be careful about what new constructs are welcomed into
>the language, and not welcome them willy-nilly because some
>programmers are uncomfortable with existing constructs.
>
> > and I think this construct is particularly
> > likely to provide real help (although I can see improvements).
>
>I'm not getting this. I see three new keywords and radical new
>behavior, all to fix a bug that some programmers create by failing to
>write exception-transparent functions, without any indication of how
>common this bug really is.

Exceptions are, by definition, exceptional. They don't occur often.
Bugs in exception handling therefore manifest themselves with low
frequency. But they are costly. And very difficult to track down.

Regarding statistics: I've no idea. Perhaps somebody else knows.

But IMHO we should not decide to throw away a safety improvement,
which I think it would be, because of lack of statistics. There
may be other reasons, however, outside of what we've discussed.

>I appreciate all the effort that you have put into thinking about
>this. But there are big pieces missing for me, and you have done
>nothing to supply them.

On the contrary, I've now used a lot of time replying to you,
two times, plus describing the scheme and replying to others.

I hope that you can now see the truth of the <read> section.

Sincerely,

- Alf

Alf P. Steinbach

unread,
Sep 18, 2002, 3:36:01 PM9/18/02
to
A FORMAL DEFINITION OF THE "SUCCEED_OR_THROW" CONSTRUCT.

This formal definition is posted only to [comp.std.c++].

[comp.lang.c++.moderated] has been removed in order to reduce
the lag-time for responses.

The formal definition is meant to form a basis for concrete,
informed discussion (perhaps of changes), and to show

(1) That the construct can be efficiently implemented, at
least as efficiently as try-catch.

(2) That the construct can be implemented without changing
the exception handling mechanism of current C++, in
particular without changing the meaning of throwing.

(3) That the general construct's equivalence to a hierarchy
of nested try-catch'es resolves all issues regarding
interaction with try-catch and throwing.

I'd like to present an actual implementation, but discovered
that modififying the g++ compiler required more work than I'm
willing to put in just to produce a proof-of-concept (I
implemented the "succeed_or_throw{ statements... }" part, but
without checking for disallowed control statements etc.). So
this formal definition isn't one that's been verified by
actual implementation. It might contain errors or oversights.

The construct defined here is an extension and slight
modification of the original proposal, based on the ensuing
discussion (which unfortunately was mostly limited to
comp.lang.c++.moderated). Short recap: it is a *structured*
construct that aims to help the programmer ensure that a
piece of code either succeeds or else throws. It does this
by making success explicit and failure implicit, and by
removing the need for nested try-catch'es for fallbacks.

But first, reiteration:

>META DISCUSSION ISSUES
>
>In contrast to the earlier [comp.lang.c++.moderated]


>idea this proposal involves new keywords.
>
>To avoid this thread degenerating into a debate about
>new keywords (i.e., how difficult the keyword problem
>is to solve), I suggest
>
>
> * Please start a new thread if you feel like tackling
> the topic of how to (not) introduce new keywords.
>
> * In this thread, pro- and counter-arguments ASSUME
> some solution to the problem of new keywords.
>
>
>In other words, much like discussing spacesuit design
>before anyone had entered space -- but in the hope or
>assurance that that little practical problem ;-) would
>be conquered.

=====================================================================
1 SYNTAX

In the following modified BNF "+" denotes a line continuation.
Quoted text denotes literals. The special word "nothing",
unquoted, denotes nothing (used to indicate optional things).

succeed_or_throw_block:
succeed_or_throw_clause +
(fallback_clauses | nothing) +
(cleanup_clause | nothing)

succeed_or_throw_clause:
"succeed_or_throw" compound_stmt

fallback_clauses:
fallback_clause | fallback_clauses fallback_clause

fallback_clause:
"fallback_to" (xinfo_object_decl | nothing) compound_stmt

cleanup_clause:
"clean_up" compound_stmt

xinfo_object_decl:
"(" xinfo_class_name xinfo_name ")"

xinfo_class_name:
identifier // Name of class with certain properties.

xinfo_name:
identifier // Variable scoped in following compound_stmt.

succeed_stmt:
"succeed" (return_stmt | ";")

(The succeed_stmt sans embedded return_stmt transfers control to
the point directly after the entire succeed_or_throw_block.
With an embedded return_stmt the succeed_stmt transfers control
out of the function. The semantics are then as return_stmt.)

2 NEAR-SYNTACTICAL CONSTRAINTS

Constraints that conceptually are at the syntactical level, but
which cannot be (efficiently) expressed in e.g. BNF:


c1 -- Placement of succeed_stmt.
A succeed_stmt is only valid within a succeed_or_throw_clause and
within a fallback_clause. It is not valid within a cleanup_clause.
In particular, it is not valid outside a succeed_or_throw_block.

c2 -- Obligatory succeed_stmt.
Every succeed_or_throw_clause and fallback_clause must contain at
least one potentially reachable succeed_stmt.

c3 -- Validity of return_stmt in succeed_stmt.
A succeed_stmt with an embedded return_stmt is invalid if the
embedded return_stmt is invalid. For example, in non-void
functions other than main an embedded return statement must
specify a return value compatible with the function's type.

c4 -- Form of xinfo_class_name.
This can be a direct or a qualified class name.

c5 -- Availability requirement for xinfo_class_name.
There must exist an available constructor, an available
destructor and some type T such that declaring a variable
of type xinfo_class_name at the point immediately before
the succeed_or_throw_block, using one constructor argument
of type T, is valid.

c6 -- Non-throwing requirement for xinfo_class_name.
The destructor must be non-throwing. Constructors may
throw. However, with a possibly throwing constructor the
class isn't much worth as an exception-info class.

c7 -- Transfer-of-control limitations.
The only explicit statements that are allowed to transfer
control out of or into a clause in a succeed_or_throw_block
are (1) succeed_stmt and (2) any throw statement, including
pure "throw;". In particular, the statements "break",
"continue" and "goto" are not allowed to transfer control
out of or (for "goto") into any clause of the construct.
Direct use of the C "longjmp" function is also disallowed
within a succeed_or_throw_block.

3 SEMANTICS -- BY EQUIVALENCE TO CURRENT C++.

In the following equivalence specifications the equivalent
code for some syntactical entity only shows how a compiler
might implement the construct. The constraints listed in
section 2 applies so that, e.g., the equivalent code for
succeed_stmt (below) is forbidden as actual user code.
Also, this equivalence scheme doesn't imply that an actual
implementation would have to work like this; it only has
to provide the equivalent functionality.


3.1 "succeed_stmt" equivalence.

Let success_label be an imaginary label placed directly
after the succeed_or_throw_block. Then:

Form 1:
"succeed;"
Equivalence:
"goto" success_label ";"

Form 2:
"succeed" return_stmt
Equivalence:
return_stmt

There are two reasons for requiring use of the word "succeed"
for a simple function return in this context. First, to
make success explicit; that you can only pass control out of
the construct in the case of success or failure (which must
use an exception). Second, to make it easier to check the
requirement that every succeed_or_throw_clause and every
fallback_clause must contain at least one potentially
reachable succeed_stmt.


3.2 "succeed_or_throw_clause" equivalence.

Let Form 1 be a succeed_or_throw_clause that isn't followed
by any fallback_clause(s) or cleanup_clause.

Let Form 2 be the case where the succeed_or_throw_clause is
followed by one or more fallback_clause(s) and/or a
cleanup_clause. In this case, let TheRest denote the
following fallback_clause(s) and/or cleanup_clause.

Differentiating between the two forms is only necessary in
order to avoid mostly unreachable rethrows everywhere.
The equivalence for Form 2 could handle it all, but then
a "throw;" would have to be added before the final "}".
And since the earlier debate indicated that this could be
difficult to understand I've tried to avoid obviously
unreachable code here -- for good or for worse...

Form 1:
"succeed_or_throw"
compound_stmt
Equivalence:
"{"
compound_stmt
"throw std::exception();"
"}"

Form 2:
"succeed_or_throw"
compound_stmt
TheRest
Equivalence:
"try {"
compound_stmt
"throw std::exception();"
"} catch(...) {"
$EquivalenceCodeFor<<TheRest>>
"}"

The line "$EquivalenceCodeFor<<TheRest>>" denotes exactly
what you might think, namely equivalence code for TheRest.
The $ sign is used to make this notation distinct from
C++ code. Ditto for << and >>.

Note, for the following equivalence of fallback_clause and
cleanup_clause, that in the equivalence they're always inside
a generic "catch(...)", and so can rethrow the exception.


3.3 "fallback_clause" equivalence.

Let TheRest denote the fallback_clause(s) and/or cleanup_clause
that directly follows the given fallback_clause. In the forms
where TheRest exists it cannot be empty. As before this
distinction is simply to avoid unfruitful debate about
unreachable code and so on; the equivalence scheme would (I think)
be far more elegant without such a distinction.

For retrieval of exception information, let XInfoObject be an
on-the-fly generated function with a unique name,

static xinfo_class_name* XInfoObject()
{
try{
throw;
} catch( T1 const& x ) {
return new xinfo_class_name( x );
} catch( T2 const& x ) {
return new xinfo_class_name( x );

// etc.

} catch( ... ) {
struct NoType{};
return new xinfo_class_name( NoType() );
}
}

where T1, T2 etc. correspond to the types the constructors of
the user-supplied xinfo_class_name type can take as single
arguments, and the "..." catch is only generated when a "..."
constructor exists. The effect of this function is to either
return an exception info object, or else rethrow the exception.

XInfoObject() is for exposition only; a compiler would *not*
need to dynamically allocate the xinfo_class_name object.

Nor would a compiler need to use std::auto_ptr, used below for
exposition so that anyone who wants can try this out.


Form 1:
"fallback_to"
compound_stmt
Equivalence:
"{"
compound_stmt
"throw;"
"}"

Form 1.1:
"fallback_to(" xinfo_class_name xinfo_name ")"
compound_stmt
Equivalence:
"{"
std::auto_ptr<xinfo_class_name> __pInfo( XInfoObject() );
xinfo_class_name& x_info_name = *__pInfo.get();

compound_stmt
"throw;"
"}"

Form 2:
"fallback_to"
compound_stmt
TheRest
Equivalence:
"try {"
compound_stmt
"throw;"
"} catch( ... ) {"
$EquivalenceCodeFor<<TheRest>>
"}"

Form 2.1:
"fallback_to(" xinfo_class_name xinfo_name ")"
compound_stmt
TheRest
Equivalence:
"try {"
std::auto_ptr<xinfo_class_name> __pInfo( XInfoObject() );
xinfo_class_name& x_info_name = *__pInfo.get();

compound_stmt
"throw;"
"} catch( ... ) {"
$EquivalenceCodeFor<<TheRest>>
"}"

3.4 "cleanup_clause" equivalence.

Form 1:
"clean_up"
compound_stmt
Equivalence:
"{"
compound_stmt
"throw;"
"}"


3.5 Practical difference between fallback and cleanup.

In the equivalence scheme form 1 of "fallback_clause" is identical
to the only form of "cleanup_clause".

However, in a fallback clause the compiler enforces that there must
be at least one potentially reachable succeed_stmt, so a fallback
clause will ideally not throw -- it only throws on failure of
the contained code.

In a cleanup clause the compiler enforces that there is no
succeed_stmt at all, and so this clause will always end with
throwing an exception -- whether via some explicit throw statement,
or by implicitly rethrowing the caught exception.
=====================================================================


Additional point, not mentioned so far: what about succeed-or-throw
corresponding to a function try-block?


Cheers,

- Alf


(I have no peer review of this before posting, so errors may remain...)

---

Alexander Terekhov

unread,
Sep 18, 2002, 3:38:18 PM9/18/02
to

"Alf P. Steinbach" wrote:
[...]

> <read>
>
> RAII and try-catch (or the new construct) are complementary.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Uhmm, do you mean >>try-SOFT-"catch"<< (the new construct)
the one that does NOT catch UNEXPECTED exceptions and that
DOES "rethrow" *automatically* (invoking terminate() if
"cleanup" handler would try to throw something else...
suppressing the original exception being propagated)? ;-)

regards,
alexander.

---

Alf P. Steinbach

unread,
Sep 19, 2002, 8:45:20 AM9/19/02
to
On Wed, 18 Sep 2002 19:38:18 +0000 (UTC), tere...@web.de (Alexander Terekhov)
wrote:

>
>"Alf P. Steinbach" wrote:
>[...]
>> <read>
>>
>> RAII and try-catch (or the new construct) are complementary.
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
>Uhmm, do you mean >>try-SOFT-"catch"<< (the new construct)
>the one that does NOT catch UNEXPECTED exceptions and that
>DOES "rethrow" *automatically* (invoking terminate() if
>"cleanup" handler would try to throw something else...
>suppressing the original exception being propagated)? ;-)

Nope, I've never heard about that before now. Sounds awful.

Cheers,

- Alf

Allan W

unread,
Sep 19, 2002, 9:23:09 AM9/19/02
to
alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote
> 1 META DISCUSSION ISSUES
>
> In contrast to the earlier idea this proposal involves
> new keywords.

Surely the next version of the C++ standard will have at least one
new keyword anyway. We try to minimize the number of new keywords
and the impact on new code; that's different than trying to avoid
new keywords at all costs!

> succeed_or_throw
> succeed
> fallback_to
>
> Note: I'm sure others can come up with better names.

Without commenting on the rest of your proposal (I feel unqualified
to debate the merits), I'd just like to comment on the specific
keywords chosen.

If we must use new keywords, we want to minimize the impact. Consider
the way that the keyword "mutable" was chosen. An English dictionary
would tell you that the word's meaning was very close to what was meant,
but as a native English speaker I can assure you that it was not used
very often in casual conversation. It's rather an obscure word.

That was intentional, I'm sure.

The word "modifiable" is synonymous, and could have been chosen instead
of "mutable." But the word "modifiable" would have more likely to be
used in legacy code; the obscure word was less likely. This makes the
word "mutable" the superior choice.

Getting back to your proposal, Alf, I have no problems with
either "succeed_or_throw" or "fallback_to" as keywords -- these short
phrases are unlikely candidates for good variable names.

However, the name "succeed" is different. I think that there's a very
good chance that someone is using this as a variable name right now.
If it turns out that your proposal is accepted (in some form), the
word "succeed" would be a poor choice. I would nominate any of these:
succeed_now
code_succeeded
fallback_not_needed
succeed_after_throw
succeed_or_throw_finished
fallback_instead_of_rethrow
std::succeed()

I like some of these better than others, but I think that any of them
would be better than simply "succeed."
---


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Alexander Terekhov

unread,
Sep 19, 2002, 1:18:22 PM9/19/02
to

"Alf P. Steinbach" wrote:
[...]
> >Uhmm, do you mean >>try-SOFT-"catch"<< (the new construct)
> >the one that does NOT catch UNEXPECTED exceptions and that
> >DOES "rethrow" *automatically* (invoking terminate() if
> >"cleanup" handler would try to throw something else...
> >suppressing the original exception being propagated)? ;-)
>
> Nope, I've never heard about that before now. Sounds awful.

<Forward Inline>

-------- Original Message --------
Date: Thu, 29 Aug 2002 16:38:33 +0200
Newsgroups: comp.programming.threads
Subject: Re: pthread : nifty source code package

David Butenhof wrote:
[...]
> You're clearly wrong about efficiency, though. With any sort of REAL
> exception support try/catch is free (at runtime) unless an exception
> occurs;

Yes, but "const&" members (inside guard object) can also be "free",
please see below.

> whereas the guard object (if is has no other function) adds
> allocation, initialization, destruction, and deallocation overhead.

Maybe. Well, consider the following code:

template<class _FwdIt,
class _Tval> inline
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
_Nonscalar_ptr_iterator_tag)
{ // copy _Val throughout raw [_First, _Last), arbitrary type
_FwdIt _Next = _First;

_TRY_BEGIN
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
_CATCH_ALL
for (; _Next != _First; ++_Next)
_Destroy(&*_Next);
_RERAISE;
_CATCH_END
}

Now consider the following somewhat verbose "alternative":

template< class _FwdIt >
class _Uninit_fill_cleanup {
_FwdIt const& m_First;
_FwdIt const& m_Last;
_FwdIt m_Next;
public:

_Uninit_fill_cleanup( _FwdIt const& _First, _FwdIt const& _Last ) :
m_First( _First ), m_Last( _Last ), m_Next( _First ) {
}

~_Uninit_fill_cleanup() {
if ( m_First != m_Last ) // if ( std::unwinding(this) ) // exceptional path
for (; m_First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}

};

template< class _FwdIt, class _Tval >
inline void _Uninit_fill( _FwdIt _First, _FwdIt _Last, const _Tval& _Val ) {
_Uninit_fill_cleanup< _FwdIt > _Cleanup( _First, _Last );
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

Finally, consider this:

template< context > // see "10-o'clock-wish-list"
class _Uninit_fill_cleanup {
typeof( context._First ) m_Next;
public:

_Uninit_fill_cleanup() :
m_Next( context._First ) {
}

~_Uninit_fill_cleanup() {
if ( std::unwinding(this) ) // exceptional path
for (; context._First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}

};

template< class _FwdIt, class _Tval>
inline void _Uninit_fill( _FwdIt _First, _FwdIt _Last, const _Tval& _Val ) {
_Uninit_fill_cleanup< context > _Cleanup();
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

Am I really clearly wrong about efficiency here? To me, there
should be no difference whatsoever (using some smart compiler,
of course). The advantage is that using guard objects, nothing
"unknown"/"unexpected" is caught.

Well, I'll admit that I'll have no problems with something like

template<class _FwdIt,
class _Tval> inline
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
_Nonscalar_ptr_iterator_tag)
{ // copy _Val throughout raw [_First, _Last), arbitrary type
_FwdIt _Next = _First;
try {
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}
action_on_propagation_of(...) { /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */
/* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */
/* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */
for (; _Next != _First; ++_Next)
_Destroy(&*_Next);
}
}

too; probably. ;-)

regards,
alexander.

Alf P. Steinbach

unread,
Sep 19, 2002, 4:54:07 PM9/19/02
to
Follow-ups to the formal specification thread in [comp.std.c++], please.

(Assuming it passes moderation, of course.)


On 13 Sep 2002 19:59:36 GMT, alf_p_s...@yahoo.no.invalid (Alf P. Steinbach)
wrote:

This is now done.


>...


>The "catch(...)"es nag me because (1) I'm all too familiar with the
>MSVC compiler, which has non-standard default semantics for generic
>catch, and in addition has a bug that calls the destructor of an
>exception object twice when the exception is rethrown and caught
>within a catch, and (2) because low-level code then has no way of
>sort of "pushing" an exception right through the normal exception
>handling. But using a restricted catch, as is the usual with
>try-catch, would mean that some exceptions could, in theory, slip
>through the net.
>
>Do we want a net or do we want a brickwall, that's question #1.
>As described in the OP, generic catch, i.e. brickwall.

I decided to go for the OP idea, generic catches.

Reason: If the programmer wants a more limited catch this can be
accomplished by using an exception info object.

Temporary problem: folks who try out the equivalence scheme in
the specification, using MSVC 7.0, will find that the compiler
doesn't support the "advanced" (MS terminology) techniques...

>Do we want some default_exception as default, or rethrow as
>default, that's question #2.

The posted specification uses rethrow as default.

>There are probably more questions...

One that I forgot in the specification is ordering of exceptions
by specificity in the automatically generated set of catch clauses
for an exception info object.

This guarantee is also one additional advantage of the construct,
although I think most modern compilers will tell if a base class
is listed before a derived class in a set of catch clauses.

I'll probably post an amendment to the spec if someone (yes, that
would make my day!) points out the oversight.


Cheers,

- Alf
---


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Alf P. Steinbach

unread,
Sep 19, 2002, 4:54:26 PM9/19/02
to
On 18 Sep 02 07:08:02 GMT, Hyman Rosen <hyr...@mail.com> wrote:

>Alf P. Steinbach wrote:
>> Ah, IMHO your example (or rather, the misconception that it does the
>> same as the original) shows precisely why the language does need some
>> special construct!
>
>Your example did not show any use being made of exception information.

First, that was not the only problem with your code. See my answer
for 3 points about that. Where x-info was just part of one point,
which mainly concerned the ability to rethrow the caught exception
(not present in your code).

Second, as I wrote in the posting with the example equivalence,


<quote>

And please note: I just wrote this off the cuff, right here and
now, so it's not any kind of technical reference to what I meant,
more like the general idea, sans retrieval of exception info.

</quote>


I've now posted a formal spec -- or near-to formal -- to
std.comp.c++, and that specification includes exception retrieval.

Such retrieval is integral to the scheme -- see the original
posting -- and any equivalence that doesn't make room
for this aspect is not faithfully representing the construct.


>> From the general response, it appears that this is a design pattern
>with a customer of one.

Well, two! There was one posting supporting it.

But I've yet to see any good counter-argument. Most attempts at
arguing against the construct have been simple logical fallacies.
I don't think it's fair to judge a proposal by the number of
erronous statements that can be made about it.


> I don't expect we'll be seeing this in C++ any time soon.

Neither do I... But what saddens me isn't that, but the reasons why.

Cheers,

- Alf
---


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Bob Bell

unread,
Sep 19, 2002, 6:13:22 PM9/19/02
to
alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message
news:<3d84b345...@news.online.no>...

> >alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message
news:<3d8223a5....@news.online.no>...
> > > Many people use try-catch as a simple cleanup mechanism even for
> > > a single attempt at something.
> >
> >Many people don't know how to use exceptions.
>
> In my experience that is literally true. But the implication that
> try-catch is mainly used because of ignorance of some better way is
> IMO incorrect. What is your basis for that implication?

I think I can save us (and the moderators) a little time, and
summarize our differences here.

You think:

-- it's a good idea to use try blocks for resource release
-- exception transparency is difficult and error-prone with try blocks
-- so-called multiple-attempt functions are common enough and error
prone enough to warrant special attention

I think:

-- it's a good idea to use RAII for resource release
-- exception transparency is trivial using RAII
-- so-called multiple-attempt functions are extremely rare

Thus, you see a need for an explicit "succeed or throw" construct. I
don't. Single attempt functions using RAII already succeed or throw.
Multiple attempt functions are too rare to justify a language change.

Since most of our differences seem to revolve around what RAII is and
is not good for, I will also address these other points directly:

> > > RAII can alleviate some of that, but isn't a general solution.
> >
> >That's exactly what RAII is: a general solution to the problem of
> >cleanup. I'd be curious to hear why you think it's not.
>
> In a program without at least a top-level try-catch the first
> exception will terminate it. So RAII is not a general solution.

I never claimed RAII is a general solution for handling exceptions. My
claim was (and is) that RAII is a general solution for the problem of
ensuring that resources are released in the presence of exceptions. No
more, no less.

> There is also the cost, inconvenience and inefficiency of writing a
> cleanup class for every little small thing. I guess that was
> the main reason for the decision to use try-catch above. So
> RAII is not a general solution.

These RAII classes are very often reusable. Try blocks are not. Also,
when releasing resources using try blocks, you often end up repeating
the release code -- once in the catch handler before the rethrow, once
in the normal flow of the function. This duplication gains nothing,
but does increase the risk of error.

> And there is the small problem of exceptions in destructors, which
> can in some cases be (partially or completely) solved using try-
> catch, so RAII is not a general solution.

The "exceptions in destructors" problem is not a consequence of RAII
per se, but can be a consequence of misapplication of RAII.

> And there are many other ways that RAII falls short of being
> a general solution, e.g. the cleanup where an exception must
> be translated to a return value -- which is not uncommon --
> so RAII is not a general solution.

Don't mix responsibilites. Just because you need a try block to
translate an exception to an error code doesn't mean you need to
release resources in the try block's catch handlers. Example:

int F(void)
{
try {
std::auto_ptr<Bar> p1(new Bar());
std::auto_ptr<Baz> p2(new Baz());

// etc.
}
catch (...) {
return -1;
}
return 0;
}

vs.

int F(void)
{
Bar* p1 = 0;
Baz* p2 = 0;

try {
p1 = new Bar();
p2 = new Baz();

// etc.

delete p1;
delete p2;
}
catch (...) {
delete p1;
delete p2;

return -1;
}
return 0;
}

The first version looks a lot simpler to me.

Oh, yeah, one last thing. I wrote:

> >Many people don't know how to use exceptions.

> >I'd rather educate them than change the language to suit their
> >faulty expertise.

You wrote:

> Now P.J.Plauger here uses a style that's well suited to
> shortening template code, a style some people disagree with,
> but you think his expertise is faulty? That you should
> educate him?

For the record, I wouldn't presume to teach P.J. Plaugher anything. My
claim about "faulty expertise" was about programmers who

1) use exceptions incorrectly/inappropriately
2) get bitten by bugs as a result, then
3) want the language changed to help them avoid creating those bugs
that were a result of their faulty expertise in the first place

I don't put Mr. Plaugher in that category.

Bob
---


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Alf P. Steinbach

unread,
Sep 20, 2002, 8:01:31 AM9/20/02
to
On Thu, 19 Sep 2002 17:18:22 +0000 (UTC), tere...@web.de (Alexander Terekhov)
wrote:

>


>"Alf P. Steinbach" wrote:
>[...]
>> >Uhmm, do you mean >>try-SOFT-"catch"<< (the new construct)
>> >the one that does NOT catch UNEXPECTED exceptions and that
>> >DOES "rethrow" *automatically* (invoking terminate() if
>> >"cleanup" handler would try to throw something else...
>> >suppressing the original exception being propagated)? ;-)
>>
>> Nope, I've never heard about that before now. Sounds awful.
>
><Forward Inline>
>
>-------- Original Message --------
>Date: Thu, 29 Aug 2002 16:38:33 +0200
>Newsgroups: comp.programming.threads
>Subject: Re: pthread : nifty source code package

I had to look up that thread to get your meaning. In short,
but correct me if I'm wrong, what you are referring to is the
problem of thread cancellation via exceptions, where

* If catch(...) catches the thread cancellation exception, then
code using catch(...) may inadvertently "swallow" a thread
cancellation.

* Otoh., if catch(...) doesn't catch the thread cancellation
exception, then much of current code, including standard
library code, that uses catch(...), is not thread cancellation
safe.

This seems to be a catch 22.

Then we add to the soup,


* If the code only uses RAII then the first exception of any
kind must terminate the thread.


As I see it, discussing this in the absence of threading support in
C++ is probably unfruitful. It is a thread support integration issue.
As such, it doesn't impact on the proposed exception handling
construct, which simply uses whatever semantics the implementation
provides for catch(...) -- and helps to ensure rethrows.

Cheers,

- Alf

Alf P. Steinbach

unread,
Sep 20, 2002, 8:03:04 AM9/20/02
to
This is an amendment to the original formal specification, concerning:


* Vague description: of XInfoObject function.

It was not 100% clear that the equivalence scheme required one
such function per user-supplied exception-info class.


* Oversight: specificity ordering within XInfoObject function.

I forgot to state the requirement of specificity ordering for
the automatically generated catch-clauses.


Changes as follows:


======================================================================
On Wed, 18 Sep 2002 19:36:01 +0000 (UTC), alf_p_s...@yahoo.no.invalid (Alf P.
Steinbach) wrote:

>3.3 "fallback_clause" equivalence.
>
>...


>For retrieval of exception information, let XInfoObject be an
>on-the-fly generated function with a unique name,
>
> static xinfo_class_name* XInfoObject()

> ...

<replacement-text>
For retrieval of exception information in an object of class
XInfo, let XInfoObject<XInfo> be a generated function,

template<>
XInfo* XInfoObject<XInfo>()


{
try{
throw;
} catch( T1 const& x ) {

return new XInfo( x );


} catch( T2 const& x ) {

return new XInfo( x );

// etc.

} catch( ... ) {
struct NoType{};

return new XInfo( NoType() );
}
}

</replacement-text>

>where T1, T2 etc. correspond to the types the constructors of
>the user-supplied xinfo_class_name type can take as single
>arguments, and the "..." catch is only generated when a "..."
>constructor exists. The effect of this function is to either
>return an exception info object, or else rethrow the exception.

<replacement-text>


The effect of this function is to either return an exception info
object, or else rethrow the exception.

The types T1, T2 etc. correspond to the types the constructors of
the user-supplied XInfo class can take as single arguments (this
ensures that they must be distinct types). For any pair of such
types Ta and Tb, if Ta is a visible base class of Tb, then Tb (the
most specific class) must appear before Ta in the list of catch
clauses. Visibility means that at the point of call to XInfoObject,
given a hypotethical pointer pb to a Tb object, the expression
dynamic_cast<Ta const*>(pb) would be non-null.

The "..." catch is only generated when a "..." constructor exists.
</replacement-text>
======================================================================


Base-class first in catch-clauses is one more "inadvertent error"
that this construct helps programmers to avoid, simply by automating
the task -- and avoiding redundancy (the same code in different
catch-clauses) to boot.

I think the construct must be a good idea from Platon-land: much more
nice properties have emerged than I tried to put in in the first place.


Cheers,

- Alf

Alf P. Steinbach

unread,
Sep 21, 2002, 10:23:57 AM9/21/02
to
General comment. I'm reminded of old Pascal, which had *reserved words*
and *standard words*, the former corresponding to C++ keywords, while
the latter could be freely redefined by the programmer (losing the built-in
meaning in the context of the redefinition).

Or if it wasn't Pascal then it was some other language in that family, e.g.
Modula-2 -- whatever.

Perhaps something like that distinction is needed for C++0x.


On 19 Sep 2002 09:23:09 -0400, All...@my-dejanews.com (Allan W) wrote:

>...


>Getting back to your proposal, Alf, I have no problems with
>either "succeed_or_throw" or "fallback_to" as keywords -- these short
>phrases are unlikely candidates for good variable names.
>
>However, the name "succeed" is different. I think that there's a very
>good chance that someone is using this as a variable name right now.
>If it turns out that your proposal is accepted (in some form), the
>word "succeed" would be a poor choice. I would nominate any of these:

> succeed_now

[+]
Much better than my suggestion, yes.

[?]
Or perhaps "succeed_by_continuing" and "succeed_by" (which would be
followed by a return-statement), instead of a single keyword "succeed".
It would bring the number of keywords, including "clean_up" (which
probably also needs adjustment!) to 5, and that's a more lucky number
than 4 or 3... ;-)


> code_succeeded

[-]
I think this may be used by current code, choosing names influenced by
the early idea of condition codes.


> fallback_not_needed

[-]
While true enough, it is an incidental issue that no further fallback
is needed -- IMHO the wrong signal to give a reader of the code.

> succeed_after_throw

[-]
A succeed_or_throw must have at least one potentially reachable succeed,
for which there will usually not be any caught exception.


> succeed_or_throw_finished

[+]
Unlikely to conflict with existing code, expresses the right thing.

[-]
Not concise. Devil's advocate: may be misread as "throw finished()".


> fallback_instead_of_rethrow

[-]
Expresses the wrong thing; succeed does not cause a fallback (it does
the opposite, succeeds instead of invoking the fallback code).


> std::succeed()

[+]
Unlikely to conflict with current code if that "std::" cannot be
implicit.

[-]
Rule about non-implicit "std" is inconsistent with current
qualification rules for namespaces.

[?]
Perhaps some other standardized namespace, e.g. as Glassbarrow, or was
it Potter, suggested, "kwd" (I'm not sure of the exact phrase).

>I like some of these better than others, but I think that any of them
>would be better than simply "succeed."

Yes, I think you do have a point, and it was nagging me. Thanks.

Cheers,

- Alf

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]


[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Alf P. Steinbach

unread,
Sep 21, 2002, 10:25:17 AM9/21/02
to
On 19 Sep 2002 09:23:09 -0400, All...@my-dejanews.com (Allan W) wrote:
>...

I've replied to this in a new thread "Keywords for proposed exception
handling construct"; I hope that's OK.

Cheers,

- Alf

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]


[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Alf P. Steinbach

unread,
Sep 21, 2002, 2:13:06 PM9/21/02
to
This is a second amendment to the original formal specification, concerning:


* Oversight: closed door for solution of nested exception problem.

I forgot to limit exception info classes to constructors of one
argument only (except "..."), so that a later extension to handle
exceptions during stack unwinding isn't prohibited (if such an
extension were to rely on more than one constructor argument).


* Oversight: templated constructor not meaningful for exception info.

I forgot to explicitly disallow one-argument constructors with
templated argument type, although it's obviousน, and I stated this
xinfo class requirement in the comp.lang.c++.moderated discussion.


Changes as follows:


======================================================================
On Wed, 18 Sep 2002 19:36:01 +0000 (UTC), alf_p_s...@yahoo.no.invalid (Alf P.
Steinbach) wrote:

>...

>2 NEAR-SYNTACTICAL CONSTRAINTS
>
> ...


>
> c5 -- Availability requirement for xinfo_class_name.
> There must exist an available constructor, an available
> destructor and some type T such that declaring a variable
> of type xinfo_class_name at the point immediately before
> the succeed_or_throw_block, using one constructor argument
> of type T, is valid.

<replacement-text>

c5.A -- Availability requirement for xinfo_class_name.
There must exist an available non-templated constructor, an


available destructor and some type T such that declaring a
variable of type xinfo_class_name at the point immediately
before the succeed_or_throw_block, using one constructor
argumentof type T, is valid.

</replacement-text>


<inserted-new-text>

c5.B -- Single arg requirement for xinfo_class_name.
In addition to the one-argument constructors in c5.A the
xinfo_class_name class may have an available default constructor
that has no defaulted arguments, and it may have an available
generic constructor of the form "xinfo_class_name( ... )". It
may not have any other available constructor that takes two or
more arguments.

</inserted-new-text>
======================================================================


Renumbering of the text will have to have to wait...


Cheers,

- Alf


น) Perhaps it's not so obvious, so if not, here's an explanation. In
current C++ template type arguments are unrestricted, so with an argument
arg of template type T one might write, e.g., "arg.singalong()" with no
complaint from the compiler. In order to match an exception object to T
the compiler would have to check that "singalong()" etc. were supported,
and generate the constructor and any other called templated functions
on-the-fly, at run-time, which just isn't on. Although invoking the
compiler at run-time and doing a full compilation might do the trick...

Alf P. Steinbach

unread,
Sep 24, 2002, 12:36:03 PM9/24/02
to
{Note to moderators snipped -mod/fwg}

On 19 Sep 2002 18:13:22 -0400, bel...@pacbell.net (Bob Bell) wrote:

>alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message
>news:<3d84b345...@news.online.no>...
>> >alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message
>news:<3d8223a5....@news.online.no>...
>> > > Many people use try-catch as a simple cleanup mechanism even for
>> > > a single attempt at something.
>> >
>> >Many people don't know how to use exceptions.
>>
>> In my experience that is literally true. But the implication that
>> try-catch is mainly used because of ignorance of some better way is
>> IMO incorrect. What is your basis for that implication?
>
>I think I can save us (and the moderators) a little time, and
>summarize our differences here.

Ok, you choose to not answer, but I won't hark on it. Let's just note
for the record that there is no basis for implying that people who use
try-catch for cleanup of a single-attempt don't necessarily know how to
use exceptions. So that comment was out-of-line, as I showed by example
code from an implementation of the standard library, written by one of
the best experts around.

>You think:
>
>-- it's a good idea to use try blocks for resource release

False. I don't think that.

I don't think it's always a bad idea, either.

Since this is the second time I've stated that explicitly (in the
previous posting even directing your attention to that statement,
both at the very top of the posting and at the very end), please
quote the text where you think I've said or implied otherwise.

>-- exception transparency is difficult and error-prone with try blocks

False.

True, if you substitute "can be" for "is".

The "can be" is important and common enough, though.


>-- so-called multiple-attempt functions are common enough and error
>prone enough to warrant special attention

Literally true.

Reiteration: if there is no try-catch in a program, then that program
crashes on the first exception.

Multiple attempt functions are logically *necessary* for robustness
in a system relying on exceptions -- no way out of that, AFAICS.

The implication that this is the only point of merit for the proposed
exception handling construct, however, is false.

I refer to the original posting's section 4 for some advantages of the
new construct, and in addition note (although not of high importance)
that when using this construct the error of placing a base class
before a derived class in a set of catch clause cannot happen.

>I think:
>
>-- it's a good idea to use RAII for resource release
>-- exception transparency is trivial using RAII
>-- so-called multiple-attempt functions are extremely rare
>
>Thus, you see a need for an explicit "succeed or throw" construct. I
>don't.

I must again refer you to the original posting. The points listed
above all focus down on multiple-attempt functions. Important, but
by no means the only important advantage of the construct.

From your list of alleged viewpoints it seems that you have only
opinions about RAII, whereas I have only opinions about try-catch,
which is not the case.

Could I therefore ask for a clarification on your views about
try-catch, conspiciously absent from the list above?

>Single attempt functions using RAII already succeed or throw.

You're preaching to the choir, here. Neither I nor, I suspect, any
other reader of this thread or group, needs convincing that RAII
should be used whenever it is applicable and the cost is not
prohibitive. In fact, I stated that most recently in the previous
posting, the one you're replying to.

On the other hand, the implication that RAII is sufficient, not
just in theory but in practice, for writing all single-attempt
functions, is false.

For evidence that the implication is false, see e.g. the code of
your standard library implementation, where most likely you'll
find quite a few cases of try-catch used for single-attempt
function cleanup (concrete example in my previous posting).

>Multiple attempt functions are too rare to justify a language change.

Well, they are not very rare, and cannot logically be very rare in
robust code (except in trivially small systems with a single
top-level try-catch).

Secondly, they're only part of the justification for the new construct.

Third, they are nevertheless important, precisely because they
constitute the choke points for robustness wrt. exceptions, assuming
the rest of the code cleans up correctly and passes on exceptions.

I again refer you to the original posting for advantages that
don't have to do with multiple attempt functions.


>Since most of our differences seem to revolve around what RAII is and
>is not good for

False. Our differences revolve, IMHO, around the proposed exception
handling construct, where I suspect that RAII is much of strawman-
argument. But then that isn't a very fruitful line of discussion,
so please read the paragraph below that starts with "Perhaps".

What you have *literally* written about RAII hasn't been much off the
common consensus about that method; only the implication that RAII
should always be used, to the exclusion of try-catch, is arguable, and
then, being a pure implication (of e.g. "some people don't know how to
use exceptions" and "their faulty expertise") it's extremely difficult to
argue against.

Perhaps you could spell out in clear, no "some people", "faulty expertise"
or such, exactly what your position is wrt. use of RAII versus try-catch.
Note that I have done that, twice. It's so much better for rational
discussion to have some firm idea about each other's actual position.

> I will also address these other points directly:
>

<reinserted context>


>> > > Many people use try-catch as a simple cleanup mechanism even for
>> > > a single attempt at something.

</reinserted context>


>> > > RAII can alleviate some of that, but isn't a general solution.
>> >
>> >That's exactly what RAII is: a general solution to the problem of
>> >cleanup. I'd be curious to hear why you think it's not.
>>
>> In a program without at least a top-level try-catch the first
>> exception will terminate it. So RAII is not a general solution.
>
>I never claimed RAII is a general solution for handling exceptions. My
>claim was (and is) that RAII is a general solution for the problem of
>ensuring that resources are released in the presence of exceptions. No
>more, no less.

In theory, and in theory so is try-catch, but that's academic: we don't
want to always use try-catch, to the exclusion of RAII, and many of us,
including, as I showed, top-level experts, don't want to always use RAII
to the exclusion of try-catch.

If the comment was only about theoretic "power" to handle cleanup,
I fail to see the relevance to anything; it applies as well to try-catch.

It was therefore natural to assume something more substantial was
implied, and I apologize for making that error.

>> There is also the cost, inconvenience and inefficiency of writing a
>> cleanup class for every little small thing. I guess that was
>> the main reason for the decision to use try-catch above. So
>> RAII is not a general solution.
>
>These RAII classes are very often reusable.

If "very often" is changed to "sometimes", then I agree with that.

In the cases where reusable cleanup classes can be used the cost
of using them drops to near zero, and so in those cases, IMHO, RAII
should be used.

But I've stated that before, e.g. both here and in the previous
posting.

>Try blocks are not [reusable].

That depends on one's definition of "reusable". Although I disagree
with the statement, I don't think the fact that try-blocks can be
reusable is an argument in favor of them. Quite the opposite;
generally I abhor redundancy in code, and I think, so do you.


>Also, when releasing resources using try blocks, you often end up
>repeating the release code -- once in the catch handler before the
>rethrow, once in the normal flow of the function. This duplication
>gains nothing, but does increase the risk of error.

True. In such situations try-catch is strongly counter-indicated.

>> And there is the small problem of exceptions in destructors, which
>> can in some cases be (partially or completely) solved using try-
>> catch, so RAII is not a general solution.
>
>The "exceptions in destructors" problem is not a consequence of RAII
>per se, but can be a consequence of misapplication of RAII.

I think we actually agree on this point.

>> And there are many other ways that RAII falls short of being
>> a general solution, e.g. the cleanup where an exception must
>> be translated to a return value -- which is not uncommon --
>> so RAII is not a general solution.
>
>Don't mix responsibilites.

Good advice, in general. I've always said that to my students.

>Just because you need a try block to translate an exception to an
>error code doesn't mean you need to release resources in the try
>block's catch handlers.

That's also good advice, but perhaps, instead of trying to imply
something about me, you're arguing from a genuine misconception that
I generally avoid RAII.

On the contrary, I've stated my position on that, explicitly,
several times, both in this posting and the previous one.

To reiterate, once again, I think RAII should be used whenever it's
applicable and the cost is not prohibitive.

Again, you're preaching to the choir.

Except -- I think we should not be afraid to commit ourselves,
to say that something *is* simpler.

As I wrote in the posting you're replying to, I think RAII should
be used whenever it's applicable and the cost is not prohibitive.

>Oh, yeah, one last thing. I wrote:

<reinserted context>


>> > Many people use try-catch as a simple cleanup mechanism even for
>> > a single attempt at something.

</reinserted context>

>> >Many people don't know how to use exceptions.
>> >I'd rather educate them than change the language to suit their
>> >faulty expertise.
>
>You wrote:
>
>> Now P.J.Plauger here uses a style that's well suited to
>> shortening template code, a style some people disagree with,
>> but you think his expertise is faulty? That you should
>> educate him?
>
>For the record, I wouldn't presume to teach P.J. Plaugher anything.

Unfortunately, what follows compels me to not let this go.

The people who use or have used try-catch as a simple cleanup
mechanism even for a single attempt at something, include, as I showed
by actual code, PJP, one of our finest top-level experts.

So if you were excluding the top-level experts, or just PJP in
particular, what exactly was the point of your statement?


>My claim about "faulty expertise" was about programmers who
>
>1) use exceptions incorrectly/inappropriately
>2) get bitten by bugs as a result, then
>3) want the language changed to help them avoid creating those bugs
>that were a result of their faulty expertise in the first place

Ah, I think I see!

I'll leave it to others to draw the inference.

Sincerely,

- Alf

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]


[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]

[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Bob Bell

unread,
Sep 27, 2002, 6:36:32 AM9/27/02
to
After reading your entire post, I've come to the conclusion that
you're taking my remarks too personally. No offense was intended, nor
should one be inferred. I am not in this discussion to insult you, win
points or anything like that. You suggested a language proposal, and I
have some serious questions about it.

I also didn't intend to dodge any questions, if that's what you
thought. I was simply trying to "cut to the chase." However, my
attempt at streamlining apparently failed, so in this post I will
attempt to answer every point.

> >> > > Many people use try-catch as a simple cleanup mechanism even for
> >> > > a single attempt at something.
> >> >
> >> >Many people don't know how to use exceptions.
> >>
> >> In my experience that is literally true. But the implication that
> >> try-catch is mainly used because of ignorance of some better way is
> >> IMO incorrect. What is your basis for that implication?
> >
> >I think I can save us (and the moderators) a little time, and
> >summarize our differences here.
>
> Ok, you choose to not answer, but I won't hark on it. Let's just note
> for the record that there is no basis for implying that people who use
> try-catch for cleanup of a single-attempt don't necessarily know how to
> use exceptions. So that comment was out-of-line, as I showed by example
> code from an implementation of the standard library, written by one of
> the best experts around.

I agree. There is no basis for implying that people who use try-catch
for clean up don't necessarily know how to use exceptions. But I
didn't make that implication, so I don't feel compelled to defend it.

However, I will say that most C++ programmers I have had experience
with who use try-catch _only_ to release resources don't know how to
use exceptions very well; when shown that there is an alternative
(RAII), most have agreed that RAII is a superior choice.

Does that mean that I think that _all_ programmers who use try-catch
for clean up are ignorant, as you suggest? No.

> >You think:
> >
> >-- it's a good idea to use try blocks for resource release
>
> False. I don't think that.
>
> I don't think it's always a bad idea, either.
>
> Since this is the second time I've stated that explicitly (in the
> previous posting even directing your attention to that statement,
> both at the very top of the posting and at the very end), please
> quote the text where you think I've said or implied otherwise.

On September 14, you wrote:

> Many people use try-catch as a simple cleanup mechanism even for

> a single attempt at something. RAII can alleviate some of that,


> but isn't a general solution.

You then argued vigorously on September 18 that RAII is not a general
solution to the problem of clean up. Given that there are only two
alternatives for clean up, and you argued so strongly against RAII, I
concluded that you prefer the alternative, which is manual clean up in
a catch block.

> >-- exception transparency is difficult and error-prone with try blocks
>
> False.
>
> True, if you substitute "can be" for "is".
>
> The "can be" is important and common enough, though.

It sounds like I was close enough on this one. :-)

> >-- so-called multiple-attempt functions are common enough and error
> >prone enough to warrant special attention
>
> Literally true.
>
> Reiteration: if there is no try-catch in a program, then that program
> crashes on the first exception.
>
> Multiple attempt functions are logically *necessary* for robustness
> in a system relying on exceptions -- no way out of that, AFAICS.

You've made this statement before and I'm afraid I don't understand
it. I can easily imagine robust programs that don't use
multiple-attempt functions.

Here is a description of one. At the top level, main creates an
Application object and then calls its RunEventLoop function, which
looks like this:

void Application::RunEventLoop(void)
{
while (!mIsDone)
try {
GetAndHandleEvent();
}
catch (...) {
ReportErrorToUser();
}
}

This function is not multiple-attempt, as we have been discussing it.
GetAndHandleEvent, as well as every function it calls, certainly does
not need to be multiple-attempt either.

> The implication that this is the only point of merit for the proposed
> exception handling construct, however, is false.

The reason I focus on the multiple-attempt case is that support for
guaranteed exception transparency of multiple-attempt functions is the
only thing that I see the proposal giving us for which there is not
already an adequate solution.

> I refer to the original posting's section 4 for some advantages of the
> new construct, and in addition note (although not of high importance)
> that when using this construct the error of placing a base class
> before a derived class in a set of catch clause cannot happen.

None of those advantages seem (to me) to be compelling enough to
warrant a change to the language. My overall point in making these
posts is to give you an opportunity to convince me (and,
theoretically, others who may agree with me). So far, we're not there
yet.

> >I think:
> >
> >-- it's a good idea to use RAII for resource release
> >-- exception transparency is trivial using RAII
> >-- so-called multiple-attempt functions are extremely rare
> >
> >Thus, you see a need for an explicit "succeed or throw" construct. I
> >don't.
>
> I must again refer you to the original posting. The points listed
> above all focus down on multiple-attempt functions. Important, but
> by no means the only important advantage of the construct.

You should take it as a given that I have read the original posting
and that it did not convince me. Continually referring back to it will
not add anything new to the discussion.

> From your list of alleged viewpoints it seems that you have only
> opinions about RAII, whereas I have only opinions about try-catch,
> which is not the case.
>
> Could I therefore ask for a clarification on your views about

> try-catch, conspicuously absent from the list above?

Sure. Try-catch is used when you want to _stop_ an exception from
propagating through a function. It may be a permanent stop (as in a
"buck stops here" catch in main or in Application::RunEventLoop
above), or it may be temporary, where you're going to do some more
work and then rethrow. "More work" could be releasing resources,
translating the exception to a new type, extracting information from
the exception for display/logging purposes, trying a different
algorithm, etc.

There are very few times when I want to stop an exception for any
reason. For most functions, I want exceptions to pass through
unhindered. Therefore, there will be very few parts of my program that
need to use try-catch.

> >Single attempt functions using RAII already succeed or throw.
>
> You're preaching to the choir, here. Neither I nor, I suspect, any
> other reader of this thread or group, needs convincing that RAII
> should be used whenever it is applicable and the cost is not
> prohibitive. In fact, I stated that most recently in the previous
> posting, the one you're replying to.
>
> On the other hand, the implication that RAII is sufficient, not
> just in theory but in practice, for writing all single-attempt
> functions, is false.

I never said anything like, "RAII is sufficient for writing all
single-attempt functions." I did, however, say things like "RAII
should be preferred." If you don't want to use it, that's your choice.

In any case, since you say that RAII is not sufficient for writing
single-attempt functions, you should be able to show us at least one
single-attempt function for which RAII would not work. Can you do
that?

> For evidence that the implication is false, see e.g. the code of
> your standard library implementation, where most likely you'll
> find quite a few cases of try-catch used for single-attempt
> function cleanup (concrete example in my previous posting).

Not good enough. Just because you can find a piece of code which uses
try-catch instead of RAII for clean up does not mean that RAII
wouldn't also have worked. Also, if you can't tell me _why_ such code
uses try-catch, then you haven't really established that the code
supports your position. For example, the Dinkumware code you posted
uses macros rather than try-catch directly. Why is that? For all we
know, their code runs on environments where exceptions don't exist, or
maybe they do exist but have different semantics from standard C++.
That may have something to do with the reason RAII was not used.

Showing us this code example proves nothing. I just did a quick search
through my vendor's standard library and found 41 occurrences of
"goto." Would you apply the same logic and advocate the use of goto?

> >Multiple attempt functions are too rare to justify a language change.
>
> Well, they are not very rare, and cannot logically be very rare in
> robust code (except in trivially small systems with a single
> top-level try-catch).

Even if I accepted your "robust programs must have multiple-attempt
functions" argument, how many functions in the many thousands of
functions in a program need to be multiple-attempt? More than one
percent? Personally, I would be surprised if the number were more than
one tenth of a percent. Less than one tenth of one percent of the
functions in a program is what I mean by "too rare."

> Secondly, they're only part of the justification for the new construct.

True, but from my point of view, the multiple-attempt justification is
the best one in the proposal -- the other gains/advantages solve
problems that are already solved.

> Third, they are nevertheless important, precisely because they
> constitute the choke points for robustness wrt. exceptions, assuming
> the rest of the code cleans up correctly and passes on exceptions.

This is true.

> >Since most of our differences seem to revolve around what RAII is and
> >is not good for
>
> False. Our differences revolve, IMHO, around the proposed exception
> handling construct, where I suspect that RAII is much of strawman-
> argument. But then that isn't a very fruitful line of discussion,
> so please read the paragraph below that starts with "Perhaps".

The only reason RAII came into it is because I claimed that RAII
pretty much already solves the single-attempt case. You then spent a
large chunk of your previous post on September 18 attacking my
position on RAII. So it seemed to me like a large part of our
differences. If I'm wrong about that, I'm wrong.

> What you have *literally* written about RAII hasn't been much off the
> common consensus about that method; only the implication that RAII
> should always be used, to the exclusion of try-catch, is arguable, and
> then, being a pure implication (of e.g. "some people don't know how to
> use exceptions" and "their faulty expertise") it's extremely difficult to
> argue against.

If my remarks came off as attacking, I apologize. I did not mean to
imply, for instance, that _you_ don't know how to use exceptions, or
that your expertise is faulty. As I said before, I've had experience
with programmer using try-catch for resource clean up, and it usually
turned out that they didn't know what they were doing. If I hit a
nerve, I apologize.

> Perhaps you could spell out in clear, no "some people", "faulty expertise"
> or such, exactly what your position is wrt. use of RAII versus try-catch.
> Note that I have done that, twice. It's so much better for rational
> discussion to have some firm idea about each other's actual position.

OK:

Earlier I referred to try-catch as being used to stop an exception.
RAII, on the other hand, is used to take advantage of the guarantees
that C++ makes about destructor invocation to ensure that resources
are released. An object's destructor is called whenever its lifetime
ends. (Actually, to be pedantic, an object's lifetime ends when the
destructor is called, but the distinction isn't important to my
point.)

The lifetime of a local variable ends when control passes out of the
block in which it is defined, either normally or by way of a thrown
exception. The lifetime of a contained object ends when the containing
object's lifetime ends. The lifetime of static variables ends after
main exits. The lifetime of a dynamically allocated object ends when
its pointer is deleted.

In all but the last case, the lifetime management is automatically
handled by the language, and is therefore a detail the programmer is
freed from worrying about.

Whenever the lifetime of a dynamically allocated resource is in
alignment with one of the automatically managed cases, RAII is
indicated. Thus, RAII is appropriate for making sure resources
allocated in a function and held by local variables are released when
either the function returns normally or when an exception passes
through. RAII is also appropriate under the other cases, such as when
an object needs to hold, as a member variable, a resource that must be
released when the object is destroyed.

Now, about RAII vs. try-catch:

RAII should be preferred for resource clean up.

Consider: a try block that exists solely for resource clean up has
this general form:

void F(void)
{
// initialize resource variables to "NULL" here
try {
// allocate resources here

// work with resources here

// possibly deallocate resources here
}
catch (...) {
// deallocate resources here
throw;
}
}

Some functions using try blocks will deallocate the resources in two
places, once in the main line of the function ("possibly deallocate
resources here") and in the catch handler. This is almost always the
case when exception transparency and/or the strong exception safety
guarantee is desired. This duplication is bad, which I believe you
already agreed with.

If the function can avoid the deallocation in the main line, then the
function is allocating resources that must be deallocated by some
other function. For example, F could be a member function, the
resources could be held in member variables, and some other member
function could perform the deallocation.

However, there is still duplication of deallocation code: between the
deallocation in the catch and the deallocation in the second function.
Often, this duplication can be mitigated by factoring it into a third
function, which makes the situation better.

So here are the advantages of RAII over manual clean up using a catch
handler:

-- no duplication involved
-- less typing (no tries, catches, or throws)
-- less complex (don't have to remember to initialize everything to
NULL first, no duplication of deallocation, etc.)
-- RAII classes are often reusable
-- RAII classes are testable in isolation

> <reinserted context>
> >> > > Many people use try-catch as a simple cleanup mechanism even for
> >> > > a single attempt at something.
> </reinserted context>
> >> > > RAII can alleviate some of that, but isn't a general solution.
> >> >
> >> >That's exactly what RAII is: a general solution to the problem of
> >> >cleanup. I'd be curious to hear why you think it's not.
> >>
> >> In a program without at least a top-level try-catch the first
> >> exception will terminate it. So RAII is not a general solution.
> >
> >I never claimed RAII is a general solution for handling exceptions. My
> >claim was (and is) that RAII is a general solution for the problem of
> >ensuring that resources are released in the presence of exceptions. No
> >more, no less.
>
> In theory, and in theory so is try-catch, but that's academic: we don't
> want to always use try-catch, to the exclusion of RAII, and many of us,
> including, as I showed, top-level experts, don't want to always use RAII
> to the exclusion of try-catch.
>
> If the comment was only about theoretic "power" to handle cleanup,
> I fail to see the relevance to anything; it applies as well to try-catch.
>
> It was therefore natural to assume something more substantial was
> implied, and I apologize for making that error.

I don't quite understand this line of reasoning. You asserted that
RAII was not a general solution for clean up during a single attempt
at something. I contradicted you. I don't understand what try-catch
has to do with whether or not RAII is a general solution for clean up.

> >> There is also the cost, inconvenience and inefficiency of writing a
> >> cleanup class for every little small thing. I guess that was
> >> the main reason for the decision to use try-catch above. So
> >> RAII is not a general solution.
> >
> >These RAII classes are very often reusable.
>
> If "very often" is changed to "sometimes", then I agree with that.
>
> In the cases where reusable cleanup classes can be used the cost
> of using them drops to near zero, and so in those cases, IMHO, RAII
> should be used.
>
> But I've stated that before, e.g. both here and in the previous
> posting.

Can you give me an example of a function that performs clean up
operations that would never be needed anywhere else? In my experience,
such cases are nowhere near common.

> >Try blocks are not [reusable].
>
> That depends on one's definition of "reusable".

Can you give a definition of "reusable" that allows you to "reuse" a
try block? The only one I can think of is "can copy and paste the
code."

> Although I disagree
> with the statement, I don't think the fact that try-blocks can be
> reusable is an argument in favor of them. Quite the opposite;
> generally I abhor redundancy in code, and I think, so do you.

I don't get you. Are you saying try blocks increase needless
redundancy, or are you saying try blocks are reusable? Or something
else?

> >Also, when releasing resources using try blocks, you often end up
> >repeating the release code -- once in the catch handler before the
> >rethrow, once in the normal flow of the function. This duplication
> >gains nothing, but does increase the risk of error.
>
> True. In such situations try-catch is strongly counter-indicated.

Such situations are very common when trying to achieve exception
transparency. Therefore, it's very common for try-catch to be strongly
counter-indicated.

> >> And there is the small problem of exceptions in destructors, which
> >> can in some cases be (partially or completely) solved using try-
> >> catch, so RAII is not a general solution.
> >
> >The "exceptions in destructors" problem is not a consequence of RAII
> >per se, but can be a consequence of misapplication of RAII.
>
> I think we actually agree on this point.

I think so too. :-)

> >> And there are many other ways that RAII falls short of being
> >> a general solution, e.g. the cleanup where an exception must
> >> be translated to a return value -- which is not uncommon --
> >> so RAII is not a general solution.
> >
> >Don't mix responsibilites.
>
> Good advice, in general. I've always said that to my students.

More agreement. That's good.

> >Just because you need a try block to translate an exception to an
> >error code doesn't mean you need to release resources in the try
> >block's catch handlers.
>
> That's also good advice, but perhaps, instead of trying to imply
> something about me, you're arguing from a genuine misconception that
> I generally avoid RAII.

I'm not implying anything, just contradicting an earlier statement of
yours. You argued against RAII by saying that it doesn't work for "the
cleanup where an exception must be translated to a return value" (see
above for the context). All I did was point out that RAII works fine
in this case.

> On the contrary, I've stated my position on that, explicitly,
> several times, both in this posting and the previous one.
>
> To reiterate, once again, I think RAII should be used whenever it's
> applicable and the cost is not prohibitive.
>

[ function example snipped ]

> >The first version looks a lot simpler to me.
>
> Again, you're preaching to the choir.
>
> Except -- I think we should not be afraid to commit ourselves,
> to say that something *is* simpler.

OK: The first version *is* simpler.

> As I wrote in the posting you're replying to, I think RAII should
> be used whenever it's applicable and the cost is not prohibitive.

Yes. But I think we have different ideas of "whenever it's applicable

I'll try to make this simple.

We began by discussing the merits of your proposal. In discussing your
proposal, I said that RAII already covers the case of resource release
in a single attempt. Then, in an attempt to "prove" that try blocks
are just as natural for resource release, you trotted out some of
Dinkumware's code and implied that I was somehow attacking Mr.
Plaugher. Which, of course, I was not. What exactly was the point of
accusing me of attacking Mr. Plaugher?

Bringing P.J. Plaugher into this was kind of silly, did nothing to add
to the discussion, and in fact distracted from discussing the merits
of your proposal.

So if you didn't like my response, well, I'm sorry about that. I did
not intend to offend. If you thought I was talking about you, I
wasn't. I described before in this post that I've worked with
programmers who had "faulty expertise" and had to be educated.

Whether it looks like it or not, I am in this discussion to give you a
chance to convince me. But in order to convince me, you're going to
have to argue with me and defend your position. That's what peer
review is all about. If you think I'm being tough, what do you think
the standards committee would be like?

I now conclude as I have in each previous post: by saying that I
appreciate all your hard work and effort on this proposal. I may not
agree with it, but I can still be impressed by your passion for it.
Keep fighting the good fight.

Bob
---

Allan W

unread,
Oct 1, 2002, 2:38:06 AM10/1/02
to
bel...@pacbell.net (Bob Bell) wrote

> > Reiteration: if there is no try-catch in a program, then that
program
> > crashes on the first exception.

> You've made this statement before and I'm afraid I don't understand


> it. I can easily imagine robust programs that don't use
> multiple-attempt functions.

I think that what Alf meant (and I'm certain he'll correct me if I'm
wrong), is this:
Assume that a program doesn't have any catch statements at all,
not even in library routines. If that program reaches a throw
statement, by definition it remains uncaught, so it will crash.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

David Abrahams

unread,
Oct 1, 2002, 12:01:48 PM10/1/02
to
"Allan W" <all...@my-dejanews.com> wrote in message
news:7f2735a5.02093...@posting.google.com...

> bel...@pacbell.net (Bob Bell) wrote
>
> > > Reiteration: if there is no try-catch in a program, then that
> program
> > > crashes on the first exception.
>
> > You've made this statement before and I'm afraid I don't understand
> > it. I can easily imagine robust programs that don't use
> > multiple-attempt functions.
>
> I think that what Alf meant (and I'm certain he'll correct me if I'm
> wrong), is this:
> Assume that a program doesn't have any catch statements at all,
> not even in library routines. If that program reaches a throw
> statement, by definition it remains uncaught, so it will crash.

Some people happen to think there's a useful difference between a crash and
a terminate().

--
-----------------------------------------------------------
David Abrahams * Boost Consulting
da...@boost-consulting.com * http://www.boost-consulting.com
---

Andy Sawyer

unread,
Oct 1, 2002, 12:03:05 PM10/1/02
to
all...@my-dejanews.com (Allan W) writes:

> bel...@pacbell.net (Bob Bell) wrote
>
> > > Reiteration: if there is no try-catch in a program, then that
> program
> > > crashes on the first exception.
>
> > You've made this statement before and I'm afraid I don't understand
> > it. I can easily imagine robust programs that don't use
> > multiple-attempt functions.
>
> I think that what Alf meant (and I'm certain he'll correct me if I'm
> wrong), is this:

I think it's what he meant, I also think it's what he wrote.

> Assume that a program doesn't have any catch statements at all,
> not even in library routines. If that program reaches a throw
> statement, by definition it remains uncaught, so it will crash.

Strictly speaking, it won't "crash" (whatever that means) but it
_will_ call the terminate() function:

15.3, para 9:

"If no matching handler is found in a program, the function
terminate() is called; whether or not the stack is unwound before
this call to terminate() is implementationdefined (15.5.1)."

If there are no try-catch blocks, there are no _matching_ handlers.

Regards,
Andy S.
--
"Light thinks it travels faster than anything but it is wrong. No matter
how fast light travels it finds the darkness has always got there first,
and is waiting for it." -- Terry Pratchett, Reaper Man
---

Bob Bell

unread,
Oct 2, 2002, 5:52:40 AM10/2/02
to
all...@my-dejanews.com (Allan W) wrote in message news:<7f2735a5.02093...@posting.google.com>...

> bel...@pacbell.net (Bob Bell) wrote
>
> > > Reiteration: if there is no try-catch in a program, then that
> program
> > > crashes on the first exception.
>
> > You've made this statement before and I'm afraid I don't understand
> > it. I can easily imagine robust programs that don't use
> > multiple-attempt functions.
>
> I think that what Alf meant (and I'm certain he'll correct me if I'm
> wrong), is this:
> Assume that a program doesn't have any catch statements at all,
> not even in library routines. If that program reaches a throw
> statement, by definition it remains uncaught, so it will crash.

For clarification, the statement that I was responding to was

> Multiple attempt functions are logically *necessary* for robustness
> in a system relying on exceptions -- no way out of that, AFAICS.

Sounds to me like he's saying that there must be at least one function
that tries to perform its operation in two or more ways. He's said
that a couple of different times. I don't understand what is so
magical about a function which tries two or more algorithms that makes
it more robust than a function which tries only one algorithm. Both
functions can fail and throw an exception.

Bob
---

Andy Sawyer

unread,
Oct 3, 2002, 8:05:52 AM10/3/02
to
bel...@pacbell.net (Bob Bell) writes:

> For clarification, the statement that I was responding to was
>
> > Multiple attempt functions are logically *necessary* for robustness
> > in a system relying on exceptions -- no way out of that, AFAICS.
>
> Sounds to me like he's saying that there must be at least one function
> that tries to perform its operation in two or more ways.

I read "multiple attempt functions" to be functions which can make
"multiple attempts" to complete their operation - not which
necessarily offer two mechanisms for doing so. That does not preclude
external help, so for example function A attempts to allocate memory,
fails and throws an exception. Something else furthur up the callstack
has memory it can release safely and does so. We're now in a position
to rety function A - to make a second "attempt".

Regards,
Andy S.
--
"Light thinks it travels faster than anything but it is wrong. No matter
how fast light travels it finds the darkness has always got there
first,
and is waiting for it." -- Terry Pratchett, Reaper Man

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]


[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Alf P. Steinbach

unread,
Oct 3, 2002, 12:01:06 PM10/3/02
to
On 2 Oct 2002 05:52:40 -0400, bel...@pacbell.net (Bob Bell) wrote:
>For clarification, the statement that I was responding to was
>
>> Multiple attempt functions are logically *necessary* for robustness
>> in a system relying on exceptions -- no way out of that, AFAICS.
>
>Sounds to me like he's saying that there must be at least one function
>that tries to perform its operation in two or more ways. He's said
>that a couple of different times. I don't understand what is so
>magical about a function which tries two or more algorithms that makes
>it more robust than a function which tries only one algorithm. Both
>functions can fail and throw an exception.

That last sentence is literally true, but far from the whole story...

I understand that what's a simple concept in my mind isn't all that
obvious to everybody else. Usually it's that way with me also when
trying to understand something; there's a period where nothing seems
to make sense, until suddenly something clicks and it all fits. I
have even experienced that with some pieces of music; the rhythm is
all off, some instrument starts up but isn't just off-rhythm but
seemingly off-key, then suddenly there's a shift of perception where
the tune synthesizer in the head gets a grip on what the audio input
is really all about and starts modelling the music half in advance;
and so I believe music is very much about expectation, and doesn't
make much sense without a good fit between model and input -- and
perhaps discurse about computer science subjects is also that way.

So, what I'll do is to try to build up some kind of conceptual model,
very gradually, sort of like listening to some new piece of music.

Let's first examine the case of a simplest possible function,


void f();


It doesn't matter much whether f() is a member function or a free
function. It's intended to do something, which I'll call the GOAL
of f(). Old and presumably well-known for all: in order to accomplish
the goal function f() has to make some assumptions about the state it
is called in (program state, external state, time, whatever), and we
call that set of assumptions the PRECONDITIONS of f(); similarly,
given that the preconditions are satisfied, f() guarantees some result
that we call the POSTCONDITION of f(), which includes the goal.

I apologize for rehashing this, but if we don't start at the bottom I
think it will be that impossible-to-grok out-of-synch atonal music...

As a first attempt at formalizing some notion of "correctness" for f()
one might say that the CONTRACT for f() is: given that the preconditions
hold, f() guarantees the postcondition; otherwise, anything goes.

Now, what's wrong with that?

First of all, the preconditions are only requirements on the caller of
f(). The function f() might have additional requirements that the
caller cannot in principle or in practice guarantee, such as e.g.
enough memory. So even with satisfied preconditions and perfect logic
in the f() implementation, f() might fail to ensure the postcondition.

In that case, the only option is to report a breach of contract, e.g.,
in C++, to throw an exception, return an error status (which may
require a change of the f() signature), or whatever scheme is employed
for that purpose.

With this consideration what we called "the contract" becomes merely
the normal case clauses, and the CONTRACT now additionally includes
a set of failure case clauses, mainly how and which failures should
be reported.

An interesting feature of this new and extended contract is that it
cannot be easily specified using only propositional logic, for in
essence it says: try first to ensure the normal case, and *if that
fails*, try to report the error as specified by the failure clauses.
If we (mistakenly) add this to a propositional logic postcondition
it becomes something like "PGoal || PFailure". Which f() can
always fulfill, i.e. be "correct", by just ensuring PFailure...

The point of that little discussion was to show that even for a trivial
single-attempt function a boolean propositional logic postcondition
is just not good enough to fully capture "correctness" -- any
implementation that skipped the step of trying to establish PGoal
would not in any reasonable sense of the word be "correct" -- and
so the notion of such pre- and postcondition specifications isn't
a good choice for definining multiple attempt functions. Therefore,
I'll continue this with only informal definitions. And beg
forgiveness if anyone knows of specification scheme that *is* adequate.

Another weakness of the original attempt at correctness is that even
the normal case might involve a sequence of preferred actions, fallback
actions and so on. Since simple boolean propositional logic conditions
were not good enough for the SINGLE-ATTEMPT function f() it does not
matter much that they also and more profoundly are inadequate for such
a MULTIPLE-ATTEMPT function(). It would, however, be interesting to
know if there is any formal and at least partially language-supported
specification scheme, in the spirit of e.g. Eiffel DBC (Design by
Contract), that is up to this.

But I'm getting ahead of myself...

To explain multiple-attempt functions, assume now that f() calls g(),
which in turn calls h(),


f() --> g() --> h()


h() is an e-mail delivery function. It accesses some e-mail server
and sends an e-mail, which it received as an argument. This operation
may fail, but the normal case contract of h() is to send the mail:
otherwise (failure case contract), it reports failure via an exception.

h() is a simple single-attempt function: it has one single goal, and
if that goal can't be achieved in a single attempt, it fails.

Note for much later discussion, just put this note somewhere at the
back of the head or e.g. in a shirt pocket: that h() reports failure
via an exception is *not* critical here. I just selected that scheme
so that my proposed exception handling construct could most naturally
deal with the failure. If h() used some other mechanism, it would still
be a failure report, and the construct could still be employed, although
in that case the calling function g() would have to throw the exception
internally, on behalf of h().

g() is a more sophisticated function at some higher level of the system.
Its normal case contract is this: it will either deliver an e-mail (for
which it uses h(), but that is just one possible implementation), or
else it will enqueue the e-mail for deferred sending, and inform the
user that the mail wasn't sent but has been enqueued for later sending.

Thus, g() is a multiple attempt function.

g() doesn't have a simple postcondition. If we were to write something
like "PDelivered || PEnqueued || PFailure" as a postcondition, then g()
could not only be "correct" by just ensuring PFailure, it could also be
correct by just ensuring PEnqueued, without ever trying to ensure PDelivered,
or it could even be "correct" by ensuring all three subconditions (although
that last is easily rectified by interpreting || as a counting operator
which yields true when exactly one of the multiple arguments is true). This
just to exemplify the earlier statement that such postconditions fail to be
adequate in a more profound way for multiple attempt functions.

It may so happen that g() is correct in our intuitive sense of correct, i.e.
it tries PDelivered, PEnqueued and PFailure in that order, and due to first
server unavailability and then e.g. a file size limit it fails, and reports
this by throwing an exception back up to f().

Does that mean that the attempt to ensure PEnqueued was a total waste of
time (and added complexity)? That g() could just as well or better be a
single-attempt function()? Nope. First of all, the chance of both attempts
failing will usually be much less than the chance of just one attempt failing.
Second, and more important, if g() doesn't try to ensure PEnqueued, then some
higher level function must, and then *that* function will be multiple-attempt,
and g() will in that case just be a useless intermediary adding complexity.

The only way to get rid of these bothersome multiple-attempt functions is to
change the program specification to remove all attempts at robustness, and
then spend more money on marketing and user education...

Now let's consider what happens when g(), however unlikely, has failed, and
passes the buck up to f().

Let's assume a very simple system, where f() is a near top-level user command
event dispatcher function (it's handed one and one user command event from an
event loop function). Its normal case contract is this: either successfully
dispatch the command event, or else look up the command description and
inform the user that the command failed, possibly with extra information if
provided by the dispatch exception -- in this case, by g()'s exception --
and also log the failure, since the chance is it was now the program's fault.

Thus, f() is also a multiple attempt function: the first attempt (primary
goal) is to dispatch, and the second (fallback goal) to inform and log.

As with g() it is highly unlikely that f() fails, since it is a multiple
attempt function, but theoretically it can.

If it does, it reports the failure up to the event loop, which then does the
"Bird of Phoenix" song-and-dance routine; terminating and restarting the
application. I'll skip the details of the contract... But suffice it to
say that for a robust system, also this function must be multiple-attempt.

Now I hope a clearer picture of what I mean by "multiple attempt function"
has emerged. It's *not* necessarily a function that tries again and again
to achieve exactly the same thing. Although it might be, in practice, as
in the examples above, it will more often be a function that has a main goal
and one or more fallback goals; it has the fallback goals to increase
or basically to provide some robustness; and it will typically be located
on an abstraction boundary, although such functions are also common at the
top level(s) of a system, with less clearly defined abstraction boundaries.

[In passing, the example given earlier in this thread of a top-level
function that in that writer's interpretation of the term was not multiple
attempt was, in my interpretation of the term, multiple attempt because
of the form of its contract (or postcondition, although as discussed above
simple propositional postconditions are not adequate for such functions).]

Now, what makes the multiple attempt functions important with respect to
exception handling?

Simply this: they are the ones that are meant *stop* exceptions (or, in
general, failure propagation). They are the defense, the single providers
of robustness in the face of exceptions. In terms of number of functions
the rest of the system may be a near infinite universe, but that's a
universe that collapses (what? vacuum not st) if the defense doesn't work
as it should -- perfectly, at every level.

The defense is seldom invoked, and it's difficult to test, since almost by
definition exceptions are exceptional, and often unexpected. That means
that the defense should be constructed using techniques, restrictions, so
that to the degree possible it's obvious whether it will work or not, when
the time comes. The responsibility for achieving this clarity, keeping to
the restrictions, can be laid 100% on the programmer, who is given complete
freedom to "compile" the structure down to low-level C++ constructs, or --
the language can provide some support.


Sincerely,

- Alf


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Bob Bell

unread,
Oct 4, 2002, 9:29:37 AM10/4/02
to
Andy Sawyer <an...@evo6.com> wrote in message news:<zntxxf...@ender.evo6.com>...

> bel...@pacbell.net (Bob Bell) writes:
>
> > For clarification, the statement that I was responding to was
> >
> > > Multiple attempt functions are logically *necessary* for robustness
> > > in a system relying on exceptions -- no way out of that, AFAICS.
> >
> > Sounds to me like he's saying that there must be at least one function
> > that tries to perform its operation in two or more ways.
>
> I read "multiple attempt functions" to be functions which can make
> "multiple attempts" to complete their operation - not which
> necessarily offer two mechanisms for doing so. That does not preclude
> external help, so for example function A attempts to allocate memory,
> fails and throws an exception. Something else furthur up the callstack
> has memory it can release safely and does so. We're now in a position
> to rety function A - to make a second "attempt".

Sure, but

1) That isn't what Alf said, nor is it what I was debating. He was
specifically talking about a function which offers two mechanisms to
perform its service. This is one of the motivating examples for the
whole succeed or throw construct.

2) The succeed or throw construct of the OP does not make the case you
are describing any easier or harder.

3) I still don't see why a robust program "must" contain such
functionality. For the particular example you give, there is no
guarantee that just because the higher level function released some
memory that now A has enough to succeed. A could still fail and throw
an exception.

Bob
---

Bob Bell

unread,
Oct 5, 2002, 5:52:05 AM10/5/02
to
alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message news:<3d9bab97....@news.bluecom.no>...

> On 2 Oct 2002 05:52:40 -0400, bel...@pacbell.net (Bob Bell) wrote:
> >For clarification, the statement that I was responding to was
> >
> >> Multiple attempt functions are logically *necessary* for robustness
> >> in a system relying on exceptions -- no way out of that, AFAICS.
> >
> >Sounds to me like he's saying that there must be at least one function
> >that tries to perform its operation in two or more ways. He's said
> >that a couple of different times. I don't understand what is so
> >magical about a function which tries two or more algorithms that makes
> >it more robust than a function which tries only one algorithm. Both
> >functions can fail and throw an exception.
>
> That last sentence is literally true, but far from the whole story...

[snip]

> As a first attempt at formalizing some notion of "correctness" for f()
> one might say that the CONTRACT for f() is: given that the preconditions
> hold, f() guarantees the postcondition; otherwise, anything goes.
>
> Now, what's wrong with that?

Nothing. So far I agree completely. I disagree with the definitions
that follow. Obviously, I therefore disagree with the conclusions that
follow from them.

Specifically:

[ snip new definition of contract ]

> An interesting feature of this new and extended contract is that it
> cannot be easily specified using only propositional logic, for in
> essence it says: try first to ensure the normal case, and *if that
> fails*, try to report the error as specified by the failure clauses.
> If we (mistakenly) add this to a propositional logic postcondition
> it becomes something like "PGoal || PFailure". Which f() can
> always fulfill, i.e. be "correct", by just ensuring PFailure...

Point in fact: a function which does not satisfy its goal but
satisfied its failure clause _is_ correct. From the point of view of a
calling function, it doesn't matter which goal the function satisfies
-- the caller must be able to handle both.

Your reasoning is flawed because it has less to do with formal
specification than what you _want_ the function to do. You _want_ a
function to succeed more often than it fails, so you _want_ a formal
description that includes that preference.

It is perfectly valid for a function to satisfy its failure condition
more often than its success condition. For that matter, it is
perfectly valid for a function to satisfy its failure condition 100%
of the time. A caller _cannot_ prefer one over the other; it must be
prepared for both.

Here's an example of why this point of view is useful.

For unit testing, we routinely run low level modules outside of the
context of the system at large; we write test code which takes on the
role of the system and makes calls to the low level module to exercise
it. A useful method or testing higher level modules is to perform the
same substitution in reverse: we write test code that substitutes for
the low level module. The high level module then makes calls to the
testing code in the same way that it would call the low level module
itself.

In particular, this allows us to test how the high level code responds
to failures. We can write the test functions called by the high level
code to fail more frequently than the low level code does. In fact, we
can write the test functions to do nothing _but_ fail, just to test
how well the high level modules respond.

Note that the testing code satisfies the formal goals of the low level
module, which is why it can be substituted into the system without
breaking it.

But if your point of view is that it is _invalid_ for a function to
never satisfy its success goal, you might not consider such a powerful
method for testing.

Because it begins with flawed premises, the rest of your post leads to
flawed conclusions. However, I will comment on the following point:

> Now, what makes the multiple attempt functions important with respect to
> exception handling?
>
> Simply this: they are the ones that are meant *stop* exceptions (or, in
> general, failure propagation). They are the defense, the single providers
> of robustness in the face of exceptions. In terms of number of functions
> the rest of the system may be a near infinite universe, but that's a
> universe that collapses (what? vacuum not st) if the defense doesn't work
> as it should -- perfectly, at every level.

This is an interesting point. It seems like you're saying that a
multiple attempt function is any function with a try block, since a
catch handler is by definition the only thing that can stop an
exception. This is a somewhat broader definition of "multiple attempt"
than I was thinking of, and I stand corrected. I can now see why you
say that robust programs *must* have a multiple attempt function.

Bob
---

Alf P. Steinbach

unread,
Oct 5, 2002, 1:45:31 PM10/5/02
to
On 5 Oct 2002 05:52:05 -0400, bel...@pacbell.net (Bob Bell) wrote:

>alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message

Heh heh. :-)

So you're prepared to pay the consulting firm its outragous fee when
they deliver a perfect system -- no function implementation consists
of more than "throw std::runtime_error()"?

>Here's an example of why this point of view is useful.
>
>For unit testing, we routinely run low level modules outside of the
>context of the system at large; we write test code which takes on the
>role of the system and makes calls to the low level module to exercise
>it. A useful method or testing higher level modules is to perform the
>same substitution in reverse: we write test code that substitutes for
>the low level module. The high level module then makes calls to the
>testing code in the same way that it would call the low level module
>itself.
>
>In particular, this allows us to test how the high level code responds
>to failures. We can write the test functions called by the high level
>code to fail more frequently than the low level code does. In fact, we
>can write the test functions to do nothing _but_ fail, just to test
>how well the high level modules respond.
>
>Note that the testing code satisfies the formal goals of the low level
>module, which is why it can be substituted into the system without
>breaking it.
>
>But if your point of view is that it is _invalid_ for a function to
>never satisfy its success goal, you might not consider such a powerful
>method for testing.

Uhm, I think this belongs in the realm of contextual interpretation of
contracts. It is IMHO a very good point, related to testing and formal
specifications. But this practical point, the *degree* to which simple
propositional postconditions are inadequate, is simply not relevant to
the discussion we had -- it's a new discussion.

Anyway, what's needed to resolve that "dilemma" (allow testing, yet don't
accept a non-functional system) is probably yet again to differentatiate
instead of thinking of two different things as the same.

But I'll leave it to others to do that differentiation. It's quite
possible that there are several good resolutions. And it isn't like
it's a showstopper: we know what we mean by correctness, what we (or
I and you) don't quite know is how to best express it formally.

>Because it begins with flawed premises, the rest of your post leads to
>flawed conclusions.

The premise about inadequate propositional postcondition specifications
now seems only stronger and more well-founded, thanks to your point.

And that premise was made to explain why I didn't base the discussion
on such postconditions...

I don't see any other premise?

> However, I will comment on the following point:
>
>> Now, what makes the multiple attempt functions important with respect to
>> exception handling?
>>
>> Simply this: they are the ones that are meant *stop* exceptions (or, in
>> general, failure propagation). They are the defense, the single providers
>> of robustness in the face of exceptions. In terms of number of functions
>> the rest of the system may be a near infinite universe, but that's a
>> universe that collapses (what? vacuum not st) if the defense doesn't work
>> as it should -- perfectly, at every level.
>
>This is an interesting point. It seems like you're saying that a
>multiple attempt function is any function with a try block, since a
>catch handler is by definition the only thing that can stop an
>exception. This is a somewhat broader definition of "multiple attempt"
>than I was thinking of, and I stand corrected. I can now see why you
>say that robust programs *must* have a multiple attempt function.

I'm glad we're converging here. Detail: not "any" try block, since a
try block -- however distasteful it might seem -- might be used for
just simple cleanup, or even as just a creative control structure. But
in conventional well-written code it seems that most try-blocks, if not
all, could probably be better expressed as either RAII or s-or-t.


Sincerely,

- Alf


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Bob Bell

unread,
Oct 7, 2002, 12:19:37 PM10/7/02
to
alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message news:<3d9eedbd....@news.bluecom.no>...

> On 5 Oct 2002 05:52:05 -0400, bel...@pacbell.net (Bob Bell) wrote:
> >It is perfectly valid for a function to satisfy its failure condition
> >more often than its success condition. For that matter, it is
> >perfectly valid for a function to satisfy its failure condition 100%
> >of the time. A caller _cannot_ prefer one over the other; it must be
> >prepared for both.
>
> Heh heh. :-)
>
> So you're prepared to pay the consulting firm its outragous fee when
> they deliver a perfect system -- no function implementation consists
> of more than "throw std::runtime_error()"?

This is a ridiculous extrapolation of my point, since I was speaking
about the point of view of a calling function, although I'll give you
the benefit of the doubt and assume you were joking.

[ snip ]

> >But if your point of view is that it is _invalid_ for a function to
> >never satisfy its success goal, you might not consider such a powerful
> >method for testing.
>
> Uhm, I think this belongs in the realm of contextual interpretation of
> contracts. It is IMHO a very good point, related to testing and formal
> specifications. But this practical point, the *degree* to which simple
> propositional postconditions are inadequate, is simply not relevant to
> the discussion we had -- it's a new discussion.
>
> Anyway, what's needed to resolve that "dilemma" (allow testing, yet don't
> accept a non-functional system) is probably yet again to differentatiate
> instead of thinking of two different things as the same.
>
> But I'll leave it to others to do that differentiation. It's quite
> possible that there are several good resolutions. And it isn't like
> it's a showstopper: we know what we mean by correctness, what we (or
> I and you) don't quite know is how to best express it formally.

But I think that this is getting to one of the fundamental points of
our disagreement. One of my main points all along has been that I've
never been convinced of the necessity of defining a function's
postconditions to exclude thrown exceptions, but much of your argument
stems from just this exclusion. Because we disagree on such a
fundamental premise, of course we disagree on conclusions.

As far as I'm concerned, a function's postconditions _are_ the
promises the function makes about changes to the state of the system
once the function has exited, whether it has exited due to an
exception or a normal return. Making exceptions "special" just adds
confusion to the issue, IMHO. For example,

-- it makes the idea of a contract more complex than it needs to be.
-- it reinforces the idea that reasoning about exception handling
flows is somehow fundamentally different from reasoning about other
control flows
-- it reinforces the idea that exceptions are difficult to write,
test, and predict
-- it reinforces the idea that exceptions are "dangerous" and should
only be used by experts

In addition, I haven't been convinced of any benefit from excluding
exceptions from postconditions. All pain, no gain.

I'm not saying that exception handling is trivial. But the
"reinforcements" listed above are inaccurate exagerrations of the true
situation, and are therefore not very helpful.

I'm pretty sure that we're never going to agree on this, but I'm happy
to continue debating this as long as you want. Perhaps you could tell
me what benefit we get from saying that postconditions can't include
exceptions? Or tell me what's wrong with the other points I made
above?

Bob
---

Alf P. Steinbach

unread,
Oct 8, 2002, 12:01:24 AM10/8/02
to
On 7 Oct 2002 12:19:37 -0400, bel...@pacbell.net (Bob Bell) wrote:

>alf_p_s...@yahoo.no.invalid (Alf P. Steinbach) wrote in message
news:<3d9eedbd....@news.bluecom.no>...
>> On 5 Oct 2002 05:52:05 -0400, bel...@pacbell.net (Bob Bell) wrote:
>> >It is perfectly valid for a function to satisfy its failure
condition
>> >more often than its success condition. For that matter, it is
>> >perfectly valid for a function to satisfy its failure condition 100%
>> >of the time. A caller _cannot_ prefer one over the other; it must be
>> >prepared for both.
>>
>> Heh heh. :-)
>>
>> So you're prepared to pay the consulting firm its outragous fee when
>> they deliver a perfect system -- no function implementation
consists
>> of more than "throw std::runtime_error()"?
>
>This is a ridiculous extrapolation of my point, since I was speaking
>about the point of view of a calling function, although I'll give you
>the benefit of the doubt and assume you were joking.

Heh heh heh. :-))


<reinserted context, later Bob wrote>
>> >Note that the testing code [alf's note: where every function throws]


>> >satisfies the formal goals of the low level module, which is why it
>> >can be substituted into the system without breaking it.

</reinserted context, later Bob wrote>

So no, I wasn't just joking. Either propositional postconditions are
fully adequate to specify the formal correctness, as you seem to
maintain,
in which case, pay the consulting firm for that perfect system where
every function just throws (pay up). Or else, they are not fully
adequate,
or formal correctness is in itself inadequate to specify what you're
willing to pay for. ;-)

But as I note below,


* let's continue *that* debate in e.g. [comp.programming] --
or let's discontinue it.

>...


>But I think that this is getting to one of the fundamental points of
>our disagreement. One of my main points all along has been that I've
>never been convinced of the necessity of defining a function's
>postconditions to exclude thrown exceptions, but much of your argument
>stems from just this exclusion. Because we disagree on such a
>fundamental premise, of course we disagree on conclusions.

I don't get the semantic content. Postconditions were not a premise for
anything. I wrote


... so the notion of such pre- and postcondition specifications isn't


a good choice for definining multiple attempt functions. Therefore,
I'll continue this with only informal definitions. And beg
forgiveness if anyone knows of specification scheme that *is*
adequate.


But the postcondition debate is a good one on its own, although not
more relevant in this thread, and indeed OFF-TOPIC for [comp.std.c++],
so
perhaps a new thread on that, in e.g. [comp.programming]?

>As far as I'm concerned, a function's postconditions _are_ the
>promises the function makes about changes to the state of the system
>once the function has exited, whether it has exited due to an
>exception or a normal return. Making exceptions "special" just adds
>confusion to the issue, IMHO. For example,
>
>-- it makes the idea of a contract more complex than it needs to be.
>-- it reinforces the idea that reasoning about exception handling
>flows is somehow fundamentally different from reasoning about other
>control flows
>-- it reinforces the idea that exceptions are difficult to write,
>test, and predict
>-- it reinforces the idea that exceptions are "dangerous" and should
>only be used by experts
>
>In addition, I haven't been convinced of any benefit from excluding
>exceptions from postconditions. All pain, no gain.
>
>I'm not saying that exception handling is trivial. But the
>"reinforcements" listed above are inaccurate exagerrations of the true
>situation, and are therefore not very helpful.
>
>I'm pretty sure that we're never going to agree on this, but I'm happy
>to continue debating this as long as you want. Perhaps you could tell
>me what benefit we get from saying that postconditions can't include
>exceptions?

None. They can. But that is one case where they don't adequately
capture correctness, in the sense that you're willing to pay for any
system that fully conforms to the (postcondition-based) specification;
and that was one of the reasons stated for *not* basing the
exposition of multiple attempt functions on such postconditions.

But as I noted above,


* let's continue *that* debate, the one about postconditions, in e.g.
[comp.programming] -- or let's discontinue it.


It is not relevant to the succeed-or-throw construct, as I see it,
and is indeed OFF-TOPIC for [comp.std.c++], unless the proverbial
"someone" (TM) starts an "add DBC support to C++" thread.


>Or tell me what's wrong with the other points I made
>above?

See above, although I think that only scratches the surface.


Sincerely,

- Alf

(who fails to see any relevance to the merits of succeed-or-throw)


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

0 new messages