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

Guru of the Week #82: Solution

27 views
Skip to first unread message

Herb Sutter

unread,
Feb 14, 2002, 9:37:24 PM2/14/02
to

-------------------------------------------------------------------
Guru of the Week problems and solutions are posted regularly on
news:comp.lang.c++.moderated. For past problems and solutions
see the GotW archive at www.GotW.ca. (c) 2002 H.P.Sutter
News archives may keep copies of this article.
-------------------------------------------------------------------

______________________________________________________________________

GotW #82: Debate #1:
Exception Safety and Specifications -- Are They Worth It?

Difficulty: 8 / 10
______________________________________________________________________


JG Questions
------------

>1. Recap: Briefly define the Abrahams exception safety guarantees
> (basic, strong, and nothrow).

The basic guarantee is that failed operations may alter program state,
but no leaks occur and affected objects/modules are still destructible
and usable, in a consistent (but not necessarily predictable) state.

The strong guarantee involves transactional commit/rollback semantics:
failed operations guarantee program state is unchanged with respect to
the objects operated upon. This means no side effects that affect the
objects, including the validity or contents of related helper objects
such as iterators pointing into containers being manipulated.

The nothrow guarantee means that failed operations will not happen.
The operation will not throw an exception.


>2. What happens when an exception specification is violated? Why?
> Discuss the basic rationale for this C++ feature.

The idea of exception specifications is to do a run-time check that
guarantees that only exceptions of certain types will be emitted from
a function (or that none will be emitted at all). For example, the
following function's exception specification guarantees that f() will
emit only exceptions of type A or B:

int f() throw( A, B );

If an exception would be emitted that's not on the "invited-guests"
list, the function unexpected() will be called. For example:

int f() throw( A, B )
{
throw C(); // will call unexpected()
}

You can register your own handler for the unexpected-exception case by
using the standard set_unexpected() function. Your replacement
handler must take no parameters and it must have a void return type.
For example:

void MyUnexpectedHandler() { /*...*/ }

std::set_unexpected( &MyUnexpectedHandler );

The remaining question is, what can your unexpected handler do? The
one thing it can't do is return via a usual function return. There
are two things it may do:

1. It could decide to translate the exception into something that's
allowed by that exception-specification, by throwing its own
exception that does satisfy the exception-specification list that
caused it to be called, and then stack unwinding continues from
where it left off.

2. It could call terminate(). (The terminate() function can also be
replaced, but must always end the program.)


Guru Questions
--------------

>3. When is it worth it to write code that meets:
>
> a) the basic guarantee?
>
> b) the strong guarantee?
>
> c) the nothrow guarantee?

It is always worth it to write code that meets at least one of these
guarantees. There are several good reasons:

1. Exceptions happen. (To paraphrase a popular saying.)

They just do. The standard library emits them. The language emits
them. We have to code for them. Fortunately, it's not that big a
deal, because we now know how to do it. It does require adopting a
few habits, however, and following them diligently -- but then so did
learning to program with error codes.

The big thorny problem is, as it ever was, the general issue of error
handling. The detail of how to report errors, using return codes or
exceptions, is almost entirely a syntactic detail where the main
differences are in the semantics of how the reporting is done, and so
each approach requires its own style.

2. Writing exception-safe code is good for you.

Exception-safe code and good code go hand in hand. The same
techniques that have been popularized to help us write exception-safe
code are, pretty much without exception, things we usually ought to be
doing anyway. That is, exception-safety techniques are good for your
code in and of themselves, even if exception safety weren't a
consideration.Ā

To see this in action, consider the major techniques I and others have
written about to make exception safety easier:

- Use "resource acquisition is initialization" (RAII) to manage
resource ownership. Using resource-owning objects like Lock
classes and auto_ptrs is just a good idea in general. It should
come as no surprise that among their many benefits we should also
find "exception safety." How many times have you seen a function
(here we're talking about someone else's function, of course, not
something you wrote!) where one of the code branches that leads to
an early return fails to do some cleanup, because cleanup wasn't
being managed automatically using RAII?

- Use "do all the work off to the side, then commit using nonthrowing
operations only" to avoid changing internal state until youā€™re sure
the whole operation will succeed. Such transactional programming
is clearer, cleaner, and safer even with error codes. How many
times have you seen a function (and naturally here again we're
talking about someone else's function, of course, not something you
wrote!) where one of the code branches that leads to an early
return fails to preserve the object's state, because some fiddling
with internal state had already happened before a later operation
failed?

- Prefer "one class (or function), one responsibility." Functions
that have multiple effects, such as the Stack::Pop() and
EvaluateSalaryAndReturnName() functions described in Items 10 and
18 of Exceptional C++ [1], are difficult to make strongly
exception-safe. Many exception safety problems can be made much
simpler, or eliminated without conscious thought, simply by
following the "one function, one responsibility" guideline. And
that guideline long predates our knowledge that it happens to also
apply to exception safety; it's just a good idea in and of
itself.Doing these things is just plain good for you.

Having said that, then, which guarantee should we use when? In brief,
here's the guideline followed by the C++ standard library, and one
that you can profitably apply to your own code:

GUIDELINE: A function should always support the strictest guarantee
that it can support without penalizing users who don't
need it.

So if your function can support the nothrow guarantee without
penalizing some users, it should do so. Note that a handful of key
functions, such as destructors and deallocation functions, simply must
be nothrow-safe operations because otherwise it's impossible to
reliably and safely perform cleanup.

Otherwise, if your function can support the strong guarantee without
penalizing some users, it should do so. Note that vector::insert() is
an example of a function that does not support the strong guarantee in
general because doing so would force us to make a full copy of the
vector's contents every time we insert an element, and not all
programs care so much about the strong guarantee that they're willing
to incur that much overhead. (Those programs that do can "wrap"
vector::insert() with the strong guarantee themselves, trivially:
take a copy of the vector, perform the insert on the copy, and once
it's successful swap() with the original vector and you're done.)

Otherwise, your function should support the basic guarantee.

For more information about some of the above concepts, such as what a
nonthrowing swap() is all about or why destructors should never emit
exceptions, see further reading in Exceptional C++ [1] and More
Exceptional C++ [2].


>4. When is it worth it to write exception specifications on functions?
> Why would you choose to write one, or why not?

In brief, don't bother. Even experts don't bother.

Slightly less briefly, the major issues are:

- Exception specifications can cause surprising performance hits, for
example if the compiler turns off inlining for functions with
exception specifications.

- A runtime unexpected() error is not always what you want to have
happen for the kinds of mistakes that exception specifications are
meant to catch.

- You generally can't write useful exception specifications for
function templates anyway because you generally can't tell what the
types they operate on might throw.

For more, see for example the Boost exception specification rationale
available via http://www.gotw.ca/xc++s/boost_es.htm (it summarizes to
"Don't!").


References
----------

[1] H. Sutter. Exceptional C++ (Addison-Wesley, 2000).

[2] H. Sutter. More Exceptional C++ (Addison-Wesley, 2002).

---
Herb Sutter (www.gotw.ca)

Secretary, ISO WG21/ANSI J16 (C++) standards committee (www.gotw.ca/iso)
Contributing Editor, C/C++ Users Journal (www.cuj.com)

Check out "THE C++ Seminar" - Boston, March 2002 (www.gotw.ca/cpp_seminar)

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

C Nick Beaudrot

unread,
Feb 15, 2002, 1:57:35 AM2/15/02
to
So much to talk about ...

On 14 Feb 2002 21:37:24 -0500, Herb Sutter <hsu...@acm.org> wrote:
: >4. When is it worth it to write exception specifications on functions?


: > Why would you choose to write one, or why not?
:
: In brief, don't bother. Even experts don't bother.
:
: Slightly less briefly, the major issues are:
:
: - Exception specifications can cause surprising performance hits, for
: example if the compiler turns off inlining for functions with
: exception specifications.

I'm just curious ... is there any empircal evidence of this one? Or is
this just one of those things that people have started assuming, like
the fact that the compiler will inline calls to
less< double >::operator(), that COW strings are a pessimization in a
multithreaded environment, or that the Double Checked Locking Pattern
doesn't work?

[I'm inlining some trivial (zero body and accessor) functions and Forte
C++ seems perfectly willing to inline them with the throw clauses
included. YMMV of course]

: - A runtime unexpected() error is not always what you want to have


: happen for the kinds of mistakes that exception specifications are
: meant to catch.
:
: - You generally can't write useful exception specifications for
: function templates anyway because you generally can't tell what the
: types they operate on might throw.

While I agree with most of these points, one of my colleagues has
pointed out that lack of exception specifications makes it difficult to
integrate 3rd party code. That is, my friend would actually like to have
the extra safety provided by ES, even with the extra maintenance cost
(he's a little pedantic, but that's usually a good trait in a
programmer, right?). However, if I write code that doesn't use exception
specifications, he can't use it. It's as though he wrote a slew of const
correct code, and then I handed him a slew of code that was not const
correct--he can't use it without hacking in my header files. Is there
any good solution to this? Or will we just always use the preprocessor
to turn ES on/off as needed?


Cheers,
Nick

--
Don't inflict upon me an idiotic belief of a God who doesn't answer the
prayers of sick children, at least in a tangible way that we understand.
Who doesn't intervene apparently in cases of war or poverty and famine.
But hey Mr self-absorbed NFL wide receiver, you need the first down on
third-and-seven? God's right there, he's got you covered.
-Bob Costas

Francis Glassborow

unread,
Feb 15, 2002, 8:01:35 AM2/15/02
to
In article <a4hu51$ps0$1...@saturn.services.brown.edu>, C Nick Beaudrot
<n...@cs.brown.edu> writes

>While I agree with most of these points, one of my colleagues has
>pointed out that lack of exception specifications makes it difficult to
>integrate 3rd party code. That is, my friend would actually like to have
>the extra safety provided by ES, even with the extra maintenance cost
>(he's a little pedantic, but that's usually a good trait in a
>programmer, right?). However, if I write code that doesn't use exception
>specifications, he can't use it. It's as though he wrote a slew of const
>correct code, and then I handed him a slew of code that was not const
>correct--he can't use it without hacking in my header files. Is there
>any good solution to this? Or will we just always use the preprocessor
>to turn ES on/off as needed?

Currently we have the worst of all worlds. ES is not statically checked,
and because most code does not provide it, there is no motivation to
provide a supplementary tool to do so. The execution time problems
encourage programmers to avoid use of ES.

I think we need to reformulate ES in the core of the language so that:

1) It is statically checked. Yes I know that can cause problems, but,
pragmatically, compilers will provide a switch to suppress this.

1A) Make violation undefined behaviour.

2) Mechanisms to allow their use in template environments.

3) Removal of execution time checking

--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

Sergey P. Derevyago

unread,
Feb 15, 2002, 10:19:51 AM2/15/02
to
Herb Sutter wrote:
> >1. Recap: Briefly define the Abrahams exception safety guarantees
> > (basic, strong, and nothrow).
>
> The basic guarantee is that failed operations may alter program state,
> but no leaks occur and affected objects/modules are still destructible
> and usable, in a consistent (but not necessarily predictable) state.
IMHO from the practical point of view, "unpredictable" is (virtually) equal
to "unusable". So it would be enough to guarantee that "failed object" can be:
- destructed
- assigned from
- copied
I.e. the basic guarantee guarantees not to break the basic object invariants.
That's all :)

> The nothrow guarantee means that failed operations will not happen.
> The operation will not throw an exception.

I.e. the operation will not allow (possible) "internal exceptions" to escape.

> The idea of exception specifications is to do a run-time check that
> guarantees that only exceptions of certain types will be emitted from
> a function (or that none will be emitted at all).

IMHO the poor std::bad_exception should also be mentioned :)
http://www.research.att.com/~bs/3rd_issues.html
--
With all respect, Sergey. http://cpp3.virtualave.net/
mailto : ders at skeptik.net

Daniel T.

unread,
Feb 15, 2002, 12:15:41 PM2/15/02
to
Herb Sutter <hsu...@acm.org> wrote:

>>4. When is it worth it to write exception specifications on functions?
>> Why would you choose to write one, or why not?
>
>In brief, don't bother. Even experts don't bother.
>
>Slightly less briefly, the major issues are:
>
> - Exception specifications can cause surprising performance hits, for
> example if the compiler turns off inlining for functions with
> exception specifications.

Couldn't this be considered a compiler bug?

> - A runtime unexpected() error is not always what you want to have
> happen for the kinds of mistakes that exception specifications are
> meant to catch.

What if it is? See below...

> - You generally can't write useful exception specifications for
> function templates anyway because you generally can't tell what the
> types they operate on might throw.

But what if you can? See below...

>For more, see for example the Boost exception specification rationale
>available via http://www.gotw.ca/xc++s/boost_es.htm (it summarizes to
>"Don't!").

(I got a "Not Found" error on the web page above.)

[below]
One of the "rules of good programming" you forgot to mention is that all
exception types should be part of the same hierarchy. (ie all should
derive from std::exception even if indirectly.) Given this rule, then we
can reliably say that we want any breaking of this rule to find its way
to a single function that takes care of the problem (by converting the
bad exception to a good one, and/or reporting the problem to the
programmer.) We can also reliably write useful exception specifications
for function templates [either "throw()" or "throw(std::exception)".]

That leaves the issue of the compiler refusing to inline a function that
has an exception spec. Please let us know what compilers do this so that
they may be avoided...

Daniel James

unread,
Feb 15, 2002, 12:16:12 PM2/15/02
to
In article <a4hu51$ps0$1...@saturn.services.brown.edu>, C Nick
Beaudrot wrote:
> : - Exception specifications can cause surprising performance
> : hits, for example if the compiler turns off inlining for
> : functions with exception specifications.
>
> I'm just curious ... is there any empircal evidence of this one?

The Borland compiler certainly claims not to inline functions with
exception declarations.

A quick test shows that the Microsoft V7.0 compiler (from .NET) can
inline functions that have an exception specification but won't
inline a function that actually throws (Note, though, that VC7
treats any exception spec other than "nothrow()" as "throw(...)").

I think Herb's advice is basically sound, exception specs (except,
perhaps, nothrow()) don't buy you anything useful (because a call
to unexpected() at runtime is unlikely to be what you want to see
when an exception specification is violated) but may cause your
compiler (or the one you upgrade to next week) to generate less
efficient code than it might.

If exception specs could be reworked so that a mismatch could be
detected and reported reliably at compile time I'd be all for them
- but (as Herb says) the issues with templates make that
problematic.

Cheers,
Daniel
[nospam.demon.co.uk is a spam-magnet. Replace nospam with sonadata
to reply]

Dave Harris

unread,
Feb 15, 2002, 12:58:34 PM2/15/02
to
hsu...@acm.org (Herb Sutter) wrote (abridged):

> >4. When is it worth it to write exception specifications on functions?
> > Why would you choose to write one, or why not?
>
> In brief, don't bother. Even experts don't bother.

This is a self-fulfilling prophecy. I am not saying you're wrong, but I
think it's worth pointing out some of the issues and possibilities for the
future.


> - A runtime unexpected() error is not always what you want to have
> happen for the kinds of mistakes that exception specifications are
> meant to catch.

It should be reasonable to use exception specifications where you are sure
you have not made a mistake. That is:

We should aim for exception specifications which a reasonably
smart compiler can prove will be met.

This probably means the function should not call any function which does
not itself have an exception specification. For example, if a swap()
function contains only non-throwing operations it can itself have an empty
exception specification.

If most exception specifications are trivially met, it will be reasonable
for compilers to warn if they cannot prove, at compile time, they are met.
Thus we can gradually move towards *static* constraints on exceptions
thrown. This will be valuable for correctness.


> - Exception specifications can cause surprising performance hits, for
> example if the compiler turns off inlining for functions with
> exception specifications.

If nobody uses them, no vendor will bother fixing the problems.

If we use them in accordance with the above guideline, compilers will be
able to optimise away the unexpected() infrastructure. Such exception
specifications need have no runtime overhead at all.

The "Exception spec not met" warning ought to be given exactly when the
exception specification code cannot be optimised away.


> - You generally can't write useful exception specifications for
> function templates anyway because you generally can't tell what the
> types they operate on might throw.

See above. An exception specification for a template implies exception
constraints for the types it operates on. Implementations can check these
constraints at compile time. Although we can't write useful exception
specifications for all function templates, we should still use them where
they do make sense.

For example:

template <typename First, typename Second>
struct Pair {
First first;
Second second;
};

void swap( Pair &lhs, Pair &rhs ) throw() {
swap( lhs.first, rhs.first );
swap( lhs.first, rhs.second );
}

If my algorithms depend on swap<Pair&,Pair&> offering the no-throw
guarantee, I want to be warned if someone instantiates with a type which
doesn't offer a no-throwing swap.

How else can a library author communicate and enforce his or her
requirements? Templates are where exception specifications are potentially
most useful.


> For more, see for example the Boost exception specification rationale
> available via http://www.gotw.ca/xc++s/boost_es.htm (it summarizes to
> "Don't!").

That was a dead link for me. I hope the gist is in:
http://www.boost.org/more/lib_guide.htm#Exception-specification

It doesn't seem to address my points. It talks only of efficiency, not
correctness. Which of course is reasonable, since I know of no current
implementation which issues compile-time warnings for violated
specifications.

Still, compiler vendors are very aware of Boost. I think if they adopted
exception specifications, as discussed above, vendors would be more
motivated to provide compile-time warnings and efficient implementations.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

David Abrahams

unread,
Feb 15, 2002, 1:00:24 PM2/15/02
to

"Herb Sutter" <hsu...@acm.org> wrote in message
news:dlgo6us8bn5tjjlak...@4ax.com...

>
> For more, see for example the Boost exception specification rationale
> available via http://www.gotw.ca/xc++s/boost_es.htm (it summarizes to
> "Don't!").

or you can just go directly to
http://www.boost.org/more/lib_guide.htm#Exception-specification

I should point out that these guidelines were written for Boost, where we're
trying to write cross-platform portable libraries. Empty exception
specifications may make sense even on inline functions, depending on your
compiler. At the time the guidelines were written, it was best to not to
assume that these empty specifications wouldn't cause a pessimization if you
didn't know for sure. Many compilers have improved a lot since then (though
many in common use have not).

-Dave

David Abrahams

unread,
Feb 15, 2002, 1:01:20 PM2/15/02
to

"C Nick Beaudrot" <n...@cs.brown.edu> wrote in message
news:a4hu51$ps0$1...@saturn.services.brown.edu...


> While I agree with most of these points, one of my colleagues has
> pointed out that lack of exception specifications makes it difficult to
> integrate 3rd party code. That is, my friend would actually like to have
> the extra safety provided by ES

What extra safety? He wants you to promise your code will die before it
throws anything he doesn't expect?

> even with the extra maintenance cost
> (he's a little pedantic, but that's usually a good trait in a
> programmer, right?).

Usually, but blind pedantry can be destructive.

> However, if I write code that doesn't use exception
> specifications, he can't use it.

Don't you mean "he won't use it"?

> It's as though he wrote a slew of const
> correct code, and then I handed him a slew of code that was not const
> correct--he can't use it without hacking in my header files.

I don't understand what that means. Do you have a compiler that enforces
ESes somehow?

> Is there any good solution to this?

It's not clear to me whether you have a technical problem or it's a "people
issue".

-Dave

Johan Johansson

unread,
Feb 15, 2002, 1:03:08 PM2/15/02
to
Francis Glassborow wrote:

> Currently we have the worst of all worlds. ES is not statically checked,
> and because most code does not provide it, there is no motivation to
> provide a supplementary tool to do so. The execution time problems
> encourage programmers to avoid use of ES.
>
> I think we need to reformulate ES in the core of the language so that:

> 2) Mechanisms to allow their use in template environments.


In another thread I tried suggesting that the full ES of an instantiated
template would be the ones that can actually be thrown by the
instantiated template. This at least to me seem to be more in line with
e.g. type rules regarding templates that don't relate to the template
itself but to the instantiated form. In other words, the template should
specify the which exceptions it may itself produce and then those
produced by the instantiated function would be added to that
specification. For this to make sense static ES checking would be needed
of course, but then again I think this is true for exceptions
specifications in general.

j

Anthony Williams

unread,
Feb 15, 2002, 1:03:29 PM2/15/02
to
"C Nick Beaudrot" <n...@cs.brown.edu> wrote in message
news:a4hu51$ps0$1...@saturn.services.brown.edu...
> On 14 Feb 2002 21:37:24 -0500, Herb Sutter <hsu...@acm.org> wrote:
> : >4. When is it worth it to write exception specifications on functions?
> : > Why would you choose to write one, or why not?

> : Slightly less briefly, the major issues are:


> :
> : - Exception specifications can cause surprising performance hits, for
> : example if the compiler turns off inlining for functions with
> : exception specifications.
>
> I'm just curious ... is there any empircal evidence of this one?

Note that Herb said "can cause .. performance hits", not "do cause
performance hits". In any case, if an exception is thrown, the presence of
an exception specification means that the compiler must check (at runtime)
the type of the exception against all the valid cases listed in the
exception specification, in addition to the "normal" exception handling
code, so code that throws exceptions cannot help but be slower with
exception specs than without.

> [I'm inlining some trivial (zero body and accessor) functions and Forte
> C++ seems perfectly willing to inline them with the throw clauses
> included. YMMV of course]

Inlining trivial functions is trivial --- they can't violate their exception
specs as they don't have any code. Try some code that contains a call to an
extern function without an exception spec. In any case, this is compiler
dependent, so YMMV.

> : - A runtime unexpected() error is not always what you want to have
> : happen for the kinds of mistakes that exception specifications are
> : meant to catch.
> :
> : - You generally can't write useful exception specifications for
> : function templates anyway because you generally can't tell what the
> : types they operate on might throw.
>
> While I agree with most of these points, one of my colleagues has
> pointed out that lack of exception specifications makes it difficult to
> integrate 3rd party code. That is, my friend would actually like to have
> the extra safety provided by ES, even with the extra maintenance cost
> (he's a little pedantic, but that's usually a good trait in a
> programmer, right?). However, if I write code that doesn't use exception
> specifications, he can't use it. It's as though he wrote a slew of const
> correct code, and then I handed him a slew of code that was not const
> correct--he can't use it without hacking in my header files. Is there
> any good solution to this? Or will we just always use the preprocessor
> to turn ES on/off as needed?

You can easily use code without ES in code that does. Consider the
following:

void f() // throw(int)
{
throw 3;
}

void g() throw(std::runtime_error,std::bad_cast)
{
f(); // compiles OK
}

f() throws an exception that violates g()'s exception specification, so the
runtime library calls unexpected() (which calls terminate() by default). If
the exception specification on f() is uncommented, exactly the same would
happen.

Compare this to const correct code:

void f2(char*p) // doesn't modify *p
{
}

void g2(const char *p)
{
f(p); // doesn't compile, need const_cast<>
}

You should defnitely DOCUMENT what exceptions a function should throw, so
that your users' code can handle them if necessary, but the use of exception
specifications adds nothing except runtime cost and space overhead to a
program. If you're really worried about a function throwing an exception it
didn't document, wrap every function in a try/catch with a final
"catch(...){std::terminate();}" after all the "permitted" exceptions ---
this is essentially what the compiler has to do to support exception
specifications, and its not much more to maintain on your part. In any case,
the important fact is usually that the code threw an exception, not what
type it threw. You might be able to handle particular failures, but the
documentation should tell you what you need to catch; you don't need
exception specs for that.

Here is a reimplementation of g() with a try/catch instead of exception
specification:

void g()
try
{
f();
}
catch(const std::runtime_error&)
{
throw;
}
catch(const std::bad_cast&)
{
throw;
}
catch(...)
{
std::terminate();
}

OR an implementation to handle f() without an exception spec (but documented
to throw exceptions of type int)

void g() throw(std::runtime_error,std::bad_cast)
{
try
{
f();
}
catch(int)
{
throw;
}
catch(...)
{
std::terminate();
}
}

Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optical Components Ltd
The opinions expressed in this message are not necessarily those of my
employer

James Kanze

unread,
Feb 15, 2002, 1:06:20 PM2/15/02
to
n...@cs.brown.edu (C Nick Beaudrot) wrote in message
news:<a4hu51$ps0$1...@saturn.services.brown.edu>...

> So much to talk about ...

> On 14 Feb 2002 21:37:24 -0500, Herb Sutter <hsu...@acm.org> wrote:
> > >4. When is it worth it to write exception specifications on
> > > functions? Why would you choose to write one, or why not?

> > In brief, don't bother. Even experts don't bother.

> > Slightly less briefly, the major issues are:

> > - Exception specifications can cause surprising performance hits,
> > for example if the compiler turns off inlining for functions
> > with exception specifications.

> I'm just curious ... is there any empircal evidence of this one? Or
> is this just one of those things that people have started assuming,
> like the fact that the compiler will inline calls to less< double
> >::operator(), that COW strings are a pessimization in a
> multithreaded environment, or that the Double Checked Locking
> Pattern doesn't work?

Don't start confusing things. The actual status of the issues you
mention is different for each issue:

- This is the first time I've heard this claim concerning exception
specifications, but I've not been listening. I doubt that it's
true for all compilers (since I know that implementations are
possible where there is no real cost); I don't know how true it
might be for any individual compiler.

- Inlining something like less< double >::operator() is trivial. A
compiler which doesn't do it when optimization is turned on is
just stupid. But the questions I've seen haven't concerned
inlining less< double >::operator() alone; they've concerned
inlining it in a function which is already inlined, possibly with
several levels of nesting of functions. I think it pretty well
recognized that all compilers have some limits with regards to
inlining, and in complicated cases, who knows?

- I've yet to see any real consensus about COW strings. Except,
maybe, that they are harder to get right in a multithreaded
environment. At present, at least one major implementation (g++)
uses COW, and they are attempting to do so in a multithreaded
environment.

- The double checked locking idiom has been proved not to work, and
cases have been presented where it doesn't work. The problem here
is the opposite: a lot of people don't seem to be aware that it
doesn't work, and insist on continuing to use it.

> [I'm inlining some trivial (zero body and accessor) functions and
> Forte C++ seems perfectly willing to inline them with the throw
> clauses included. YMMV of course]

> > - A runtime unexpected() error is not always what you want to
> > have happen for the kinds of mistakes that exception
> > specifications are meant to catch.

> > - You generally can't write useful exception specifications for
> > function templates anyway because you generally can't tell what
> > the types they operate on might throw.

> While I agree with most of these points, one of my colleagues has
> pointed out that lack of exception specifications makes it difficult
> to integrate 3rd party code. That is, my friend would actually like
> to have the extra safety provided by ES, even with the extra
> maintenance cost (he's a little pedantic, but that's usually a good
> trait in a programmer, right?). However, if I write code that
> doesn't use exception specifications, he can't use it. It's as
> though he wrote a slew of const correct code, and then I handed him
> a slew of code that was not const correct--he can't use it without
> hacking in my header files. Is there any good solution to this? Or
> will we just always use the preprocessor to turn ES on/off as
> needed?

You (or your friend) cannot win fighting the language, and C++ has
been designed so as to make exception specifications as awkward as
possible. I can sympathize with your friend; having actually
discussed the issues with people who have done serious work in
languages with real checked exceptions (not Java), it seems clear that
they improve robustness and maintainability. But in C++, I'm afraid
it is not to be. For whatever reasons.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique orientée objet
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany, Tél.: +49 (0)69 19 86 27

Peter Dimov

unread,
Feb 15, 2002, 1:44:31 PM2/15/02
to
n...@cs.brown.edu (C Nick Beaudrot) wrote in message news:<a4hu51$ps0$1...@saturn.services.brown.edu>...
> While I agree with most of these points, one of my colleagues has
> pointed out that lack of exception specifications makes it difficult to
> integrate 3rd party code. That is, my friend would actually like to have
> the extra safety provided by ES,

Imagine a situation where I say: "Exception specifications provide no
extra safety whatsoever." How would you refute my assertion?

> even with the extra maintenance cost
> (he's a little pedantic, but that's usually a good trait in a
> programmer, right?). However, if I write code that doesn't use exception
> specifications, he can't use it.

Why? Is he using a tool that statically checks ESs?

Attila Feher

unread,
Feb 15, 2002, 1:51:04 PM2/15/02
to
C Nick Beaudrot wrote:
[SNIP]

> I'm just curious ... is there any empircal evidence of this one? Or is
> this just one of those things that people have started assuming, like
...

> that COW strings are a pessimization in a
> multithreaded environment...

Khm. Khm. Using Forte C++ and _not_ hitting the COW problem? I mean I
have seen processes not being able to make more than 23% CPU load on a
4CPU system (that is about 12% on an 8 CPU, ~6% on a 16CPU), because the
threads were creating and destrying strings (implementations), which has
lead to a state, where they were waiting for mutex_create/destroy_for
each other: basically rendering any SMP to a bit slower than a single
CPU system. How can one avoid that (without changing the Rogue Wave STL
and still using strings)? I mean 60% of the CPU time was spent in
pthread_mutex_create and destroy...
[SNIP]

While this is off-topic to exception specifications, I would really like
to see - since you have boldly stated that my experiences are just
assumptions - that why do you think that? I mean I see every day COW
killing string-heavy MT applications on SMP. String-heavy means that
many strings are created and destroyed. And by string here I mean the
"guts", the implementation, having the "evil" mutex. And the "joke" is
80% of those strings are local, they would _never_ need the mutex, since
they are only accessed from one thread. :-(

((I see no use of COW anyways, if the code is designed well... Anyways
even if it would have a use, I mean if I need to copy few strings I
guess I can do it in the 75% CPU time I get with not-waiting for OS
mutex operations.))

I may sound harsh, but I am just surprised. You do not sound
unexperienced at all, and you say that what I saw isn't there. Please
help me out here! I guess I have told my "facts" and the actual
measurements showing COW killing MT on SMP. I'd like to know on what
bases (facts) are you making the statement that this is "only an
assumption". That facts may help me a lot if I meet this kind of
bottleneck next time! Pretty please! :-)

Attila

Attila Feher

unread,
Feb 15, 2002, 7:14:48 PM2/15/02
to
"Daniel T." wrote:
[SNIP]

> One of the "rules of good programming" you forgot to mention is that all
> exception types should be part of the same hierarchy. (ie all should
> derive from std::exception even if indirectly.)

OOOK. How do you do that when Manager X says: you must use 3rd Party
Library Z, which bases all of its exceptions on class Y, and for which
you have no access to the source code?

> Given this rule,

I have heard a suggestion that one better not to create rules (laws),
which cannot be kept (enforced). The above sounds like one.

[SNIP]


> That leaves the issue of the compiler refusing to inline a function that
> has an exception spec. Please let us know what compilers do this so that
> they may be avoided...

:-))) I doubt that most of us is in the state where they choose what
compiler is to be used...

A

Jerry Coffin

unread,
Feb 15, 2002, 7:16:35 PM2/15/02
to
In article <memo.20020215...@brangdon.madasafish.com>,
bran...@cix.co.uk says...

[ ... ]

> It should be reasonable to use exception specifications where you are sure
> you have not made a mistake. That is:

This is basically impossible. Just for an obvious example, consider
writing a a library that doesn't call ANY lower-level functions, and
doesn't throw anything itself. The library vendor therefore
specifies that none of its functions throws anything.

All fine and well so far, you say -- we're assured that no exception
can arise during execution of the library code.

Unfortunately, that's NOT the case. Just for an obvious example,
consider what happens when/if the user installs a handler for some
implementation-defined signal, and the signal handler throws an
exception.

In short, to at least some degree exception specifications enforce
tight coupling throughout ALL the code in a project. We can't assure
that ANY exception specification can be met until or unless we
inspect ALL the other code in the same program.

Unfortunately, that's exactly the opposite of what we want: if
nothing else, we've learned that loose coupling is one of the most
fundamental necessities in large projects -- in fact, nearly every
advance in software engineering has basically been simply one method
or another of producing looser coupling.

Of course, I should point out that the example above is only one that
occurred to me after a minute or two of thinking -- I seriously doubt
it's the only one.

--
Later,
Jerry.

The Universe is a figment of its own imagination.

Alexander Terekhov

unread,
Feb 16, 2002, 4:43:21 AM2/16/02
to
"Anthony Williams"<ant...@nortelnetworks.com> wrote in message
news:<a4j23k$56$1...@bcarh8ab.ca.nortel.com>...
[...]

> Here is a reimplementation of g() with a try/catch instead of exception
> specification:
>
> void g()
> try
> {
> f();
> }
> catch(const std::runtime_error&)
> {
> throw;
> }
> catch(const std::bad_cast&)
> {
> throw;
> }
> catch(...)
> {
> std::terminate();
> }

But the following "reimplemention" WITH exception specification
is MUCH better[1] (if you do NOT want to cope with UNDOCUMENTED
exceptions which could be thrown by some "component" you do
not "trust"; perhaps even loaded dynamically as "plug-in"):

void f_() throw(std::runtime_error,std::bad_cast) { f(); }

void g() { /**/ f_(); /**/ };

What is the problem with RUNTIME enforcement?

Whether compilers/linkers will some day start warn
me about uncaught exceptions at COMPILE/LINK time,
that has nothing to do with ES and its usefulness,
IMHO.

Gurus, you should better think of something NEW along
the lines of "bool std::expected_exception< T >()" ;-)

See "Threads and exceptions" c.p.t/c.l.c++ thread...

regards,
alexander.

[1] Because it allows to debug/"dump" at THROW
point (not catch(...) point) -- NOT loosing
rather meaningful info due to unwinding up
to catch(...) handler!

Daniel T.

unread,
Feb 16, 2002, 4:57:30 AM2/16/02
to
Attila Feher <Attila...@lmf.ericsson.se> wrote:

> "Daniel T." wrote:
> [SNIP]
> > One of the "rules of good programming" you forgot to mention is that all
> > exception types should be part of the same hierarchy. (ie all should
> > derive from std::exception even if indirectly.)
>
> OOOK. How do you do that when Manager X says: you must use 3rd Party
> Library Z, which bases all of its exceptions on class Y, and for which
> you have no access to the source code?

You could wrap the library in a function that is specified to throw
correct exceptions and translate any inapproprate exceptions that get
thrown.

Another common "rule" is to wrap libraries that are likely to change.
Certanly, if a library's use is decided by manager types rather than
technical people, the choice of which library to use will change in
rather arbitrary (at least from a technical POV) ways.

> > Given this rule,
>
> I have heard a suggestion that one better not to create rules (laws),
> which cannot be kept (enforced). The above sounds like one.

That doesn't sound wise to me. I personally live by many rules that are
not forced on me by outside agents. I don't see why my code shouldn't be
just as moral...

Peter Dimov

unread,
Feb 16, 2002, 7:50:04 AM2/16/02
to
Francis Glassborow <francis.g...@ntlworld.com> wrote in message news:<u8EuopBy...@robinton.ntlworld.com>...

> Currently we have the worst of all worlds. ES is not statically checked,
> and because most code does not provide it, there is no motivation to
> provide a supplementary tool to do so. The execution time problems
> encourage programmers to avoid use of ES.

No, not exactly. In fact, I may well go ahead and claim that what we
have now is significantly better than some attempts to "fix" the
status quo by adding static ES checking.

> I think we need to reformulate ES in the core of the language so that:
>
> 1) It is statically checked. Yes I know that can cause problems, but,
> pragmatically, compilers will provide a switch to suppress this.
>
> 1A) Make violation undefined behaviour.
>
> 2) Mechanisms to allow their use in template environments.
>
> 3) Removal of execution time checking

I agree with 1a and 3. What needs to be done is

A. Make all exception specifications except throw() no ops and
deprecate their use.

B. When an exception escapes a throw() function, call this undefined
behavior.

Note how this enables compilers to issue a diagnostic when the
compiler can prove (or strongly suspect) B violations.

The current situation enables people to legally write

template<class T> void error(T const & t) throw()
{
throw t;
}

and the compiler isn't allowed to interfere. (example by Greg Colvin.)
If you (general you) don't know what the code does, this isn't a bug
in your C++ knowledge, it's a feature.

1) and 2) aren't worth the effort. Classic example (NaNs etc not
considered, illustration only):

double sqrt(double) throw(domain_error);

double hypot(double a, double b) throw()
{
return sqrt(a * a + b * b);
}

To make this 'static ES correct' you need to wrap the body of hypot in
a try/catch block:

double hypot(double a, double b) throw()
{
try {
return sqrt(a * a + b * b);
} catch(...) {
unexpected();
}
}

which - surprise - is exactly what the current throw() does. Except
that it does this automatically.

2) is not worth the effort, either. To make it work you basically need
to repeat the template body in the exception specification.

Dave Harris

unread,
Feb 16, 2002, 8:07:58 AM2/16/02
to
jco...@taeus.com (Jerry Coffin) wrote (abridged):

> > It should be reasonable to use exception specifications where you are
> > sure you have not made a mistake.
>
> [...] Just for an obvious example, consider what happens when/if

> the user installs a handler for some implementation-defined signal,
> and the signal handler throws an exception.

So the exception can be thrown during stack unwinding. This sounds like a
very bad design to me, worse than throwing exceptions from destructors and
for much the same reason.

Exceptions thrown during signal handing need careful treatment. The signal
handler is the place to put any necessary overhead, not the no-throw
functions. There is no need to optimise bad designs.

Routines called during stack unwinding are similar to no-throw routines.
We are not scared of stack unwinding, so we shouldn't be scared of
no-throw routines either.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

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

Herb Sutter

unread,
Feb 16, 2002, 9:37:09 AM2/16/02
to
On 15 Feb 2002 10:19:51 -0500, "Sergey P. Derevyago"
<non-ex...@iobox.com> wrote:

>Herb Sutter wrote:
>> >1. Recap: Briefly define the Abrahams exception safety guarantees
>> > (basic, strong, and nothrow).
>>
>> The basic guarantee is that failed operations may alter program state,
>> but no leaks occur and affected objects/modules are still destructible
>> and usable, in a consistent (but not necessarily predictable) state.
> IMHO from the practical point of view, "unpredictable" is (virtually) equal
>to "unusable". So it would be enough to guarantee that "failed object" can be:
> - destructed
> - assigned from
> - copied
>I.e. the basic guarantee guarantees not to break the basic object invariants.
>That's all :)

Right. That's what "consistent (but not necessarily predictable)" means.
It's still inspectable. Example: If the operation fails you may not know
whether the container had an element added, but if you care you can find out
by thereafter asking .size().

Herb

---
Herb Sutter (www.gotw.ca)

Secretary, ISO WG21/ANSI J16 (C++) standards committee (www.gotw.ca/iso)
Contributing Editor, C/C++ Users Journal (www.cuj.com)

Check out "THE C++ Seminar" - Boston, March 2002 (www.gotw.ca/cpp_seminar)

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

Herb Sutter

unread,
Feb 16, 2002, 9:40:33 AM2/16/02
to
On 15 Feb 2002 12:15:41 -0500, "Daniel T." <notda...@gte.net> wrote:
>>For more, see for example the Boost exception specification rationale
>>available via http://www.gotw.ca/xc++s/boost_es.htm (it summarizes to
>>"Don't!").
>
>(I got a "Not Found" error on the web page above.)

Oops! Sorry, I forgot "/publications". It's actually:

>>available via http://www.gotw.ca/publications/xc++s/boost_es.htm

Thanks,

Herb

---
Herb Sutter (www.gotw.ca)

Secretary, ISO WG21/ANSI J16 (C++) standards committee (www.gotw.ca/iso)
Contributing Editor, C/C++ Users Journal (www.cuj.com)

Check out "THE C++ Seminar" - Boston, March 2002 (www.gotw.ca/cpp_seminar)

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

Herb Sutter

unread,
Feb 16, 2002, 9:43:58 AM2/16/02
to
On 15 Feb 2002 13:00:24 -0500, "David Abrahams" <david.a...@rcn.com>
wrote:

>
>"Herb Sutter" <hsu...@acm.org> wrote in message
>news:dlgo6us8bn5tjjlak...@4ax.com...
>>
>> For more, see for example the Boost exception specification rationale
>> available via http://www.gotw.ca/xc++s/boost_es.htm (it summarizes to
>> "Don't!").

Again, that URL should have been:
http://www.gotw.ca/publications/xc++s/boost_es.htm

Just to clarify my intent in providing my own wrapper URL: I am now
deliberately wrapping all URLs I publish in articles and books. Outside URLs
are not under my control and some webmasters too often decide to change
them, resulting in a barrage of email to me every time it happens, until I
can publish a correction in a future printing (which doesn't help with
articles, which have no future printings, and for the older printings of the
books, which stay printed). For example, that happened with your great
exception paper whose URL I published in my first book and which has since
moved; ditto for the oma.com site which morphed to objectmentor.com.

The only sane way I can manage such uncontrollable(-by-me) change is to wrap
every URL I reference with a forwarding stub -- then I can fix it once at
the first complaint and get no more until the next time, and the old
book/article is still correct as published. The "one level of indirection"
that solves many CS problems...

Herb

---
Herb Sutter (www.gotw.ca)

Secretary, ISO WG21/ANSI J16 (C++) standards committee (www.gotw.ca/iso)
Contributing Editor, C/C++ Users Journal (www.cuj.com)

Check out "THE C++ Seminar" - Boston, March 2002 (www.gotw.ca/cpp_seminar)

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

Peter Dimov

unread,
Feb 16, 2002, 12:03:00 PM2/16/02
to
bran...@cix.co.uk (Dave Harris) wrote in message
news:<memo.20020215...@brangdon.madasafish.com>...

> If most exception specifications are trivially met, it will be reasonable
> for compilers to warn if they cannot prove, at compile time, they are met.
> Thus we can gradually move towards *static* constraints on exceptions
> thrown. This will be valuable for correctness.

Why?

The underlying assumption is that code that knows what exceptions can
be thrown by f is somehow safer than code that doesn't. How, exactly,
does this follow? I assert that the opposite is true, and that code
that depends on this knowledge is less safe. The correctness of the
code should not depend on the type of the exception being thrown by f.

Note that throw() specifications are a special case because they
happen to match an exception safety guarantee.

Francis Glassborow

unread,
Feb 16, 2002, 3:47:15 PM2/16/02
to
In article <7dc3b1ea.02021...@posting.google.com>, Peter
Dimov <pdi...@mmltd.net> writes

>To make this 'static ES correct' you need to wrap the body of hypot in
>a try/catch block:
>
>double hypot(double a, double b) throw()
>{
> try {
> return sqrt(a * a + b * b);
> } catch(...) {
> unexpected();
> }
>}
>
>which - surprise - is exactly what the current throw() does. Except
>that it does this automatically.

and is still broken unless unexpected has a throw() spec which it
obviously cannot have otherwise it could not work in all the ways it is
designed to.

Part of the process of making ES statically checkable would be to design
mechanisms to deal with that. Perhaps by providing a mechanism whereby a
programmer can take full responsibility for not throwing. Without such,
it will remain impossible to design adequate static tools. As
std::nothrow is already in the language perhaps:

double hypot(double a, double b) std::nothrow;
might be a usable syntax with any exception propagated from hypot being
undefined behaviour.

I am firmly of the opinion that runtime enforcement is simply
unacceptable in real programs and static enforcement needs more support.

--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

Sergey P. Derevyago

unread,
Feb 16, 2002, 3:48:04 PM2/16/02
to
Herb Sutter wrote:
> >I.e. the basic guarantee guarantees not to break the basic object
invariants.
> >That's all :)
> Right. That's what "consistent (but not necessarily predictable)" means.
> It's still inspectable. Example: If the operation fails you may not know
> whether the container had an element added, but if you care you can find out
> by thereafter asking .size().
However, the container can also be truncated. I.e. the basic guarantee can
invalidate even implicit (or relative) iterators:

typedef vector<SomeClass> vecsc;

void broken(vecsc& v, SomeClass obj)
{
vecsc::iterator it=v.begin(); // provided v.size()>0

v.push_back(obj);
obj=*it; // error: "it" can be invalidated by push_back
}

void alsobroken(vecsc& v, SomeClass obj)
{
int i=0; // provided v.size()>0
try {
v.push_back(obj);
obj=v[i]; // ok: implicit iterator "i" is valid (it's relative to v)
}
catch (...) {
obj=v[i]; // error: "i" can be invalidated by push_back
// because push_back has the basic guarantee
// and is allowed to truncate the container


}
}
--
With all respect, Sergey. http://cpp3.virtualave.net/
mailto : ders at skeptik.net

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

Jerry Coffin

unread,
Feb 16, 2002, 6:23:16 PM2/16/02
to
In article <memo.20020216...@brangdon.madasafish.com>,
bran...@cix.co.uk says...

> jco...@taeus.com (Jerry Coffin) wrote (abridged):
> > > It should be reasonable to use exception specifications where you are
> > > sure you have not made a mistake.
> >
> > [...] Just for an obvious example, consider what happens when/if
> > the user installs a handler for some implementation-defined signal,
> > and the signal handler throws an exception.
>
> So the exception can be thrown during stack unwinding. This sounds like a
> very bad design to me, worse than throwing exceptions from destructors and
> for much the same reason.

Yes and no -- I agree that it's far from optimal. OTOH, consider
that signals ARE fundamentally a (rudimentary) exception handling
mechanism. As such, if somebody decides to consolidate the actions
of these two similar but separate mechanisms into one, I'd have to
say that I approve of at least the general idea.

Now, as you've said (and I agree) handling signals in this way _can_
lead to complications, some of which might be problematic or even
disastrous. OTOH, NOT handling them in such a way is likely to do so
even _more_ often.

> Exceptions thrown during signal handing need careful treatment. The signal
> handler is the place to put any necessary overhead, not the no-throw
> functions. There is no need to optimise bad designs.

There may be no need to optimize bad designs, but there IS a need to
keep from turning a design that's at least somewhat reasonable into
an unmitigated disaster.



> Routines called during stack unwinding are similar to no-throw routines.
> We are not scared of stack unwinding, so we shouldn't be scared of
> no-throw routines either.

You're getting things wrong: no-throw routines are not the problem:
the design of exception specifications IS. We can design no-throw
routines, and indeed I've done so, without fear. One of the primary
reasons for exception handling, however, is that it allows low-level
routines report problems withOUT imposing major policy limitations on
the rest of the design. Exception specifications undo all that good,
and impose even more Draconian policy throughout the entire rest of a
design.

--
Later,
Jerry.

The Universe is a figment of its own imagination.

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

Peter Dimov

unread,
Feb 16, 2002, 6:26:51 PM2/16/02
to
tere...@web.de (Alexander Terekhov) wrote in message news:<c29b5e33.02021...@posting.google.com>...

> But the following "reimplemention" WITH exception specification
> is MUCH better[1] (if you do NOT want to cope with UNDOCUMENTED
> exceptions which could be thrown by some "component" you do
> not "trust"; perhaps even loaded dynamically as "plug-in"):

If you don't trust a component, you won't let it terminate() you at will.

> void f_() throw(std::runtime_error,std::bad_cast) { f(); }
>
> void g() { /**/ f_(); /**/ };

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

Alexander Terekhov

unread,
Feb 16, 2002, 6:27:12 PM2/16/02
to
pdi...@mmltd.net (Peter Dimov) wrote in message news:<7dc3b1ea.02021...@posting.google.com>...
[...]

> double sqrt(double) throw(domain_error);
>
> double hypot(double a, double b) throw()
> {
> return sqrt(a * a + b * b);
> }
>
> To make this 'static ES correct' you need to wrap the body of hypot in
> a try/catch block:

Personally, I would really prefer something along the lines of
"unexpected" pragma!

> double hypot(double a, double b) throw()
> {
> try {
> return sqrt(a * a + b * b);
> } catch(...) {
> unexpected();
> }
> }
>
> which - surprise - is exactly what the current throw() does.

NO! Gee!! Peter, please run this:
(on some of your lovely MSVC-boxes *first*)

#include <cstdlib>
#include <iostream>
#include <exception>

using namespace std;

void my_terminate()
{ cout << "OOPS: bow out!..."
"HELLO my dear debugger/dump-analyzer" << endl; abort(); }

struct C_ { ~C_() { cout << "C_-dtor" << endl; } };

void C() { C_ c; throw "Trouble"; }

struct B_ { ~B_() { cout << "B_-dtor" << endl; } };

void B() { B_ b; C(); }

void A() throw() { B(); }

int main()
{
set_terminate( my_terminate );
A();
return 0;
}

Do you see the point?

regards,
alexander.

Alexander Terekhov

unread,
Feb 17, 2002, 7:32:02 AM2/17/02
to
pdi...@mmltd.net (Peter Dimov) wrote in message news:<7dc3b1ea.0202...@posting.google.com>...

> tere...@web.de (Alexander Terekhov) wrote in message news:<c29b5e33.02021...@posting.google.com>...
> > But the following "reimplemention" WITH exception specification
> > is MUCH better[1] (if you do NOT want to cope with UNDOCUMENTED
> > exceptions which could be thrown by some "component" you do
> > not "trust"; perhaps even loaded dynamically as "plug-in"):
>
> If you don't trust a component, you won't let it terminate() you at will.

http://www.ibm.com/servers/eserver/zseries/software/sa/pdf/ingpie02.pdf
(see "Recovery Concept for the Automation Manager"...
"StateQueue/Takeover file" is *C++ objects store*,
updated incrementally WITH transactional semantics.
It works even with system BLOWUPs/LOSSOFPOWER/...
so, yes, I do "let it terminate() you at will")

Also, consider:

"-a takeover resulting in the same problem

When the new automation manager detects that
a workitem has been rolled back twice, the
process will be stopped and a WARM initialization
will be triggered, thus preventing endless retries
failing with the same persistent problem."

regards,
alexander.

Luis Pedro Coelho

unread,
Feb 17, 2002, 7:34:29 AM2/17/02
to
Dave Harris wrote:

>
> If most exception specifications are trivially met, it will be reasonable
> for compilers to warn if they cannot prove, at compile time, they are met.

> Thus we can gradually move towards static constraints on exceptions


> thrown. This will be valuable for correctness.

Consider:

unsigned max_of(const std::vector<unsigned>& vec) throw()
{
typedef std::vector<unsigned>::size_type st;
const st len = vec.size();
unsigned res = 0;
for (st i = 0; i != len; ++i)
{
if (res < vec.at(i)) // vector::at can throw !!
res = vec.at(i);
}
return res;
}

I know that this function will not throw. However, how can the compiler
know this?

IMHO this is the killer argument against the static checking of exceptions.

(There is a loophole in the above example. One can argue that a smart
enough compiler will know the semantics of std::vector<T>::at and so it
will be possible for it to know that the above function is correct.
However: 1. It would be a *very* smart compiler and 2. The above example
could be rewritten to use a user defined type.)

Regards,
--
Luis Pedro Coelho.

Check out my game of Hearts, a card game, for KDE at:
http://hearts.sourceforge.net/

Dave Harris

unread,
Feb 17, 2002, 1:12:03 PM2/17/02
to
pdi...@mmltd.net (Peter Dimov) wrote (abridged):

> > Thus we can gradually move towards *static* constraints on
> > exceptions thrown. This will be valuable for correctness.
>
> Why?
> [...]

> Note that throw() specifications are a special case because they
> happen to match an exception safety guarantee.

That special case is the one I have in mind. I don't personally care about
support for non-empty exception specifications.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

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

Sergey P. Derevyago

unread,
Feb 17, 2002, 1:13:04 PM2/17/02
to
Luis Pedro Coelho wrote:
> > If most exception specifications are trivially met, it will be reasonable
> > for compilers to warn if they cannot prove, at compile time, they are
met.
> > Thus we can gradually move towards static constraints on exceptions
> > thrown. This will be valuable for correctness.
> Consider:
> unsigned max_of(const std::vector<unsigned>& vec) throw()
> {
> typedef std::vector<unsigned>::size_type st;
> const st len = vec.size();
> unsigned res = 0;
> for (st i = 0; i != len; ++i)
> {
> if (res < vec.at(i)) // vector::at can throw !!
> res = vec.at(i);
> }
> return res;
> }
> I know that this function will not throw.
In principle, it can:
1. Some other thread can (concurrently) truncate vec.
2. vec::size() and/or vec::at() can contain an error (the real problem of
realworld UDTs).

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

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

Dave Harris

unread,
Feb 17, 2002, 1:13:43 PM2/17/02
to
jco...@taeus.com (Jerry Coffin) wrote (abridged):
> You're getting things wrong: no-throw routines are not the problem:
> the design of exception specifications IS. We can design no-throw
> routines, and indeed I've done so, without fear. One of the primary
> reasons for exception handling, however, is that it allows low-level
> routines report problems withOUT imposing major policy limitations on
> the rest of the design. Exception specifications undo all that good,
> and impose even more Draconian policy throughout the entire rest of a
> design.

I'm sorry, but I don't follow this. It seems to me that if we have a
design that relies on non-throwing primitives for its integrity, and we
also have signal handlers which can inject a throw at any point in the
code, then we have a broken design. It is broken whether or not we use
exception specifications.

In other words, no-throw routines /are/ the problem. They are the policy
limitation. The exception specifications are merely documenting and
enforcing that policy.

I must be missing a step in your logic.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

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

Dave Harris

unread,
Feb 17, 2002, 1:17:44 PM2/17/02
to
luis_...@netcabo.pt (Luis Pedro Coelho) wrote (abridged):

> I know that this function will not throw. However, how can the compiler
> know this?

It can't. I think it is reasonable to get a warning from such code. It
uses throwing operations like at() and then adds the empty exception
specification. The code is a bit self-contradictory. What was the author
thinking?

I would change it in one of three ways. First, by removing the empty
exception specification. It's best to keep no-throw code simple, and your
example is quite complicated.

Second, rewrite it in terms of no-throw operations. In effect, use
operator[]() instead of at(), so that out-of-range indexes become
undefined behaviour instead of exceptions. After all, you seem pretty
confident that range errors won't occur. This might mean substituting a
user-defined class, unless vector is changed to add empty exception
specifications to size() and operator[]().

Static exception specifications are like static const-correctness. They
should go all the way down. Code should be used in no-throw routines only
if it is /designed/ to be used in no-throw routines. In which case, the
fact should be made explicit with an empty exception specification.

Thirdly, we could add a try/catch block:

unsigned max_of(const std::vector<unsigned>& vec) throw() {

try {


typedef std::vector<unsigned>::size_type st;
const st len = vec.size();
unsigned res = 0;
for (st i = 0; i != len; ++i)
if (res < vec.at(i)) // vector::at can throw !!
res = vec.at(i);
return res;
}

catch (...) {
assert( false ); // Cannot be reached.
return 0;
}
}

This is almost the moral equivalent of a const-cast. I say "almost"
because a const-cast typically has no overhead. The overhead for an
exception handler which is never executed is implementation dependent, but
can be very close to zero if not zero.

Perhaps we need an exception_cast<> to express the idea better?


> IMHO this is the killer argument against the static checking of
> exceptions.

Do you agree that exceptions should not be checked dynamically, either? If
so, I presume you never use exception specifications at all. In which
case, this debate doesn't effect you much.

Admittedly you will find it harder to interoperate with people who do use
static exception specifications. Fortunately, only very simple and limited
code should have the no-throw guarantee. Exception specifications are not
as all-pervading as const-correctness.

Basically, all static type/correctness checking leads to tighter coupling
and constraints. The killer argument can always be made against it. I'm
not saying the argument is always wrong, but it's not always right, either
(else we'd all be using dynamically checked languages like Smalltalk). On
this occasion, I think it loses.

I think exception safety is both important and hard, and the restrictions
are relatively minor because a relatively small amount of code is
exception-critical. So I think the benefits of static checking are worth
the costs.


> (There is a loophole in the above example. One can argue that a smart
> enough compiler will know the semantics of std::vector<T>::at and so it
> will be possible for it to know that the above function is correct.
> However: 1. It would be a *very* smart compiler and 2. The above example
> could be rewritten to use a user defined type.)

I agree, I'd rather the compiler did not have as much discretion as that.
I'd support full-on errors instead of mere warnings; I am only advocating
warnings to ease the transition, and because it can be done now, without a
language change. Mandated full-on errors cannot depend on how smart the
compiler is.

On this occasion, using a user-defined type makes life easier because we
can give it the necessary no-throw operations.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

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

Mary K. Kuhner

unread,
Feb 17, 2002, 1:19:01 PM2/17/02
to
Peter Dimov <pdi...@mmltd.net> wrote:

>The underlying assumption is that code that knows what exceptions can
>be thrown by f is somehow safer than code that doesn't. How, exactly,
>does this follow? I assert that the opposite is true, and that code
>that depends on this knowledge is less safe. The correctness of the
>code should not depend on the type of the exception being thrown by f.

This is speculative--I am going to have to write such code someday,
but not yet--but it seems to me that knowing which parts of my
program can throw std::bad_alloc and which can't could make a large
difference.

One of these days, my program is going to have to contain recovery
code for running out of memory--it should save its work in progress so
that the user can do something about the memory situation and then
recover, rather than having to start a week-long run again from
scratch. The recovery code will presumably involve reaching the
nearest consistent state and then printing intermediate results to
file. Having a pre-allocated block of unused memory around will
probably be useful.

I'd really like to know for sure that routines A and B can throw
bad_alloc but routines C and D definitely don't. The
recovery code has to be hand-crafted for each throwing routine,
so putting catch(bad_alloc) everywhere, even where it's not
needed, would be a huge waste of effort.

Doing this recovery work for errors other than bad_alloc would be
wrong. So far, at least, the other exceptions this program throws
all indicate some kind of bad failure internally. It wouldn't be
any use saving the intermediate state because it's probably
corrupt. bad_alloc, however, does not necessarily indicate a
program flaw, and there's a hope of recovery.

Mary Kuhner mkku...@genetics.washington.edu

Francis Glassborow

unread,
Feb 17, 2002, 1:19:44 PM2/17/02
to
In article <a4neba$t54$1...@venus.telepac.pt>, Luis Pedro Coelho
<luis_...@netcabo.pt> writes

>I know that this function will not throw. However, how can the compiler
>know this?
>
>IMHO this is the killer argument against the static checking of exceptions.

No, it is the reason that we need to revisit the mechanism. Aliasing is
not a killer argument against using pointers but it is a good reason for
considering how we can tell a compiler that two pointers will be to
different objects at run time (that is the idea behind C's restrict
keyword). In the same way we need a way to tell the compiler that the
programmer 'guarantees' that a function will not throw, under pain of
undefined behaviour if that promise is broken. I have already posted an
idea for providing such in the next release of C++. Perhaps we should be
focusing on how to achieve static enforcement rather than throwing our
hands up and declaring that it cannot be done.

--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

Daniel T.

unread,
Feb 17, 2002, 6:09:34 PM2/17/02
to
In article <memo.20020217...@brangdon.madasafish.com>,
bran...@cix.co.uk (Dave Harris) wrote:

> pdi...@mmltd.net (Peter Dimov) wrote (abridged):
> > > Thus we can gradually move towards *static* constraints on
> > > exceptions thrown. This will be valuable for correctness.
> >
> > Why?
> > [...]
> > Note that throw() specifications are a special case because they
> > happen to match an exception safety guarantee.
>
> That special case is the one I have in mind. I don't personally care about
> support for non-empty exception specifications.

>From what I've read, those in the Java world are making the same
realization. What's important (and should be checked staticly) is
whether a function throws or not. If it throws, the exact type of
exception is of little concequence to the interface specification.

Matthew Austern

unread,
Feb 17, 2002, 6:10:03 PM2/17/02
to

> Doing this recovery work for errors other than bad_alloc would be
> wrong. So far, at least, the other exceptions this program throws
> all indicate some kind of bad failure internally. It wouldn't be
> any use saving the intermediate state because it's probably
> corrupt. bad_alloc, however, does not necessarily indicate a
> program flaw, and there's a hope of recovery.

What about running out of file handles or sockets, or
getting an I/O exception that may indicate a full disk?

Jerry Coffin

unread,
Feb 17, 2002, 6:10:40 PM2/17/02
to
In article <memo.20020217...@brangdon.madasafish.com>,
bran...@cix.co.uk says...

[ ... ]

> I'm sorry, but I don't follow this. It seems to me that if we have a
> design that relies on non-throwing primitives for its integrity, and we
> also have signal handlers which can inject a throw at any point in the
> code, then we have a broken design. It is broken whether or not we use
> exception specifications.

Yes and no. The problem is that we can't (for example) simply tell
the compiler to ignore exception specifications that we know are
wrong, and that aren't necessary in a particular situation.

For example, assume I'm writing a program and I use somebody's matrix
manipulation library. My code is prepared to deal with a number of
exceptions that might arise, some of which might happen to arise
during the execution of the library code. Unfortunately, the idiot
who wrote the library was discourteous enough to include exception
specifications in his code -- he "documented" the exceptions he knew
his code could trigger, and assumed that meant nothing else could
arise during its execution.

Unfortunately, he was wrong, and now we have a BAD situation: my
code's prepared to handle the exceptions, but the exception
specifications prevent me from doing so -- before I receive the
exception, the system says it was unexpected, and the program is
terminated.

The problem is ultimately quite simple, especially if you think in
terms of design by contract: an exception specification is part of
the contract to be fulfilled by a piece of code. That's fine in
itself, except for one thing: the code absolutely, positively canNOT
actually keep that promise on its own -- it can only be certain of
fulfilling the promise with full knowledge of the rest of the system.
That's tight coupling throughout the ENTIRE system, and it's a bad
thing.

--
Later,
Jerry.

The Universe is a figment of its own imagination.

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

Luis Pedro Coelho

unread,
Feb 18, 2002, 6:20:36 AM2/18/02
to
Dave Harris wrote:

> luis_...@netcabo.pt (Luis Pedro Coelho) wrote (abridged):
>> I know that this function will not throw. However, how can the compiler
>> know this?
>
> It can't. I think it is reasonable to get a warning from such code. It
> uses throwing operations like at() and then adds the empty exception
> specification. The code is a bit self-contradictory. What was the author
> thinking?
>
> I would change it in one of three ways. First, by removing the empty
> exception specification. It's best to keep no-throw code simple, and your
> example is quite complicated.

Why remove the empty exception spec? Maybe this code would do something
which is good to have as a no-throw.

> Second, rewrite it in terms of no-throw operations. In effect, use
> operator[]() instead of at(), so that out-of-range indexes become
> undefined behaviour instead of exceptions. After all, you seem pretty
> confident that range errors won't occur. This might mean substituting a
> user-defined class, unless vector is changed to add empty exception
> specifications to size() and operator[]().

Would you rewrite std::vector just for this? What if it was something even
more complicated.

> Static exception specifications are like static const-correctness. They
> should go all the way down. Code should be used in no-throw routines only
> if it is /designed/ to be used in no-throw routines. In which case, the
> fact should be made explicit with an empty exception specification.

I think there is a valid point here. However, I don't believe it applies.
std::vector::at is designed to throw an exception in case its argument is
out of range, even std::vector::operator[] can do this as can any operation
on std::vector::iterator or std::vector::const_iterator under the banner of
undefined behaviour. Are you saying I shouldn't use any of them? I say I
should be able to use them if I know what I am doing.

> Thirdly, we could add a try/catch block:
>
> unsigned max_of(const std::vector<unsigned>& vec) throw() {
> try {
> typedef std::vector<unsigned>::size_type st;
> const st len = vec.size();
> unsigned res = 0;
> for (st i = 0; i != len; ++i)
> if (res < vec.at(i)) // vector::at can throw !!
> res = vec.at(i);
> return res;
> }
> catch (...) {
> assert( false ); // Cannot be reached.
> return 0;
> }
> }
>
> This is almost the moral equivalent of a const-cast. I say "almost"
> because a const-cast typically has no overhead. The overhead for an
> exception handler which is never executed is implementation dependent, but
> can be very close to zero if not zero.

That is almost exactly what the compiler generates now.
s/assert(false)/unexpected()/.

> Perhaps we need an exception_cast<> to express the idea better?

Yes, something like that might be necessary. I think Francis Glassborow was
hiting at something similar in his reply to my post. I think that the
answer to the problem might lie in that direction.

If we move towards static checking, then I think we need someway to tell
the compiler:

"This function is declared as though it might throw A, B or C. However I,
the programmer, know that it can only throw B (or even nothing)."

If that is inplace, then I would have to take back what I said.

Regards,
--
Luis Pedro Coelho.

Check out my game of Hearts, a card game, for KDE at:
http://hearts.sourceforge.net/

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

Mary K. Kuhner

unread,
Feb 18, 2002, 6:22:33 AM2/18/02
to
Matthew Austern <aus...@research.att.com> wrote:
>mkku...@kingman.genetics.washington.edu (Mary K. Kuhner) writes:

>> Doing this recovery work for errors other than bad_alloc would be
>> wrong. So far, at least, the other exceptions this program throws
>> all indicate some kind of bad failure internally. It wouldn't be
>> any use saving the intermediate state because it's probably
>> corrupt. bad_alloc, however, does not necessarily indicate a
>> program flaw, and there's a hope of recovery.

>What about running out of file handles or sockets, or
>getting an I/O exception that may indicate a full disk?

I don't think I'm clever enough to recover from these; I don't
know what to do if I can't write the intermediate data to disk.
The program is often run in background, so "ask the user for
help and wait until it arrives" is not a viable solution. (I
had some code like that, and once let the program sit around
idle for over a week because I hadn't realized it was waiting
for me to do something.)

In the case of bad_alloc I can use some pre-saved space to try
a recovery. I suppose the program could avoid file handle
problems in mid-run by opening all its files at startup, and
thus never get such an exception later. I don't know what to
do about the full disk. The program's output is quite
voluminous, and it is meant to be highly portable--tricks like
trying to write to a different disk might work on Unix but
they won't be portable.

In any case, I do think it's a situation where knowing exactly
which code can throw bad_alloc and which can't could be useful
to me. I'm not, however, at all convinced that exception
specs are the way to go; a lint-like exception checker might
be a much better tool.

The program currently has exception specs in only one place,
and that's forced on me by inheriting from a library class that
has them on its virtual functions. Otherwise I rely on
"catch(...)" in main to tell me if I am throwing something I
didn't expect; it seems to work just the same as a violated
exception spec.

Mary Kuhner mkku...@genetics.washington.edu

Alexander Terekhov

unread,
Feb 18, 2002, 6:25:55 AM2/18/02
to
Jerry Coffin wrote:
[...]

> For example, assume I'm writing a program and I use somebody's matrix
> manipulation library. My code is prepared to deal with a number of
> exceptions that might arise, some of which might happen to arise
> during the execution of the library code. Unfortunately, the idiot
> who wrote the library was discourteous enough to include exception
> specifications in his code -- he "documented" the exceptions he knew
> his code could trigger, and assumed that meant nothing else could
> arise during its execution.

Or perhaps "the idiot" just assumed that anything else is
the indication of BROKEN state of his data (or things like
that), and just wanted to *prevent* YOU (his *clever* client
programmer) from catching those silly (really, really
*unexpected*) exceptions (using something along the lines
of silly catch(...) {} handlers) that would severely
impact/limit HIS (i.e. "the idiot") ability to SERVICE
*you* -- fix/debug/analyze the problems based on the
information/program state[1] at those unexpected THROW
points.

Current ES is NOT a documenting tool that you should
apply to your arbitrary functions. This "problem"
aside, they are just GREAT, IMHO!

regards,
alexander.

[1] For example:
http://www.tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51_HTML/ARH9RBTE/DOCU0008.HTM#dt_errors

"The Threads Library internal errors result in a bugcheck.
The Threads Library writes a message that summarizes the
problem to the process' current error device, and (on
OpenVMS) writes a file that contains more detailed
information. (On Tru64 UNIX systems, the core file is
sufficient for analysis of the process using the Ladebug
debugger.)
...
If you encounter a bugcheck, first check your application
for memory corruptions, calls from AST routines, etc., and
then contact your Compaq support representative and include
this information file (or the Tru64 UNIX core file) along
with sample code and output. Always include the full name
and version of the operating system, and any patches that
have been installed. If complete version information is
lacking, useful core file analysis might not be possible."

James Kanze

unread,
Feb 18, 2002, 6:33:59 AM2/18/02
to
pdi...@mmltd.net (Peter Dimov) wrote in message
news:<7dc3b1ea.02021...@posting.google.com>...

> Francis Glassborow <francis.g...@ntlworld.com> wrote in
> message news:<u8EuopBy...@robinton.ntlworld.com>...
> > Currently we have the worst of all worlds. ES is not statically
> > checked, and because most code does not provide it, there is no
> > motivation to provide a supplementary tool to do so. The
> > execution time problems encourage programmers to avoid use of ES.

> No, not exactly. In fact, I may well go ahead and claim that what we
> have now is significantly better than some attempts to "fix" the
> status quo by adding static ES checking.

May I ask what actual experience you have in large scale projects
using statically checked exception specifications. Because everyone
else I've talked to who has actually used them swears by them.

> > I think we need to reformulate ES in the core of the language so that:

> > 1) It is statically checked. Yes I know that can cause problems, but,
> > pragmatically, compilers will provide a switch to suppress this.

> > 1A) Make violation undefined behaviour.

> > 2) Mechanisms to allow their use in template environments.

> > 3) Removal of execution time checking

[...]

> 1) and 2) aren't worth the effort. Classic example (NaNs etc not
> considered, illustration only):

> double sqrt(double) throw(domain_error);

> double hypot(double a, double b) throw()
> {
> return sqrt(a * a + b * b);
> }

> To make this 'static ES correct' you need to wrap the body of hypot
> in a try/catch block:

So?

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

James Kanze

unread,
Feb 18, 2002, 6:35:19 AM2/18/02
to
Attila Feher <Attila...@lmf.ericsson.se> wrote in message
news:<3C6D3356...@lmf.ericsson.se>...
> C Nick Beaudrot wrote:
> [SNIP]
> > I'm just curious ... is there any empircal evidence of this one? Or is
> > this just one of those things that people have started assuming, like
> ...
> > that COW strings are a pessimization in a
> > multithreaded environment...

> Khm. Khm. Using Forte C++ and _not_ hitting the COW problem? I
> mean I have seen processes not being able to make more than 23% CPU
> load on a 4CPU system (that is about 12% on an 8 CPU, ~6% on a
> 16CPU), because the threads were creating and destrying strings
> (implementations), which has lead to a state, where they were
> waiting for mutex_create/destroy_for each other: basically rendering
> any SMP to a bit slower than a single CPU system. How can one avoid
> that (without changing the Rogue Wave STL and still using strings)?
> I mean 60% of the CPU time was spent in pthread_mutex_create and
> destroy...

Which, of course, doesn't mean that COW per se is the problem, but the
implementation of COW that you are using is:-). From what I
understand, Sparc is one of the architectures where an efficient COW
is theoretically possible. Of the two COW implementations I know of
on the machine, however, one doesn't work, and the other you've just
described:-). So we seem to have a divergence between theory and
practice.

In the end, I think the arguments for or against COW are generally
overstated, and more importantly, they are probably irrelevant for
anyone NOT implementing a standard library. In your case, for
example, the problem isn't COW per se, it is the performance of the
library. Using another library (STLPort, etc.) might solve the
problem. In the end, of course, you don't care whether the string is
COW or not, as long as it is fast enough, and when the G++ library
finally works in a multithreaded environment on Sparc, it might be
fast enough, while still being COW. (Or it might end up not being
COW, depending on the resources available to the developpers and their
priorities.)

For you own code, of course, the first implementation should never be
COW (too difficult to get right). If profiling later shows a problem,
you experiment. If COW is the solution, you use it. If it isn't, you
don't.

James Kanze

unread,
Feb 18, 2002, 6:36:00 AM2/18/02
to
Herb Sutter <hsu...@acm.org> wrote in message
news:<niar6uoep3qi31u2d...@4ax.com>...
> <non-ex...@iobox.com> wrote:

> >Herb Sutter wrote:
> >> >1. Recap: Briefly define the Abrahams exception safety guarantees
> >> > (basic, strong, and nothrow).

> >> The basic guarantee is that failed operations may alter program state,
> >> but no leaks occur and affected objects/modules are still destructible
> >> and usable, in a consistent (but not necessarily predictable) state.
> > IMHO from the practical point of view, "unpredictable" is
> >(virtually) equal to "unusable". So it would be enough to guarantee
> >that "failed object" can be:

> > - destructed
> > - assigned from
> > - copied
> >I.e. the basic guarantee guarantees not to break the basic object
> >invariants. That's all :)

> Right. That's what "consistent (but not necessarily predictable)"
> means. It's still inspectable. Example: If the operation fails you
> may not know whether the container had an element added, but if you
> care you can find out by thereafter asking .size().

I'm not even sure that the basic guarantee means that much (although I
suppose that it depends on what you mean by inspectable). At the
limit, all that I can do with the object is destruct it. For
example, if a container class only made the basic guarantee, I would
not count on .size() returning a significant value -- it might return
the number of elements currently in the container, or it might not.
Even if it did return the correct number of elements, I wouldn't count
on their values being anything reasonable.

On the other hand, if the object contains a pointer, and the
destructor executes a delete on that pointer, then even after an
exception, that pointer is either null, or points to memory allocated
with an operator new. It can't just take a random value.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 18, 2002, 6:37:21 AM2/18/02
to
"Sergey P. Derevyago" <non-ex...@iobox.com> wrote in message
news:<3C6E794E...@iobox.com>...

> Herb Sutter wrote:
> > >I.e. the basic guarantee guarantees not to break the basic object
> > >invariants. That's all :)

> > Right. That's what "consistent (but not necessarily predictable)"
> > means. It's still inspectable. Example: If the operation fails
> > you may not know whether the container had an element added, but
> > if you care you can find out by thereafter asking .size().

> However, the container can also be truncated. I.e. the basic
> guarantee can invalidate even implicit (or relative) iterators:

Interesting. You've just raised two points I hadn't considered before.

- When we talk of exception safety, we've always been concerned
about the class where the exception was thrown. As you correctly
point out, however, an exception in one class can have
implications on other classes. I would suppose that if the class
where the exception is thrown offers the strong guarantee, this
would apply equally to all related classes. But I think it worth
mentionning that the weak guarantee may mean that related classes
are no longer usable as well.

- When we talk of invalidating an iterator, of course, I (and I
suppose everyone else) assumes that even if the iterator is
invalid, its destructor can be called. It just seems like it
would be a good idea if this were stated somewhere.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 18, 2002, 6:38:01 AM2/18/02
to
bran...@cix.co.uk (Dave Harris) wrote in message
news:<memo.20020215...@brangdon.madasafish.com>...
> hsu...@acm.org (Herb Sutter) wrote (abridged):
> > >4. When is it worth it to write exception specifications on
> > > functions? Why would you choose to write one, or why not?

> > In brief, don't bother. Even experts don't bother.

> This is a self-fulfilling prophecy. I am not saying you're wrong,
> but I think it's worth pointing out some of the issues and
> possibilities for the future.

It also raises the question: who are the experts? I don't think many
(if any) of the C++ experts use exception specifications, but are they
experts in C++, or in software engineering. All of the Ada experts,
for example, use exception specifications in their code -- this would
argue just as strongly *for* exception specifications.

Since all of the people I've talked to who have actually used
statically enforced exception specifications (in other languages) have
been favorably impressed by the improvement they make in software
reliability, I am tentatively of the opinion that they are a good
thing. If they aren't a good thing in C++, this would suggest that
there is a problem in this regard with the language more than anything
else.

James Kanze

unread,
Feb 18, 2002, 6:39:22 AM2/18/02
to
"Daniel T." <notda...@gte.net> wrote in message
news:<notdanielt3-4168...@paloalto-snr1.gtei.net>...

> In article <memo.20020217...@brangdon.madasafish.com>,
> bran...@cix.co.uk (Dave Harris) wrote:

> > pdi...@mmltd.net (Peter Dimov) wrote (abridged):
> > > > Thus we can gradually move towards *static* constraints on
> > > > exceptions thrown. This will be valuable for correctness.

> > > Why?
> > > [...]
> > > Note that throw() specifications are a special case because they
> > > happen to match an exception safety guarantee.

> > That special case is the one I have in mind. I don't personally
> > care about support for non-empty exception specifications.

> From what I've read, those in the Java world are making the same
> realization. What's important (and should be checked staticly) is
> whether a function throws or not. If it throws, the exact type of
> exception is of little concequence to the interface specification.

I suspect that this is typically the case (although there may be
exceptions). That is probably why all exception hierarchies tend to
derive from a single class.

But I'm curious about the reference to Java. Because in Java, unlike
C++, it is impossible to specify that a function doesn't throw -- the
most useful exception specification is the one you can't state. (Of
course, functions which cannot throw are much rarer in Java than in
C++. Partially because most types must be dynamically allocated,
which opens the possibility of the Java equivalent of std::bad_alloc,
and partially because Java has much less undefined behavior --
dividing by zero throws a defined exception in Java, for example.)

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 18, 2002, 6:42:09 AM2/18/02
to
Matthew Austern <aus...@research.att.com> wrote in message
news:<dillmdr...@isolde.research.att.com>...

> mkku...@kingman.genetics.washington.edu (Mary K. Kuhner) writes:

> > Doing this recovery work for errors other than bad_alloc would be
> > wrong. So far, at least, the other exceptions this program throws
> > all indicate some kind of bad failure internally. It wouldn't be
> > any use saving the intermediate state because it's probably
> > corrupt. bad_alloc, however, does not necessarily indicate a
> > program flaw, and there's a hope of recovery.

> What about running out of file handles or sockets, or getting an I/O
> exception that may indicate a full disk?

They might not indicate a program flaw, but they are going to make it
difficult to save the current state to disk:-) (which is the recovery
action proposed for bad_alloc).

And of course, nobody has mentioned recovering from stack overflow:-).

From some of Mary's other postings, I gather her applications are of
the sort which uses very large quantities of heap memory. So
bad_alloc is a very real possibility, whereas the other errors
mentionned could conceivably occur, but are significantly less
likely. If the machine catches fire, the process will also have to be
started from scratch, but the risk is considered acceptable.

Her applications are in no way like mine (where bad_alloc almost
certainly indicates a memory leak, which in my applications is a fatal
error). But I have no problem conceiving that her needs are different
than mine, and from her description, separating bad_alloc from the
other exceptions seems reasonable, even if it would not be reasonable
in my applications.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 18, 2002, 6:42:44 AM2/18/02
to
Luis Pedro Coelho <luis_...@netcabo.pt> wrote in message
news:<a4neba$t54$1...@venus.telepac.pt>...

> Dave Harris wrote:

> > If most exception specifications are trivially met, it will be
> > reasonable for compilers to warn if they cannot prove, at compile
> > time, they are met. Thus we can gradually move towards static
> > constraints on exceptions thrown. This will be valuable for
> > correctness.

> Consider:

> unsigned max_of(const std::vector<unsigned>& vec) throw()
> {
> typedef std::vector<unsigned>::size_type st;
> const st len = vec.size();
> unsigned res = 0;
> for (st i = 0; i != len; ++i)
> {
> if (res < vec.at(i)) // vector::at can throw !!
> res = vec.at(i);
> }
> return res;
> }

> I know that this function will not throw.

Of course, if you know that the access cannot throw, you would have
written [], right?

> However, how can the compiler know this?

> IMHO this is the killer argument against the static checking of
> exceptions.

Where is the argument? If you "know" that some sequence cannot throw,
even if the exception specifications of the called functions say it
can, then you very definitly need to impart this knowledge in the
program somehow. You could use comments, but I would find something
like:

try {
// ...
} catch ( ... ) {
ProcessManager::fatalError( "unexpected exception..." ) ;
}

far more preferrable.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 18, 2002, 6:43:28 AM2/18/02
to
"Sergey P. Derevyago" <non-ex...@iobox.com> wrote in message
news:<3C6FA684...@iobox.com>...

> Luis Pedro Coelho wrote:
> > > If most exception specifications are trivially met, it will be
> > > reasonable for compilers to warn if they cannot prove, at
> > > compile time, they are met. Thus we can gradually move towards
> > > static constraints on exceptions thrown. This will be valuable
> > > for correctness.

> > Consider:
> > unsigned max_of(const std::vector<unsigned>& vec) throw()
> > {
> > typedef std::vector<unsigned>::size_type st;
> > const st len = vec.size();
> > unsigned res = 0;
> > for (st i = 0; i != len; ++i)
> > {
> > if (res < vec.at(i)) // vector::at can throw !!
> > res = vec.at(i);
> > }
> > return res;
> > }
> > I know that this function will not throw.

> In principle, it can:
> 1. Some other thread can (concurrently) truncate vec.

In which case, at might not throw, but might access uninitialized
memory anyway.

> 2. vec::size() and/or vec::at() can contain an error (the real problem of
> realworld UDTs).

The most likely scenario is that the code between the .size() and
.at() changes, and what was previously correct isn't any more. It
happens every day in real life projects. If that wasn't the case,
there would be no point in even having the function .at(). Wrapping
the calls to .at() in a try block, and raising an internal error in
the catch block, seems to me to be a necessary part of defensive
coding, which should only be suppressed if profiling shows a problem
here.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

Jean-Marc Bourguet

unread,
Feb 18, 2002, 12:39:32 PM2/18/02
to
ka...@gabi-soft.de (James Kanze) writes:

> All of the Ada experts, for example, use exception specifications in
> their code -- this would argue just as strongly *for* exception
> specifications.

As far as I know, there is so such things as exception specifications
in Ada.

Perhaps are you talking about SPARK, which is a subset of Ada with
annotations? There are quite a lot of things which are statically
checkable in SPARK, but then you are more constrained in what is
acceptable.

Yours,

--
Jean-Marc

David Abrahams

unread,
Feb 18, 2002, 12:41:53 PM2/18/02
to

"James Kanze" <ka...@gabi-soft.de> wrote in message
news:d6651fb6.02021...@posting.google.com...

> I'm not even sure that the basic guarantee means that much (although I
> suppose that it depends on what you mean by inspectable). At the
> limit, all that I can do with the object is destruct it. For
> example, if a container class only made the basic guarantee, I would
> not count on .size() returning a significant value -- it might return
> the number of elements currently in the container, or it might not.
> Even if it did return the correct number of elements, I wouldn't count
> on their values being anything reasonable.

That's an incorrect understanding of the basic guarantee. If the container
documentation says that .size() returns the number of elements in the
container, then that's what it returns, before or after an exception is
thrown.

You're right that the basic guarantee doesn't give us much, but it does give
us a bit more than you have inferred it does. For example, a container can
be clear()ed and used from there. Also, we know that its elements are
reasonable in the sense that the library can only have used the operations
given in the container's requirements for the element type. Typically, the
elements must only be Assignable, so your container<T> just contains copies
of some other T which has existed.

> On the other hand, if the object contains a pointer, and the
> destructor executes a delete on that pointer, then even after an
> exception, that pointer is either null, or points to memory allocated
> with an operator new. It can't just take a random value.

I really think you're barking up the wrong tree here by talking about
implementation details: the guarantees deal with external interface. If an
operation on a class X gives me the basic guarantee, I don't (need to) care
what's going on inside the X object.

So, for example, you'd be wrong in this case:

X::~X()
{
if (condition) delete p;
}

In this case, it wouldn't matter if p takes a random value as long as
condition is false.

-Dave

Smokey the Phat Basset

unread,
Feb 18, 2002, 12:42:11 PM2/18/02
to
Alexander, I tentatively agree with some of your observations about
exception specs, and definitely agree with you very much about evil
catch(...) {} handlers, but in the specific examples of Tru64 or OpenVMS,
how can you appropriately represent in your exception spec that various
non-fatal OS exceptions may be thrown (pthread_cancel_e, pthread_exit_e)?
This problem would be even worse in 3rd party code that is not even aware of
the various different types of OS-based exceptions that could be thrown:

void func_3rd_party() throw(std::bad_alloc)
{
using namespace std;
auto_ptr<T> p(new T);
cout << p << endl; // isn't this a cancellation point? (if not,
substitute printf()..)
}
(not to mention, there is not really even a way to represent Tru64/VMS
thread-cancellation exception in C++, it is only catchable via
catch(...)...)

We wouldn't really want this to call unexpected() if the thread is
cancelled? Does it become necessary to wrap the entire library in
pthread-aware wrapper funcs that can disable cancellations around each
3rd-party function?

Hillel Sims
hys420 AT optonline.net

"Alexander Terekhov" <tere...@web.de> wrote in message
news:3C70C69A...@web.de...

Peter Dimov

unread,
Feb 18, 2002, 12:42:33 PM2/18/02
to
Francis Glassborow <francis.g...@ntlworld.com> wrote in message news:<6VI7iCBC...@robinton.ntlworld.com>...
> In article <7dc3b1ea.02021...@posting.google.com>, Peter
> Dimov <pdi...@mmltd.net> writes

> >To make this 'static ES correct' you need to wrap the body of hypot in
> >a try/catch block:
> >
> >double hypot(double a, double b) throw()
> >{
> > try {

> > return sqrt(a * a + b * b);
> > } catch(...) {
> unexpected();
> > }
> >}
> >
> >which - surprise - is exactly what the current throw() does. Except
> >that it does this automatically.
>
> and is still broken unless unexpected has a throw() spec which it
> obviously cannot have otherwise it could not work in all the ways it is
> designed to.

Yes, I was trying to make the point that static ES checking is worse
than the current status quo.

> Part of the process of making ES statically checkable would be to design
> mechanisms to deal with that. Perhaps by providing a mechanism whereby a
> programmer can take full responsibility for not throwing. Without such,
> it will remain impossible to design adequate static tools. As
> std::nothrow is already in the language perhaps:
>
> double hypot(double a, double b) std::nothrow;
> might be a usable syntax with any exception propagated from hypot being
> undefined behaviour.

Yes, I fully agree that an ES violation should be undefined behavior.
This is the only logical choice, and it enables the compiler to make
an attempt to statically check.

I disagree that we need to do anything beyond that to support static
ES checking. When done right, this will offer nearly zero benefits,
and if done wrong, it will be a significant step backwards.

Program verification should remain outside the language specification.
Besides, there are much more common errors that should have priority,
like index checking, invalid pointer operations, and so on. You may or
may not like the way Java does things, but at least it is consistent.

> I am firmly of the opinion that runtime enforcement is simply
> unacceptable in real programs and static enforcement needs more support.

Runtime enforcement is an illusion, but at least it doesn't cry wolf.

To put it another way, runtime ESes will die gradually and quietly,
while static ESes will die quickly and noisily.

Jerry Coffin

unread,
Feb 18, 2002, 2:46:16 PM2/18/02
to
In article <3C70C69A...@web.de>, tere...@web.de says...

[ ... ]

> Or perhaps "the idiot" just assumed that anything else is
> the indication of BROKEN state of his data (or things like
> that), and just wanted to *prevent* YOU (his *clever* client
> programmer) from catching those silly (really, really
> *unexpected*) exceptions

Why? Why should he have the ability to dictate that I can't catch an
exception if I want to? And how is the author of a library to
predict what exceptions are or aren't expected in a given design? He
can predict which exceptions his code will generate, but NOT what the
rest of the design will do.

> (using something along the lines
> of silly catch(...) {} handlers) that would severely
> impact/limit HIS (i.e. "the idiot") ability to SERVICE
> *you*

How does his forcing the program abort help him service anything for
anybody?

> Current ES is NOT a documenting tool that you should
> apply to your arbitrary functions. This "problem"
> aside, they are just GREAT, IMHO!

The question is whether there's any reasonable situation in which
they do at least as much good as harm. The answer is that if there
is such a thing, I've yet to see or hear of it.

--
Later,
Jerry.

The Universe is a figment of its own imagination.

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

Attila Feher

unread,
Feb 18, 2002, 2:52:20 PM2/18/02
to
"Daniel T." wrote:
>
> Attila Feher <Attila...@lmf.ericsson.se> wrote:
>
> > "Daniel T." wrote:
> > [SNIP]
> > > One of the "rules of good programming" you forgot to mention is that all
> > > exception types should be part of the same hierarchy. (ie all should
> > > derive from std::exception even if indirectly.)
> >
> > OOOK. How do you do that when Manager X says: you must use 3rd Party
> > Library Z, which bases all of its exceptions on class Y, and for which
> > you have no access to the source code?
>
> You could wrap the library in a function that is specified to throw
> correct exceptions and translate any inapproprate exceptions that get
> thrown.

Which is a nice idea, but adds new a level, which has to be designed
etc. etc.

> Another common "rule" is to wrap libraries that are likely to change.
> Certanly, if a library's use is decided by manager types rather than
> technical people, the choice of which library to use will change in
> rather arbitrary (at least from a technical POV) ways.

As above. Nice rule, I am trying to make people understand that for...
about 3 years? Usual manager (finance POV) opinion about this: does it
work without it? Yes. Then we do not have the time for it. Not where
I work, now, it has never been said, but I have heard it enough times.
:-(

> > > Given this rule,
> >
> > I have heard a suggestion that one better not to create rules (laws),
> > which cannot be kept (enforced). The above sounds like one.
>
> That doesn't sound wise to me. I personally live by many rules that are
> not forced on me by outside agents. I don't see why my code shouldn't be
> just as moral...

Yep. But here we talked about rules, which can and will be kept in a
(possibly) geographically distributed (and again possibly) large
development team, working with (3rd possibly) tons of 3rd party stuff
etc. etc. If you make a rule for yourself, you keep it. But how
effective is a "do not ... (the final reasult of eating) "flush" in the
hotel lobby, if you are the only one keeping it?

Attila

Mary K. Kuhner

unread,
Feb 18, 2002, 2:55:49 PM2/18/02
to
James Kanze <ka...@gabi-soft.de> wrote:

> From some of Mary's other postings, I gather her applications are of
>the sort which uses very large quantities of heap memory. So
>bad_alloc is a very real possibility, whereas the other errors
>mentionned could conceivably occur, but are significantly less
>likely. If the machine catches fire, the process will also have to be
>started from scratch, but the risk is considered acceptable.

Yes, this is exactly correct. Whenever I try a new analysis there's
a substantial chance I'll run out of memory and have to either
reduce the size of my analysis or sigh and move to the larger (but
much slower) departmental machine. My remote users report the
same; there's mail in my box this morning saying "The analysis
begins but then terminates midway" that is almost surely an out-of-
memory problem.

I find it nearly impossible to predict in advance whether this
will happen. It depends loosely on the size of the data set, but
much more on the number of events inferred per sample of the
Monte Carlo simulator, and if I knew how to predict *that* I
wouldn't have written a Monte Carlo simulator. I try to apply some
internal limits--I don't allow more than N events per sample--
but the fact remains that a run which averages 30 events but
spikes to 200 may succeed, whereas a run which averages 50 events
won't, so catching the occasional high spikes is not enough.
And figuring out the correct N for a given machine is also hard.
(Currently I cop out by allowing the user to choose N, but of
course she doesn't know either.)

So I think it would be ideal for me to treat bad_alloc as a problem,
but a somewhat expected problem from which recovery should be
considered. I'm not doing it yet for two reasons: (a) it sounds
like a lot of work to find a way to reach a consistent state,
and (b) I have an uneasy feeling that Linux doesn't always throw
bad_alloc when it's supposed to. But it's on the queue, whereas
recovering from other exceptions is not.

Mary Kuhner mkku...@genetics.washington.edu

Mary K. Kuhner

unread,
Feb 18, 2002, 2:57:47 PM2/18/02
to
James Kanze <ka...@gabi-soft.de> wrote:

>I'm not even sure that the basic guarantee means that much (although I
>suppose that it depends on what you mean by inspectable). At the
>limit, all that I can do with the object is destruct it. For
>example, if a container class only made the basic guarantee, I would
>not count on .size() returning a significant value -- it might return
>the number of elements currently in the container, or it might not.
>Even if it did return the correct number of elements, I wouldn't count
>on their values being anything reasonable.

If the container no longer knows how many elements it contains,
how can you write a correct destructor for it? Isn't there likely
to be code somewhere containing either a loop across elements
or the STL algorithm equivalent, and won't both of those die if
the container's state is really inconsistent?

I don't see how an inconsistent container, especially of owning
pointers, could be successfully destructed.

Mary Kuhner mkku...@genetics.washington.edu

James Kanze

unread,
Feb 18, 2002, 2:58:05 PM2/18/02
to
Luis Pedro Coelho <luis_...@netcabo.pt> wrote in message
news:<a4q4ub$3b3$1...@venus.telepac.pt>...

> Dave Harris wrote:

> > Thirdly, we could add a try/catch block:

> > unsigned max_of(const std::vector<unsigned>& vec) throw() {
> > try {
> > typedef std::vector<unsigned>::size_type st;
> > const st len = vec.size();
> > unsigned res = 0;
> > for (st i = 0; i != len; ++i)
> > if (res < vec.at(i)) // vector::at can throw !!
> > res = vec.at(i);
> > return res;
> > }
> > catch (...) {
> > assert( false ); // Cannot be reached.
> > return 0;
> > }
> > }

> > This is almost the moral equivalent of a const-cast. I say
> > "almost" because a const-cast typically has no overhead. The
> > overhead for an exception handler which is never executed is
> > implementation dependent, but can be very close to zero if not
> > zero.

> That is almost exactly what the compiler generates now.
> s/assert(false)/unexpected()/.

As I mentionned in another post, most programs cater to internal
errors anyway. If this throws, it is an internal error, and should be
handled by whatever means the program usually handles internal errors.

You can get this exact behavior with what the compiler generates now;
just set the unexpected handler. The only problem is that what the
compiler generates now is that it shouldn't be the default. There may
be cases when profiling shows you need it, but they should be
exceptional.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --

-- Conseils en informatique orientée objet
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany, Tél.: +49 (0)69 19 86 27

Francis Glassborow

unread,
Feb 19, 2002, 12:37:17 AM2/19/02
to
In article <a4q9l1$vvm$1...@nntp6.u.washington.edu>, Mary K. Kuhner
<mkku...@kingman.genetics.washington.edu> writes

>In the case of bad_alloc I can use some pre-saved space to try
>a recovery. I suppose the program could avoid file handle
>problems in mid-run by opening all its files at startup, and
>thus never get such an exception later. I don't know what to
>do about the full disk. The program's output is quite
>voluminous, and it is meant to be highly portable--tricks like
>trying to write to a different disk might work on Unix but
>they won't be portable.

But surely it is the job of new_handlers to try for recovery with
pre-saved memory, or memory that can be recovered from elsewhere (such
as caches used by particular classes).

I think that concept can be extended to other cases. You could write a
file handle allocator that used a handler to release file handles. Yes
you do need to provide a considerable superstructure for such but it can
be done.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

Francis Glassborow

unread,
Feb 19, 2002, 12:37:36 AM2/19/02
to
In article <d6651fb6.02021...@posting.google.com>, James
Kanze <ka...@gabi-soft.de> writes

>Since all of the people I've talked to who have actually used
>statically enforced exception specifications (in other languages) have
>been favorably impressed by the improvement they make in software
>reliability, I am tentatively of the opinion that they are a good
>thing. If they aren't a good thing in C++, this would suggest that
>there is a problem in this regard with the language more than anything
>else.

Which is why I want to see a serious attempt made to provide static
checking. I believe Ada includes generics, so how do they handle static
checking of exceptions for those? Take that as a rhetorical question,
but I think we should be looking at their solution and considering if it
teaches us anything we could apply to C++.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

Peter Dimov

unread,
Feb 19, 2002, 12:38:48 AM2/19/02
to
ka...@gabi-soft.de (James Kanze) wrote in message
news:<d6651fb6.02021...@posting.google.com>...

> pdi...@mmltd.net (Peter Dimov) wrote in message
> news:<7dc3b1ea.02021...@posting.google.com>...
> > Francis Glassborow <francis.g...@ntlworld.com> wrote in
> > message news:<u8EuopBy...@robinton.ntlworld.com>...
> > > Currently we have the worst of all worlds. ES is not statically
> > > checked, and because most code does not provide it, there is no
> > > motivation to provide a supplementary tool to do so. The
> > > execution time problems encourage programmers to avoid use of ES.
>
> > No, not exactly. In fact, I may well go ahead and claim that what we
> > have now is significantly better than some attempts to "fix" the
> > status quo by adding static ES checking.
>
> May I ask what actual experience you have in large scale projects
> using statically checked exception specifications.

None. I also have no experience with statically checked domain
specifications, and no experience with other static program
verification techniques.

> Because everyone
> else I've talked to who has actually used them swears by them.

Then, perhaps, you could explain what, exactly, those people find
appealing in static ESes, and what problems do they solve. The only
reference I have is D&E, where the arguments given are:

1. Exception specifications ensure that every exception thrown will be
caught by its associated catch().

A: The current exception specifications don't solve this problem
anyway, and I wonder why this kind of error is perceived as more
important than other, much more common and important, errors.

2. Exceptions are chaotic; every function may throw everything and the
programmer has no control over them.

A: Yes, this is true, and is inherent. Exceptions run the show, and
the programmer must adapt. Writing exception safe code requires
effort, and exception specifications don't ensure that code will be
exception safe. One might even bring up the universal "false sense of
security" argument.

> > double sqrt(double) throw(domain_error);
>
> > double hypot(double a, double b) throw()
> > {
> > return sqrt(a * a + b * b);
> > }
>
> > To make this 'static ES correct' you need to wrap the body of hypot
> > in a try/catch block:
>
> So?

For a static program verification tool to be useful, it has to have a
high degree of success. Frequent false positives make the programmer
ignore the output of the tool, s/he will simply insert the code
required for the tool to shut up without thinking.

I don't see how requiring a function to be "strict ES correct" is a
step towards this goal. Making violations undefined behavior allows
the compiler/tool to emit a diagnostic only when the degree of
confidence is more than a certain threshold. When 85% of the
diagnostics turn out to be actual bugs, the tool output will be
respected.

Dave Harris

unread,
Feb 19, 2002, 12:40:01 AM2/19/02
to
jco...@taeus.com (Jerry Coffin) wrote (abridged):

> For example, assume I'm writing a program and I use somebody's matrix
> manipulation library. My code is prepared to deal with a number of
> exceptions that might arise, some of which might happen to arise
> during the execution of the library code. Unfortunately, the idiot
> who wrote the library was discourteous enough to include exception
> specifications in his code -- he "documented" the exceptions he knew
> his code could trigger, and assumed that meant nothing else could
> arise during its execution.

If his library requires that certain routines, such as swap(), provide the
no-throw guarantee, and your application cannot supply no-throw swaps,
then you cannot use his library with your application. Static checking of
exception specs will tell you this at compile time, but they are not the
cause of the problem. It seems to me you are shooting the messenger.

By the way, I think the no-throw guarantee is what static checking is
primarily for. The only good exception spec is an empty one. So I suspect
that matrix library is badly designed. That said, I think it unlikely we
will drop non-empty specs for 2 reasons:

1) Backwards compatibility. C++ has them now so we are pretty much stuck
with them.

2) There may be obscure situations where non-empty exception
specifications are good design.

That they can be abused is not sufficient reason to ban them, in C++.
Especially when the "abuse" is a value judgement. If you don't like how a
library is designed, switch to a different vendor.


> The problem is ultimately quite simple, especially if you think in
> terms of design by contract: an exception specification is part of
> the contract to be fulfilled by a piece of code.

Agreed. I do understand this.


> That's fine in itself, except for one thing: the code
> absolutely, positively canNOT actually keep that promise on its own
> -- it can only be certain of fulfilling the promise with full
> knowledge of the rest of the system. That's tight coupling
> throughout the ENTIRE system, and it's a bad thing.

Code which offers the no-throw guarantee has to know whether routines it
uses have the no-throw guarantee. Just as a const routine cannot call a
non-const one. But this is hardly knowing "the whole system" - just the
routines it uses.

If you are still thinking of writing a signal handler which throws, then
as I say, you have a problem. It's not enough to say your code is able to
handle its exceptions. If they can occur in the matrix library's routines,
the matrix library needs to handle them too. Otherwise its data may become
corrupt and bring the whole application down.

To put it another way, you can't use such a signal handler unless you know
there is no no-throw code anywhere else in the system. /This/ is where the
global system knowledge comes in.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Dave Harris

unread,
Feb 19, 2002, 12:40:19 AM2/19/02
to
luis_...@netcabo.pt (Luis Pedro Coelho) wrote (abridged):
> > I would change it in one of three ways. First, by removing the empty
> > exception specification. It's best to keep no-throw code simple, and
> > your example is quite complicated.
>
> Why remove the empty exception spec? Maybe this code would do something
> which is good to have as a no-throw.

I suggested three ways. Use whichever is most appropriate. It doesn't have
to be the first one.


> Would you rewrite std::vector just for this? What if it was something
> even more complicated.

I think, if we go for static exception specification checking, we will
eventually want to review the std library and add no-throw clauses.


> I think there is a valid point here. However, I don't believe it
> applies. std::vector::at is designed to throw an exception in case
> its argument is out of range

Which makes its use in no-throw routines very strange, don't you think? If
you are so certain the argument will always be in range, why not use
operator[]() instead?


> even std::vector::operator[] can do this as can any operation
> on std::vector::iterator or std::vector::const_iterator under the
> banner of undefined behaviour.

Undefined behaviour is another kettle of fish. Certainly a no-throw
routine which invokes undefined behaviour may end up throwing. Or doing
anything else for that matter. It's impossible to reason about such code.
Exception specifications are only valid if there is no undefined
behaviour.

I still don't think you can use vector::operator[](), because it does not
have an empty exception specification. But as I mentioned above, this
should be reviewed.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

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

Daniel T.

unread,
Feb 19, 2002, 12:40:56 AM2/19/02
to
Attila Feher <Attila...@lmf.ericsson.se> wrote:

>"Daniel T." wrote:


>>
>> Attila Feher <Attila...@lmf.ericsson.se> wrote:
>>
>> > I have heard a suggestion that one better not to create rules (laws),
>> > which cannot be kept (enforced). The above sounds like one.
>>
>> That doesn't sound wise to me. I personally live by many rules that are
>> not forced on me by outside agents. I don't see why my code shouldn't be
>> just as moral...
>
>Yep. But here we talked about rules, which can and will be kept in a
>(possibly) geographically distributed (and again possibly) large
>development team, working with (3rd possibly) tons of 3rd party stuff
>etc. etc. If you make a rule for yourself, you keep it.

In that case, most of the rules that Scott Meyers and Herb Sutter
espouse are pointless, why do they bother? Why do we bother to read them?

I realize where you are comming from, I've been just as cynical of my
situation many times. The whole "why bother" mentality... Fight the evil
side Luke. Be the bright light that others walk to. Use whatever cliche
you can think of, but be the better programmer!

David Abrahams

unread,
Feb 19, 2002, 7:30:16 AM2/19/02
to
I've written large programs that recover from out-of-memory conditions, and
though the application domains have been very different from yours the
scenario is very similar - it's possible to consume very large amounts of
heap memory. What I can't understand is how you think it's going to help to
narrow down which code might throw bad alloc (specifically). Of course, for
your exception-recovery code, you need to know that it's not going to throw
at all (or that you're ready to give up and die if it does throw), but why
focus specifically on knowing whether bad_alloc might be thrown, as opposed
to anything else? If the computation is really important, shouldn't you try
your usual recovery strategy in all cases?

-Dave


"Mary K. Kuhner" <mkku...@kingman.genetics.washington.edu> wrote in message
news:a4r8ov$gae$1...@nntp6.u.washington.edu...

> So I think it would be ideal for me to treat bad_alloc as a problem,
> but a somewhat expected problem from which recovery should be
> considered. I'm not doing it yet for two reasons: (a) it sounds
> like a lot of work to find a way to reach a consistent state,
> and (b) I have an uneasy feeling that Linux doesn't always throw
> bad_alloc when it's supposed to. But it's on the queue, whereas
> recovering from other exceptions is not.

Luis Pedro Coelho

unread,
Feb 19, 2002, 7:32:17 AM2/19/02
to
James Kanze wrote:
> Luis Pedro Coelho <luis_...@netcabo.pt> wrote in message
> news:<a4neba$t54$1...@venus.telepac.pt>...
>
> > Dave Harris wrote:
>
> > > If most exception specifications are trivially met, it will be
> > > reasonable for compilers to warn if they cannot prove, at compile
> > > time, they are met. Thus we can gradually move towards static
> > > constraints on exceptions thrown. This will be valuable for
> > > correctness.
>
> > Consider:
>
> > unsigned max_of(const std::vector<unsigned>& vec) throw()
> > {
> > typedef std::vector<unsigned>::size_type st;
> > const st len = vec.size();
> > unsigned res = 0;
> > for (st i = 0; i != len; ++i)
> > {
> > if (res < vec.at(i)) // vector::at can throw !!
> > res = vec.at(i);
> > }
> > return res;
> > }
>
> > I know that this function will not throw.
>
> Of course, if you know that the access cannot throw, you would have
> written [], right?

This was only an example to show the underlying idea: The use of a function
which might throw in a way I know it will not.

BTW, operator[] is written without an exception clause. It might throw
anything.

> > However, how can the compiler know this?
>
> > IMHO this is the killer argument against the static checking of
> > exceptions.
>
> Where is the argument? If you "know" that some sequence cannot throw,
> even if the exception specifications of the called functions say it
> can, then you very definitly need to impart this knowledge in the
> program somehow. You could use comments, but I would find something
> like:
>
> try {
> // ...
> } catch ( ... ) {
> ProcessManager::fatalError( "unexpected exception..." ) ;
> }
>
> far more preferrable.

That is exacty what the compiler generates now. Except the compiler uses
unexpected() instead of ProcessManager::fatalError(...).

I don't really like using try / catch that way. Maybe it's because it isn't
idiomatic and I will change my opinion when I am used to seeing it more.

I would also like to see a more statement level option. Maybe something
like:

<code>
std::vector<unsigned> vec;
vec.push_back(1);
throw(): unsigned x = vec.at(0);
</code>

The throw() pseudo label tells the compiler that the statement that follows
will not throw.

This could allow for something more complex. Let's say I have a class
Double which is similar to Double. Let's say I also have this function:

Double operator / (Double a, Double b)
throw(std::bad_alloc, // Double allocate memory
std::logic_error) // if b == 0 throws logic_error

<code>
Double a = // .... ;
throw(std::bad_alloc): Double b = a / Double(2.0);
</code>

Here the throw(std::bad_alloc) tells the compiler that the statement that
follows will at most throw a std::bad_alloc from which it can deduce that
it will *not* throw std::logic_error.

Maybe it would be good to allow a block level construct:

<code>

throw(): {
//...
//...
}
</code>

Following the replies I got to my original message, I have changed my mind:

I still think it is undesirable with the current language, but maybe it
will be good if one can say to the compiler: "I _know_ this will not throw
everything it says it might".

Regards,
--
Luis Pedro Coelho.

Check out my game of Hearts, a card game, for KDE at:
http://hearts.sourceforge.net/

C Nick Beaudrot

unread,
Feb 19, 2002, 7:33:38 AM2/19/02
to
On 15 Feb 2002 13:01:20 -0500, David Abrahams <david.a...@rcn.com> wrote:
:
: "C Nick Beaudrot" <n...@cs.brown.edu> wrote in message
: news:a4hu51$ps0$1...@saturn.services.brown.edu...
:
:
: > While I agree with most of these points, one of my colleagues has
: > pointed out that lack of exception specifications makes it difficult to
: > integrate 3rd party code. That is, my friend would actually like to have
: > the extra safety provided by ES
: >
: > [...]
: >
: > It's as though he wrote a slew of const
: > correct code, and then I handed him a slew of code that was not const
: > correct--he can't use it without hacking in my header files.
:
: I don't understand what that means.
: Do you have a compiler that enforces
: ESes somehow?

Yes. As a non-standard extension, Sun's compiler will enforce ES at
compile time if you like (that's hearsay; I haven't tried doing such a
thing). So if I hand him code without ESs, he won't be able to compile
unless he either
(a) repairs my header files, which relies on looking at my code or
trusting my documentation, or
(b) wraps every call in my library with
try {
}
catch (LegitimateException1& e) {
}
catch (LegitimateException2& e) {
}
// ...
catch (...) {
}
"which is so painfully verbose it makes my teeth hurt", in
addition to being error prone.

:
: > Is there any good solution to this?
:
: It's not clear to me whether you have a technical problem or it's a "people
: issue".

Fair enough. BTW, my friend is not a particularly large fan of C++
runtime exception enforcement. He says he wouldn't use ES if he didn't
have a compiler that could check it at compile time.

Cheers,
Nick

--
Don't inflict upon me an idiotic belief of a God who doesn't answer the
prayers of sick children, at least in a tangible way that we understand.
Who doesn't intervene apparently in cases of war or poverty and famine.
But hey Mr self-absorbed NFL wide receiver, you need the first down on
third-and-seven? God's right there, he's got you covered.
-Bob Costas

C Nick Beaudrot

unread,
Feb 19, 2002, 7:34:17 AM2/19/02
to
On 18 Feb 2002 06:35:19 -0500, James Kanze <ka...@gabi-soft.de> wrote:
: In the end, I think the arguments for or against COW are generally
: overstated, and more importantly, they are probably irrelevant for
: anyone NOT implementing a standard library.
:
: [various interesting people issues snipped]

However, it is worth noting that atomic increment/decrement operations
(necessary for reference counting, necessary for mutexless COW) are of
interest to people writing things other than COW std::basic_string<>s, e.g.
boost::smart_ptr.

Cheers,
Nick (long live the atomic cow)

Helmut Zeisel

unread,
Feb 19, 2002, 8:10:31 AM2/19/02
to
Peter Dimov wrote:

> None. I also have no experience with statically checked domain
> specifications, and no experience with other static program
> verification techniques.

It might be also interesting to look at the experiences in Java:

http://www.java-zone.com/free/articles/Kabutz03/Kabutz03-1.asp

"Seeing that a large number of Java programmers are novices due
to the age of the language, we could say that, even though I like them,
checked
exceptions could perhaps have been a mistake."

Alexander Terekhov

unread,
Feb 19, 2002, 9:45:28 AM2/19/02
to

Smokey the Phat Basset wrote:
>
> Alexander, I tentatively agree with some of your observations about
> exception specs, and definitely agree with you very much about evil
> catch(...) {} handlers, but in the specific examples of Tru64 or OpenVMS,
> how can you appropriately represent in your exception spec that various
> non-fatal OS exceptions may be thrown (pthread_cancel_e, pthread_exit_e)?

In general (for non-"throw()" operations), I would do
it in the documentation only, unless I really want to
specify ALL "expected" exceptions -- just to prevent
someone from irresponsibly catching those "unexpected"
exceptions, which is most likely the ONLY reason to
use throw(something) ESpecs, I think.

But throw() is a special case. I use it quite often
but mostly for things along the lines of swap() or
auto_ptr-like things. If I do NOT need a function
to throw *nothing* I'll better leave it as throw
ANYTHING (again, "protection" mode aside). Non-
throwing function "overloads" (std::nothrow) might
be quite useful too in some situations.

> This problem would be even worse in 3rd party code that is not even aware of
> the various different types of OS-based exceptions that could be thrown:
>
> void func_3rd_party() throw(std::bad_alloc)
> {
> using namespace std;
> auto_ptr<T> p(new T);
> cout << p << endl; // isn't this a cancellation point? (if not,
> substitute printf()..)
> }

Why not just ask those vendor(s) to provide you a
THREAD-SAFE version of the libraries that would
include cancel exception in throw(something) for
cancellation points or just have only throw()
ESpecs (for things meant to be called in
NOTHROW places), if your vendor(s) are willing
to provides such less restrictive versions of
their libs? ;-)

> (not to mention, there is not really even a way to represent Tru64/VMS
> thread-cancellation exception in C++, it is only catchable via
> catch(...)...)

http://www.tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51_HTML/ARH9RBTE/DOCU0009.HTM#excep_langs

"You can use the C language exception handling
mechanism (SEH) to catch exceptions. You can
catch exceptions in C++ using catch(...),
and propagation of exceptions will run C++
object destructors. Currently, C++ code cannot
catch specific exceptions. Also, CATCH,
CATCH_ALL and FINALLY clauses will not run
when C++ code raises an exception. (These
restrictions will be reduced or removed in
a future release.)"

As for cancel/exit... yeah, I guess that's because:

a) there is no standard POSIX/Pthread *C++* bindings;

b) they are not supposed to be finalized/"eaten"
by the application code[1].

On the other hand, AFAICT you could easily catch
them in C. Also, consider that LGPL pthreads-win32
lib[2], for example, raises true C++ cancel/exit
exceptions... *undocumented*, though! ;-)

BTW, I would like to invite anyone who uses
linuxthreads/glibc/g++ to participate in this
discussion/"plea":

http://sources.redhat.com/ml/libc-alpha/2002-02/msg00110.html
http://sources.redhat.com/ml/libc-alpha/2002-02/msg00116.html
http://sources.redhat.com/ml/libc-alpha/2002-02/msg00117.html

> We wouldn't really want this to call unexpected() if the thread is
> cancelled?

Well, I think it would be nice if the cancellation
points would internally call something along the lines
of "bool future_std::expected_exception< cancel_e >()"[3]
in addition to cancellation state checks. Also, I think
that something along the lines of "bool future_std::
unwinding(T*)"[4] would be quite handy too! ;-)

regards,
alexander.

[1] See this c.p.t post (no response from Butenhof yet :(

http://groups.google.com/groups?as_umsgid=c29b5e33.0202150920.4c9f991a%40posting.google.com

[2] http://sources.redhat.com/pthreads-win32

[3] AFAICT, on any reasonable implementation,
"throw-expression" does the SEARCH for some handler
first (prior to unwind), and if not found or just
restricted by some ES in the DYNAMIC/RUNTIME context,
calls ...->terminate();

So, why not make that SEARCH/CHECK-ES functionality
available separately from "throw-expression"?!

[4] "The process of calling destructors for automatic
objects constructed on the path from a try block
to a throw-expression is called 'stack unwinding.'"

A::~A() { try { B b(this); } catch( int ) {...} }
B::~B() { cout << future_std::unwinding( this ) <<
future_std::unwinding( m_pA ) <<
future_std::expected_exception< int >(); }

void f() { A a; throw "Trouble"; }

OUTPUT: 011

Attila Feher

unread,
Feb 19, 2002, 11:37:29 AM2/19/02
to
James Kanze wrote:
[SNIP]

> In the end, I think the arguments for or against COW are generally
> overstated, and more importantly, they are probably irrelevant for
> anyone NOT implementing a standard library. In your case, for
> example, the problem isn't COW per se, it is the performance of the
> library. Using another library (STLPort, etc.) might solve the
> problem. In the end, of course, you don't care whether the string is
> COW or not, as long as it is fast enough
[SNIP]

Actually I do care. Why? COW cannot be implemented according to the
standard. Ask Herb! With COW, simple, non-throw string operations may
throw std::bad_alloc. Operations, which according to the standard,
_must_ not_ throw _anything_!

Attila

James Kanze

unread,
Feb 19, 2002, 11:45:34 AM2/19/02
to

My real answer should be to ask them, not me. I've never used
staticly checked exception specifications either. All I notice is
that everyone I know who has used them evaluates them very positively,
and everyone I know who criticizes them has never actually used them.

In practice, of course, I can think of some probable reasons. What
exceptions a function may throw is part of the contract. If this part
of the contract can be validated by the compiler, so much the better.
(Note that many of the languages which have statically enforced
exception specifications also have subrange types, and such, which
also allow more compile time checking of the contract.)

Note too that I say, may throw, rather than does throw. I may specify
that a function may throw any exception derived from std::exception,
for example, or even that a function may throw anything. Nothing
requires me to lock myself into a straight jacket up front, unless I
feel that such requirements are necessary, in order to increase the
utility of the class. Typically, the most important contractual
guarantee that I can give is that the function throws nothing. If I
change this guarantee, I would really like it for the compiler to
complain, because it is likely to break client code.

With regards to being required to specify more details, I'll admit
that I'm only partially convinced. I suspect that in some cases, the
desire for detailed checked exception specifications is due to using
exceptions for things that should really be return codes. This is
definitly the case in Java, for example, where for the most part, the
only things I would use exceptions for all derive from
RunTimeException, and are not checked. But of course, I'm only
speculating.

> The only reference I have is D&E, where the arguments given are:

> 1. Exception specifications ensure that every exception thrown will
> be caught by its associated catch().

At some level or other. On the other hand, both the specification and
the catch can be very vague: std::exception, for example.

More importantly, an empty exception specification ensures that the
function will not throw.

> A: The current exception specifications don't solve this problem
> anyway, and I wonder why this kind of error is perceived as more
> important than other, much more common and important, errors.

The current specifications don't solve this problem because they
aren't statically enforced.

> 2. Exceptions are chaotic; every function may throw everything and
> the programmer has no control over them.

As we know, this is not true. If it were, exception safety would not
be possible. Which exceptions are thrown may vary (although I would
expect some standards on any project), but if I guarantee that a
function will not throw, it won't throw. And I can often make this
guarantee in C++.

> A: Yes, this is true, and is inherent. Exceptions run the show, and
> the programmer must adapt. Writing exception safe code requires
> effort, and exception specifications don't ensure that code will be
> exception safe. One might even bring up the universal "false sense
> of security" argument.

The use of statically checked exception specifications don't make the
code exception safe. They do ensure that if a function which didn't
throw is modified to throw, all client code must be adapted to meet
the new constraints.

> > > double sqrt(double) throw(domain_error);

> > > double hypot(double a, double b) throw()
> > > {
> > > return sqrt(a * a + b * b);
> > > }

> > > To make this 'static ES correct' you need to wrap the body of
> > > hypot in a try/catch block:

> > So?

> For a static program verification tool to be useful, it has to have
> a high degree of success. Frequent false positives make the
> programmer ignore the output of the tool, s/he will simply insert
> the code required for the tool to shut up without thinking.

If the false positives were frequent, I could have some sympathy for
your argument. In my experience, such cases as the above are rather
rare. (But perhaps this depends on the application domain. There are
certainly rare in the domains I've worked in.)

> I don't see how requiring a function to be "strict ES correct" is a
> step towards this goal. Making violations undefined behavior allows
> the compiler/tool to emit a diagnostic only when the degree of
> confidence is more than a certain threshold. When 85% of the
> diagnostics turn out to be actual bugs, the tool output will be
> respected.

If the code doesn't compile, the diagnostics will be respected. IMHO,
the above hypot function shouldn't compile without a try...catch
block.

At least in theory. I'm not sure how to make this work with
templates.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --

-- Conseils en informatique oriente objet

Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

James Kanze

unread,
Feb 19, 2002, 11:46:54 AM2/19/02
to
"David Abrahams" <david.a...@rcn.com> wrote in message
news:<a4r1o9$qog$1...@bob.news.rcn.net>...

> "James Kanze" <ka...@gabi-soft.de> wrote in message
> news:d6651fb6.02021...@posting.google.com...

> > I'm not even sure that the basic guarantee means that much
> > (although I suppose that it depends on what you mean by
> > inspectable). At the limit, all that I can do with the object is
> > destruct it. For example, if a container class only made the
> > basic guarantee, I would not count on .size() returning a
> > significant value -- it might return the number of elements
> > currently in the container, or it might not. Even if it did
> > return the correct number of elements, I wouldn't count on their
> > values being anything reasonable.

> That's an incorrect understanding of the basic guarantee. If the
> container documentation says that .size() returns the number of
> elements in the container, then that's what it returns, before or
> after an exception is thrown.

> You're right that the basic guarantee doesn't give us much, but it
> does give us a bit more than you have inferred it does. For example,
> a container can be clear()ed and used from there. Also, we know that
> its elements are reasonable in the sense that the library can only
> have used the operations given in the container's requirements for
> the element type. Typically, the elements must only be Assignable,
> so your container<T> just contains copies of some other T which has
> existed.

OK. I'm not up on the terminology. I understand that there are
basically three guarantees: the strong one, in which the function
either terminates normally, or does nothing, then one in which the
state isn't defined, but all invariants hold (which I thought was the
weak one), and one in which not even the invariants hold, and that the
only operation supported is destruction (which I thought was the basic
guarantee). One could imagine a case in which even destruction isn't
supported, but of course, such a class could not be practically used.

> > On the other hand, if the object contains a pointer, and the
> > destructor executes a delete on that pointer, then even after an
> > exception, that pointer is either null, or points to memory
> > allocated with an operator new. It can't just take a random
> > value.

> I really think you're barking up the wrong tree here by talking
> about implementation details: the guarantees deal with external
> interface. If an operation on a class X gives me the basic
> guarantee, I don't (need to) care what's going on inside the X
> object.

I agree. I was just trying to explain with an example. The weakest
guarantee is that the destructor can be called (without undefined
behavior); how the code achieves this is irrelevant.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 19, 2002, 11:47:51 AM2/19/02
to
mkku...@kingman.genetics.washington.edu (Mary K. Kuhner) wrote in
message news:<a4r82u$d4s$1...@nntp6.u.washington.edu>...
> James Kanze <ka...@gabi-soft.de> wrote:

> >I'm not even sure that the basic guarantee means that much
> >(although I suppose that it depends on what you mean by
> >inspectable). At the limit, all that I can do with the object is
> >destruct it. For example, if a container class only made the basic
> >guarantee, I would not count on .size() returning a significant
> >value -- it might return the number of elements currently in the
> >container, or it might not. Even if it did return the correct
> >number of elements, I wouldn't count on their values being anything
> >reasonable.

> If the container no longer knows how many elements it contains, how
> can you write a correct destructor for it? Isn't there likely to be
> code somewhere containing either a loop across elements or the STL
> algorithm equivalent, and won't both of those die if the container's
> state is really inconsistent?

In the specific case of std::vector, I doubt that you could write a
correct destructor if the invariants were violated. It isn't
difficult to conceive of classes where it would be possible, however.

> I don't see how an inconsistent container, especially of owning
> pointers, could be successfully destructed.

What about std::list? The size parameter may be inconsistent, but I
doubt that it is used in the destructor. More generally, if you have
not separated allocation from construction, then as long as your
pointers are OK, you should be able to destruct correctly, regardless
of the internal state.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 19, 2002, 11:48:17 AM2/19/02
to
Francis Glassborow <francis.g...@ntlworld.com> wrote in message
news:<i9P9YTAC...@robinton.ntlworld.com>...

> In article <a4q9l1$vvm$1...@nntp6.u.washington.edu>, Mary K. Kuhner
> <mkku...@kingman.genetics.washington.edu> writes
> >In the case of bad_alloc I can use some pre-saved space to try a
> >recovery. I suppose the program could avoid file handle problems
> >in mid-run by opening all its files at startup, and thus never get
> >such an exception later. I don't know what to do about the full
> >disk. The program's output is quite voluminous, and it is meant to
> >be highly portable--tricks like trying to write to a different disk
> >might work on Unix but they won't be portable.

> But surely it is the job of new_handlers to try for recovery with
> pre-saved memory, or memory that can be recovered from elsewhere
> (such as caches used by particular classes).

The job of the new handlers is to try and recover memory, so that the
program can continue. In this case, we know that there is no more
memory (other than that previously saved for precisely this case), so
the new handler cannot work. The importance of this particular case
IS that new has failed; the role of a new handler is to prevent new
from failing.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique oriente objet
Ziegelhttenweg 17a, 60598 Frankfurt, Germany, Tl.: +49 (0)69 19 86 27

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

James Kanze

unread,
Feb 19, 2002, 11:49:34 AM2/19/02
to
Jean-Marc Bourguet <j...@bourguet.org> wrote in message
news:<3c70f57d$0$16589$626a...@news.free.fr>...
> ka...@gabi-soft.de (James Kanze) writes:

> > All of the Ada experts, for example, use exception specifications
> > in their code -- this would argue just as strongly *for*
> > exception specifications.

> As far as I know, there is so such things as exception
> specifications in Ada.

> Perhaps are you talking about SPARK, which is a subset of Ada with
> annotations? There are quite a lot of things which are statically
> checkable in SPARK, but then you are more constrained in what is
> acceptable.

Or some other language:-). Regretfully, I've never had the occasion
to use any modern language outside of the C family.

My point is just that some languages require exception specifications.
And the fact that experts in these languages use exception
specifications, of course, doesn't prove anything, because they don't
have a choice. Similarly the fact that the C++ experts don't use
statically checked exception specifications makes no statement
concerning their value -- they don't exist in C++, so we don't have a
choice.

Peter Dimov

unread,
Feb 19, 2002, 2:34:25 PM2/19/02
to
mkku...@kingman.genetics.washington.edu (Mary K. Kuhner) wrote in message news:<a4om7m$1b8a$1...@nntp6.u.washington.edu>...
> Peter Dimov <pdi...@mmltd.net> wrote:
>
> >The underlying assumption is that code that knows what exceptions can
> >be thrown by f is somehow safer than code that doesn't. How, exactly,
> >does this follow? I assert that the opposite is true, and that code
> >that depends on this knowledge is less safe. The correctness of the
> >code should not depend on the type of the exception being thrown by f.
>
> This is speculative--I am going to have to write such code someday,
> but not yet--but it seems to me that knowing which parts of my
> program can throw std::bad_alloc and which can't could make a large
> difference.
>
> One of these days, my program is going to have to contain recovery
> code for running out of memory--it should save its work in progress so
> that the user can do something about the memory situation and then
> recover, rather than having to start a week-long run again from
> scratch. The recovery code will presumably involve reaching the
> nearest consistent state and then printing intermediate results to
> file. Having a pre-allocated block of unused memory around will
> probably be useful.
>
> I'd really like to know for sure that routines A and B can throw
> bad_alloc but routines C and D definitely don't. The
> recovery code has to be hand-crafted for each throwing routine,
> so putting catch(bad_alloc) everywhere, even where it's not
> needed, would be a huge waste of effort.

I might be misunderstanding something, but it seems to me that you
don't need ES for this. You simply write the lower level routines to
be exception neutral, and catch bad_alloc only at points where the
state is known to be consistent.

C Nick Beaudrot

unread,
Feb 19, 2002, 2:36:34 PM2/19/02
to
On 18 Feb 2002 06:39:22 -0500, James Kanze <ka...@gabi-soft.de> wrote:
: "Daniel T." <notda...@gte.net> wrote in message
: news:<notdanielt3-4168...@paloalto-snr1.gtei.net>...
: > In article <memo.20020217...@brangdon.madasafish.com>,
: > bran...@cix.co.uk (Dave Harris) wrote:
:
: > > > Note that throw() specifications are a special case because they
: > > > happen to match an exception safety guarantee.
:
: > > That special case is the one I have in mind. I don't personally
: > > care about support for non-empty exception specifications.
:
: > From what I've read, those in the Java world are making the same
: > realization. What's important (and should be checked staticly) is
: > whether a function throws or not. If it throws, the exact type of
: > exception is of little concequence to the interface specification.
:
: I suspect that this is typically the case (although there may be
: exceptions). That is probably why all exception hierarchies tend to
: derive from a single class.
:
: But I'm curious about the reference to Java. Because in Java, unlike
: C++, it is impossible to specify that a function doesn't throw -- the
: most useful exception specification is the one you can't state.

You can specify that a function does not throw a "static" Exception, ie
public class Foo
{
public void f()
{
throw new SubclassOfException(); // compile time error
}
}
vs
public class Bar
{
public void g()
{
throw new SubclassOfRuntimeException(); // fine and dandy
}
}
Saying "this function does not throw a static exception" is less useful
than "this function does not throw an exception", but it's a start at
least.

However, last I checked (which was 6 months ago) Sun recommended
*against* using RuntimeException for precisely this reason.

The major problem as I see it is that many of the exceptions thrown by
the libraries Sun provides derive from RuntimeException (eg NullPointer,
ArrayIndexOutOfBoundsException).

: (Of course, functions which cannot throw are much rarer in Java than
: in C++. Partially because most types must be dynamically allocated,
: which opens the possibility of the Java equivalent of std::bad_alloc,

Java's even smarter: you receive a non-catchable OutOfMemoryError if
new() fails. There's no real hope of error recovery in Java if you can't
new, so you might as well give up trying.

[this makes the sort of error recovery that Ms Kuhner needs
impossible. I'll admit that's a pain.]

Cheers,
Nick

--
Don't inflict upon me an idiotic belief of a God who doesn't answer the
prayers of sick children, at least in a tangible way that we understand.
Who doesn't intervene apparently in cases of war or poverty and famine.
But hey Mr self-absorbed NFL wide receiver, you need the first down on
third-and-seven? God's right there, he's got you covered.
-Bob Costas

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

C Nick Beaudrot

unread,
Feb 19, 2002, 2:38:51 PM2/19/02
to
On 18 Feb 2002 12:42:11 -0500, Smokey the Phat Basset

<use...@XXXXXphatbassetXXXXX.com> wrote:
: Alexander, I tentatively agree with some of your observations about
: exception specs, and definitely agree with you very much about evil
: catch(...) {} handlers, but in the specific examples of Tru64 or OpenVMS,
: how can you appropriately represent in your exception spec that various
: non-fatal OS exceptions may be thrown (pthread_cancel_e, pthread_exit_e)?
: This problem would be even worse in 3rd party code that is not even aware of

: the various different types of OS-based exceptions that could be thrown:
:
: void func_3rd_party() throw(std::bad_alloc)

*If* your 3rd party provides you with ESes, and he doesn't consider
thread cancellation, isn't that a bug for you to file with the 3rd party
("your library is not suitiable for MT use")?

: void func_3rd_party() throw(std::bad_alloc)


: {
: using namespace std;
: auto_ptr<T> p(new T);
: cout << p << endl; // isn't this a cancellation point?

I don't think there are any guarantees that cout.operator<<(whatever&)
may call write(2). But it may. So let's call it a cancellation point
just to be safe :-).

: }
: (not to mention, there is not really even a way to represent Tru64/VMS


: thread-cancellation exception in C++, it is only catchable via
: catch(...)...)

The fact that you actually get an exception on thread cancellatoin puts
Tru64/OpenVMS light years ahead of everyone else. (Sun gets half a point
for correctly pushing/popping all destructors on/off the C cancellation
stack.)

:
: We wouldn't really want this to call unexpected() if the thread is
: cancelled? Does it become necessary to wrap the entire library in
: pthread-aware wrapper funcs that can disable cancellations around each
: 3rd-party function?

Thread cancellation is another ball of wax. In all cases the stack must
be unwound all the way to the top for correct cancellation. Cancellation
is also a situation where you *do* want tight coupling throughout the
entire system (f1() calls f2() which may cancel, so f1() may cancel, and
so on and so forth) so as to improve cancellation safety, which may or
may not be the case with vanilla exceptions.

Mary K. Kuhner

unread,
Feb 19, 2002, 2:41:08 PM2/19/02
to
Francis Glassborow <fran...@robinton.demon.co.uk> wrote:

>But surely it is the job of new_handlers to try for recovery with
>pre-saved memory, or memory that can be recovered from elsewhere (such
>as caches used by particular classes).

The new_handler makes sense if you just need to obtain a bit more
memory from somewhere, but I need to do more than that; I need to
reach the nearest consistent state (for example, getting rid of the
partially-completed step that threw the exception) and then trigger
a dump-to-disk routine. My current thinking on the dump-to-disk
is that rather than trusting the new_handler to get some memory,
it should pre-allocate enough memory at program startup; this seems
a lot more sure. It shouldn't take much; enough to make
a line-length std::string, mainly.

The hard part, the part that is causing me to procrastinate, is
assuring that the intermediate results are in a consistent state
even if bad_alloc arrives midway through a cycle. If I am lucky
this will fall naturally out of the program design, but I'm afraid
I failed to design for it, so it may not.

All this aside, I'm not sure that exception specs would help at all.
I was just responding to the question "Why would you ever care
what exception a routine can throw?" I find that I have two
classes of exceptions, recoverable and not, and I do want to know
what routines throw the recoverable one.

Mary Kuhner mkku...@genetics.washington.edu

Mary K. Kuhner

unread,
Feb 19, 2002, 2:42:39 PM2/19/02
to
David Abrahams <david.a...@rcn.com> wrote:
>Of course, for
>your exception-recovery code, you need to know that it's not going to throw
>at all (or that you're ready to give up and die if it does throw), but why
>focus specifically on knowing whether bad_alloc might be thrown, as opposed
>to anything else? If the computation is really important, shouldn't you try
>your usual recovery strategy in all cases?

The other exceptions that I've seen it actually throw have always been
associated with some kind of logic error. I don't want to recover
from logic errors, because a wrong answer is worse than no answer at
all for this application. I want the program to complain loudly and
die.

I can think of a couple of other exceptions that wouldn't indicate
logic error, such as the suggested out of files, out of handles,
or out of disk space, but I don't know how to recover from these,
so I'm not planning to try. Instead, I'll try to move these errors
earlier in the run so that they are less painful.

bad_alloc probably does not represent a logic error--the user can
guarantee a bad_alloc by just increasing the size of her data set
sufficiently--and can't be moved earlier in the run, so it gets
special treatment. I hope that if I print out the intermediate
results, the user can either free up some memory, or move to a
larger machine, and restart in mid-computation using the stored
results.

This is pragmatic. It would be great to fix everything, but in most
cases I can't (especially the logic errors). I would like to
know which code can throw bad_alloc because that one I *can*
fix, if I know where it happened. (There is no use catching it
in main() as by then recovery will be impossible.)

Mary Kuhner mkku...@genetics.washington.edu

Alexander Terekhov

unread,
Feb 19, 2002, 2:43:50 PM2/19/02
to

James Kanze wrote:
[...]

> > Then, perhaps, you could explain what, exactly, those people find
> > appealing in static ESes, and what problems do they solve.
>
> My real answer should be to ask them, not me. I've never used
> staticly checked exception specifications either. All I notice is
> that everyone I know who has used them evaluates them very positively,
> and everyone I know who criticizes them has never actually used them.

http://www.mindview.net/Etc/Discussions/CheckedExceptions
("Does Java need Checked Exceptions?...")

http://www.mindview.net/Etc/Discussions/UnCheckedExceptionComments

I like this:

"...It is no surprise that all major programming languages
either never got, or dropped, checked exceptions. Even SML,
a stickler for static type checking and the language with
the best shot at inferring and propagating exception
declarations, does not force you to handle exceptions,
because it doesn't make sense.

How can this mess be fixed? There is actually a very
simple solution: just make the default exception declaration
for methods "throws Exception". That's completely backwards
compatible. And that way, people who have gotten attached to
exception declarations can continue to use them (and in a few,
limited cases, they may indeed be useful), but the rest of us
won't suffer from them and the bugs they cause in our code. "

regards,
alexander.

Alexander Terekhov

unread,
Feb 19, 2002, 2:58:05 PM2/19/02
to
Jerry Coffin wrote:
[...]
> Why? Why should he have the ability to dictate that I can't catch an
> exception if I want to?

Why would you ever want to catch HIS "internal" exceptions?
What would you do with them? You just cannot handle them/
recover! Would you ever want to "catch" some internal
assert()s that force the program abort, for example?

> And how is the author of a library to
> predict what exceptions are or aren't expected in a given design? He
> can predict which exceptions his code will generate, but NOT what the
> rest of the design will do.

Do you mean that library code calls YOUR code
via templates/callbacks/whatever? If so, then
yes -- ES should NEVER be imposed on your code
(except for throw() operations he *really*
needs to make something exception safe).

> > (using something along the lines
> > of silly catch(...) {} handlers) that would severely
> > impact/limit HIS (i.e. "the idiot") ability to SERVICE
> > *you*
>
> How does his forcing the program abort help him service anything for
> anybody?

Because he could then analyze/debug from that core-dump/
crash-dump/... to find out the reason of that unexpected
throw/system-fault and try to fix it. Same magic as with
asserts/bugchecks or whatever you call them. No difference
at all.

> > Current ES is NOT a documenting tool that you should
> > apply to your arbitrary functions. This "problem"
> > aside, they are just GREAT, IMHO!
>
> The question is whether there's any reasonable situation in which
> they do at least as much good as harm. The answer is that if there
> is such a thing, I've yet to see or hear of it.

Do you see some problems/harm w.r.t throw() ES for
operations like "swap" or auto_ptr-like interfaces?

As for throw(something) ESpecs, yeah -- other than
ability to *prevent* irresponsible catching of really
unexpected exceptions (they should better result in
abort()/"core dump" *ASAP*, just like asserts... and
they DO, unless you happen to irresponsibly catch),
I do NOT see much use for them. Nevertheless I do
think that they ARE really useful given the popularity
of catch(...){}! ;-)

Francis Glassborow

unread,
Feb 19, 2002, 2:59:34 PM2/19/02
to
In article <7dc3b1ea.02021...@posting.google.com>, Peter
Dimov <pdi...@mmltd.net> writes

>I don't see how requiring a function to be "strict ES correct" is a
>step towards this goal. Making violations undefined behavior allows
>the compiler/tool to emit a diagnostic only when the degree of
>confidence is more than a certain threshold. When 85% of the
>diagnostics turn out to be actual bugs, the tool output will be
>respected.

Undefined behaviour that comes about because of my incompetence,
ignorance or plain laziness is my fault. However the problem with
uncaught exceptions is that they are outside my direct control.
Exceptions are often raised in code that I not only did not write but
may even be code that I cannot see (because it is delivered as object
code). In the best of worlds code would always be properly documented
and critical changes in new (including slipstream) releases would be
heavily highlighted. We do not live in such a world. Because they do not
work to many people's satisfaction, ESs are not widely used.

Dynamic checking is too late, and currently static checking is not
viable. If C++ is to remain a premier language for large scale
programming relying heavily on third party libraries it, IMO, needs to
address these issues. What makes this different from the other problems
you list is that those responsible for raising exceptions are often
(usually) not responsible for handling them.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

Alexander Terekhov

unread,
Feb 19, 2002, 3:09:00 PM2/19/02
to

bran...@cix.co.uk (Dave Harris) wrote in message
news:<memo.20020218...@brangdon.madasafish.com>...
[...]

> > The problem is ultimately quite simple, especially if you think in
> > terms of design by contract: an exception specification is part of
> > the contract to be fulfilled by a piece of code.
>
> Agreed. [ ... ]

Well, to me, throw(something) exception specification is
part of *excuse* to BREAK the "contract" (with "basic" or
"strong" guarantee), NOT the "contract" itself. And personally,
I do not really care why the contract was broken, other than
for recovery purposes (and unless my code is designed in
exception-safe/neutral manner for this or that particular
operation(s) - is able to propagate "troubles" to my
caller -- so s/he should care then... if s/he could/want;
if NOT, the program should better terminate/"core dumped"
at THROW point -- *without* any useless and even harmful
unwinding/exception(fault) propagation at all because
that would severely complicate the "broken contract"
investigation/debug). I would not use throw(something)
other than for sanity/safety reasons to prevent
catching of really "unexpected" exceptions/faults,
which should be "recovered"/fixed by product service
personnel.

> If you are still thinking of writing a signal handler which throws, then
> as I say, you have a problem. It's not enough to say your code is able to
> handle its exceptions. If they can occur in the matrix library's routines,
> the matrix library needs to handle them too. Otherwise its data may become
> corrupt and bring the whole application down.
>
> To put it another way, you can't use such a signal handler unless you know
> there is no no-throw code anywhere else in the system. /This/ is where the
> global system knowledge comes in.

Since Pthreads cancellation == exception (in C++ at least),
you might want to read this:

http://www.opengroup.org/onlinepubs/007904975/xrat/xsh_chap02.html#tag_03_02_09_24
("Interaction of Cancelation with Asynchronous Signals")

Also, see "Async-Cancel Safety"...

regards,
alexander.

Francis Glassborow

unread,
Feb 19, 2002, 3:17:12 PM2/19/02
to
In article <3C71FFD1...@attglobal.net>, Helmut Zeisel
<atva...@attglobal.net> writes

> > None. I also have no experience with statically checked domain
> > specifications, and no experience with other static program
> > verification techniques.
>
>It might be also interesting to look at the experiences in Java:
>
>http://www.java-zone.com/free/articles/Kabutz03/Kabutz03-1.asp
>
>"Seeing that a large number of Java programmers are novices due
>to the age of the language, we could say that, even though I like them,
>checked
>exceptions could perhaps have been a mistake."

I suspect that Java found the wrong solution. I think C++ can find a
better one.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

James Kanze

unread,
Feb 19, 2002, 3:18:45 PM2/19/02
to
Francis Glassborow <francis.g...@ntlworld.com> wrote in message
news:<9N26EZBl...@robinton.ntlworld.com>...
> In article <d6651fb6.02021...@posting.google.com>, James
> Kanze <ka...@gabi-soft.de> writes

> >Since all of the people I've talked to who have actually used
> >statically enforced exception specifications (in other languages)
> >have been favorably impressed by the improvement they make in
> >software reliability, I am tentatively of the opinion that they are
> >a good thing. If they aren't a good thing in C++, this would
> >suggest that there is a problem in this regard with the language
> >more than anything else.

> Which is why I want to see a serious attempt made to provide static
> checking. I believe Ada includes generics, so how do they handle
> static checking of exceptions for those? Take that as a rhetorical
> question, but I think we should be looking at their solution and
> considering if it teaches us anything we could apply to C++.

Well, Jean-Marc said that Ada doesn't have exception specifications,
and he knows the language better than I do, so that may be a bad
example. On the other hand, there's always Modula-3 -- the people I
know who've actively used statically checked exception specifications,
and with whom I've discussed the issue, all come from a Modula-3
background. And I think that Modula-3 also has some form of generic
classes.

One obvious possibility is that an out of memory condition simply
terminates the process. This limits the types of applications you can
implement, but I imagine that if we eliminate std::bad_alloc, we could
get away with saying that 1) std::vector can only be instantiated on a
type whose copy constructor and assignment operator don't throw, and
2) none of the std::vector functions throw.

If this is the way Modula-3 handles the problem, then I think we will
need to look elsewhere for a C++ solution. Because C++ is a lot more
widely used, and IMHO, neither requiring termination on out of memory,
nor refusing other exceptional errors for objects in a std::vector,
are really viable options for it; I'm not 100% convinced about the
latter, but the point is moot, since the former is definitly true.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --

-- Conseils en informatique orientée objet
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany, Tél.: +49 (0)69 19 86 27

Francis Glassborow

unread,
Feb 19, 2002, 3:36:09 PM2/19/02
to
In article <d6651fb6.02021...@posting.google.com>, James
Kanze <ka...@gabi-soft.de> writes
> > But surely it is the job of new_handlers to try for recovery with
> > pre-saved memory, or memory that can be recovered from elsewhere
> > (such as caches used by particular classes).
>
>The job of the new handlers is to try and recover memory, so that the
>program can continue. In this case, we know that there is no more
>memory (other than that previously saved for precisely this case),

and that is my point, a correctly written new_handler knows about such a
reserve and can pull it in.

>so
>the new handler cannot work. The importance of this particular case
>IS that new has failed; the role of a new handler is to prevent new
>from failing.

Exactly, and if new fails the only recourse open to the programmer is to
reduce the requirement and try again. Releasing reserves should not be a
manual process.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

Dave Harris

unread,
Feb 19, 2002, 10:37:13 PM2/19/02
to
david.a...@rcn.com (David Abrahams) wrote (abridged):

> What I can't understand is how you think it's going to
> help to narrow down which code might throw bad alloc (specifically).

It's more useful to narrow down code which will definitely not throw bad
alloc. These are routines which can still be used when the heap is
exhausted. The issue here is not so much unexpected() as a probable
failure and infinite recurse.

All no-throw routines are included, of course. But what do you think
about:

class NoConsole {};

void PrintToConsole( const char *message ) throw(NoConsole);

This can fail in a way unrelated to the heap. The exception specification
is useful only if it is checked statically.

The main alternative design is along the lines of:

enum ErrorStatus { OK, NoConsole };

ErrorStatus PrintToConsole( const char *message ) throw();

I suspect this no-throw routine is actually better for use by an error
handler. However, if the same routine may also be used by normal code, and
if NoConsole failures are rare, then the exception version might be better
than forcing people to check the return value after every call.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Francis Glassborow

unread,
Feb 20, 2002, 12:07:26 AM2/20/02
to
In article <3C729687...@web.de>, Alexander Terekhov
<tere...@web.de> writes

> How can this mess be fixed? There is actually a very
> simple solution: just make the default exception declaration
> for methods "throws Exception".

So what do you think it is at the moment? If it isn't that (your
statement is a little ambiguous) then code will break.


--
Francis Glassborow
Check out the ACCU Spring Conference 2002
4 Days, 4 tracks, 4+ languages, World class speakers
For details see: http://www.accu.org/events/public/accu0204.htm

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

Dave Harris

unread,
Feb 20, 2002, 3:54:57 AM2/20/02
to
tere...@web.de (Alexander Terekhov) wrote (abridged):

> Since Pthreads cancellation == exception (in C++ at least),
> you might want to read this:
>
> http://www.opengroup.org/onlinepubs/007904975/xrat/xsh_chap02.html#tag_0
> 3_02_09_24
> ("Interaction of Cancelation with Asynchronous Signals")
>
> Also, see "Async-Cancel Safety"...

Thanks for the link. I believe it supports my case: async signals cannot
be allowed to inject exceptions willy-nilly into other code.

I am not familiar with pthreads, but it looks like it provides a facility
to disable thread cancellation temporarily. A compiler which supported
pthreads directly, could disable cancellation on entry to no-throw
routines, and enable it on exit. Blocking calls like read() could not be
no-throw, because any blocking call can be cancelled.

Pthreads is not part of the C++ standard, so it is up the vendor what to
do about it, or even if they want to support it at all.

As the article says, all of this stuff needs careful design. You can't
take a non-threaded library and expect it work in a multi-threaded
environment unchanged. Still, good designs are possible, even with static
checking of exception specs. Indeed, static exception specs help express
and verify exactly what the design is.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

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

David Abrahams

unread,
Feb 20, 2002, 4:00:18 AM2/20/02
to

"James Kanze" <ka...@gabi-soft.de> wrote in message
news:d6651fb6.02021...@posting.google.com...

> OK. I'm not up on the terminology. I understand that there are
> basically three guarantees:

In theory there are an infinite number of guarantees, but only three prove
to be useful.

> the strong one, in which the function
> either terminates normally, or does nothing,

Right.

> then one in which the
> state isn't defined, but all invariants hold (which I thought was the
> weak one),

That's the basic guarantee.

> and one in which not even the invariants hold, and that the
> only operation supported is destruction (which I thought was the basic
> guarantee).

That's silly. The invariants always hold, or they're not invariants. What
you're describing just complicates the invariant, and the conditions under
which operations are legal. In practice it's never worth implementing. Have
you ever seen an operation which could guarantee destructibility but
couldn't be trivially made to give the basic guarantee?

> One could imagine a case in which even destruction isn't
> supported, but of course, such a class could not be practically used.

Sure it could. Some people (you?) have programs which don't use exception
handling. At the limit, exception-safety is just about documenting the
behavior without lying.

And you forgot the nothrow guarantee, which is of critical importance.

-Dave

David Abrahams

unread,
Feb 20, 2002, 4:01:34 AM2/20/02
to

"Mary K. Kuhner" <mkku...@kingman.genetics.washington.edu> wrote in message
news:a4trjk$m9m$1...@nntp6.u.washington.edu...

> David Abrahams <david.a...@rcn.com> wrote:
> >Of course, for
> >your exception-recovery code, you need to know that it's not going to
throw
> >at all (or that you're ready to give up and die if it does throw), but
why
> >focus specifically on knowing whether bad_alloc might be thrown, as
opposed
> >to anything else? If the computation is really important, shouldn't you
try
> >your usual recovery strategy in all cases?
>
> The other exceptions that I've seen it actually throw have always been
> associated with some kind of logic error. I don't want to recover
> from logic errors, because a wrong answer is worse than no answer at
> all for this application. I want the program to complain loudly and
> die.

Why anyone would use an exception for that purpose is beyond me, except
maybe in some embedded OSes where processes don't clean up resources
automatically when killed. You're obviously no dummy, however, so I'll
assume you have your reasons...

> I can think of a couple of other exceptions that wouldn't indicate
> logic error, such as the suggested out of files, out of handles,
> or out of disk space, but I don't know how to recover from these,
> so I'm not planning to try. Instead, I'll try to move these errors
> earlier in the run so that they are less painful.
>
> bad_alloc probably does not represent a logic error--the user can
> guarantee a bad_alloc by just increasing the size of her data set
> sufficiently--and can't be moved earlier in the run, so it gets
> special treatment. I hope that if I print out the intermediate
> results, the user can either free up some memory, or move to a
> larger machine, and restart in mid-computation using the stored
> results.
>
> This is pragmatic. It would be great to fix everything, but in most
> cases I can't (especially the logic errors). I would like to
> know which code can throw bad_alloc because that one I *can*
> fix, if I know where it happened. (There is no use catching it
> in main() as by then recovery will be impossible.)

I guess I still don't see how it is going to help you much to find out that
there's a function which can throw bad_alloc for which you haven't written a
handler. Is your application such that there's no central place where you
can catch bad_alloc and write your intermediate results? It's generally a
sign of a bad design if you have to write lots of try/catch blocks, but I've
never written a monte carlo simulator, so maybe I'm missing something.

-Dave

Smokey the Phat Basset

unread,
Feb 20, 2002, 7:16:42 AM2/20/02
to
Alexander: I just read your questions to Butenhof, and I am now even more
concerned by these issues. I had not quite thought through the complete
extent of the hazards of this situation before, but now thinking more about
the whole notion of thread-cancellation exceptions and ES and no-throw code
in general (such as dtors), I wonder if life isn't harder than it needs to
be:

#1)
// random non-trivial dtor:
T::~T()
{
printf("~T\n");
// as per your question: what happens if a thread-cancellation exception
is raised on the
// above line when std::uncaught_exception() == true,
// OR even if not, when just deleting elements from an array (eg pointers
to very large objects)?
}
Previously, I believed this was the most reasonable approach for dealing
with thread-cancellation under most circumstances (just ignore it), but now
I think that is invalid given the two serious issues here!

#2)
T::~T()
{
try {
printf("~T\n");
}
catch (...) {}
// what happens if a thread-cancellation exception is raised during the
printf()?
}
This is a common idiom espoused by many major C++ personalities, with
important consequences for a system where thread-cancellation is implemented
via exceptions

#3)
T::~T()
{
PTHREADS_CANCEL_DISABLE_OBJ cancelguard; // restore orig cancel state on
exit
printf("~T\n");
}
If I understand
http://www.opengroup.org/onlinepubs/007904975/functions/pthread_setcancelsta
te.html correctly,
then there is no cancellation point created when re-enabling cancellation,
so everything actually works ok here.

Previously, I believed it was ok to allow thread-cancellation to propagate
from dtors (#1 vs #2), but it looks like that is also generally a bad idea!
This implies that unless cancellation-disabling is done automatically, we
really have to manually wrap every non-trivial dtor or other nothrow code
with a similar type of cancellation-disable guard object or our system can
never be fully stable! (ohhh... the people at my company will never
understand this... I barely understand this!)

I definitely like your suggestion of cancellation-point functions checking
something like expected_exception<cancel_e>, this would actually make
throw() extremely important in a lot of places, if for no other reason than
to disable cancellation (and would even make throw(something) possibly
useful again, subject to original debate anyhow ;-)! otherwise it is simply
too onerous to correctly write any non-trivial thread-safe / exception-safe
C++ code!! I guess it will be interesting to see if he responds if/how this
is handled on Tru64/VMS?

Hillel Y. Sims
hys420 AT optonline.net

"Alexander Terekhov" <tere...@web.de> wrote in message
news:3C7210C0...@web.de...


>
> In general (for non-"throw()" operations), I would do
> it in the documentation only, unless I really want to
> specify ALL "expected" exceptions -- just to prevent
> someone from irresponsibly catching those "unexpected"
> exceptions, which is most likely the ONLY reason to
> use throw(something) ESpecs, I think.

Doesn't this invalidate your rationale for using throw(something)-type ES to
get automatic bugcheck-type diagnostics via std::unexpected()? Now you seem
to be saying you wouldn't really use that style. :-) Besides, can we agree
that 3rd party library code can never know for certain what is "unexpected"
on every system it runs on (without having to code special cases for each
system, which may be unrealistic)?

>
> But throw() is a special case. I use it quite often
> but mostly for things along the lines of swap() or
> auto_ptr-like things. If I do NOT need a function
> to throw *nothing* I'll better leave it as throw
> ANYTHING (again, "protection" mode aside). Non-
> throwing function "overloads" (std::nothrow) might
> be quite useful too in some situations.
>

ok, with the assumption that thread-cancellation is disabled..

>
> Why not just ask those vendor(s) to provide you a
> THREAD-SAFE version of the libraries that would
> include cancel exception in throw(something) for
> cancellation points or just have only throw()
> ESpecs (for things meant to be called in
> NOTHROW places), if your vendor(s) are willing
> to provides such less restrictive versions of
> their libs? ;-)

ha! With VMS, we're lucky if vendors even bother to care whether the code
compiles on our platform!Also, it is currently physically impossible to
include cancellation exception in throw(something) anyhow on Tru64/VMS.

> "Also, CATCH,
> CATCH_ALL and FINALLY clauses will not run
> when C++ code raises an exception. (These
> restrictions will be reduced or removed in
> a future release.)"
>

(this seems to be a lie in practice, at least under VMS 7.2/3... we have
TRY/CATCH/CATCH_ALL/FINALLY throughout our code and it works fine in
conjunction with C++ exceptions, which seem to get propagated as a special
type of VMS exception anyhow, as long as you don't mix them inside the same
stack frame...)

>
> > We wouldn't really want this to call unexpected() if the thread is
> > cancelled?
>
> Well, I think it would be nice if the cancellation
> points would internally call something along the lines
> of "bool future_std::expected_exception< cancel_e >()"[3]
> in addition to cancellation state checks. Also, I think
> that something along the lines of "bool future_std::
> unwinding(T*)"[4] would be quite handy too! ;-)
>
> regards,
> alexander.
>
> [1] See this c.p.t post (no response from Butenhof yet :(
>
>
http://groups.google.com/groups?as_umsgid=c29b5e33.0202150920.4c9f991a%40pos
ting.google.com

{Excessive quote snipped by moderator. -mod/fwg}

Helmut Zeisel

unread,
Feb 20, 2002, 7:17:58 AM2/20/02
to
Francis Glassborow <francis.g...@ntlworld.com> wrote in message news:<GmKEO2CV...@robinton.ntlworld.com>...

> In article <3C71FFD1...@attglobal.net>, Helmut Zeisel
> <atva...@attglobal.net> writes
> > > None. I also have no experience with statically checked domain
> > > specifications, and no experience with other static program
> > > verification techniques.
> >
> >It might be also interesting to look at the experiences in Java:
> >
> >http://www.java-zone.com/free/articles/Kabutz03/Kabutz03-1.asp
> >
> >"Seeing that a large number of Java programmers are novices due
> >to the age of the language, we could say that, even though I like them,
> >checked
> >exceptions could perhaps have been a mistake."
>
> I suspect that Java found the wrong solution. I think C++ can find a
> better one.

What should be wrong with the Java solution that
is not a problem of statically checked exception specification itself?

Statically checked exceptions force the programmers
to handle the exceptions locally
(as I understand, this is what should be preferred in Java:
my textbook says:
"It is a lot easier to catch and handle an exception than to
declare it throughout the class hierarchy. If so,
you have discovered a key benefit of Java's exception-handling
approach.";
Jamie Jaworski, Java 1.1)

The problem, however, is that
many (most?) exceptions cannot be handled locally
and should better be rethrown.

The Java experience tells that
introducing statically checked exception specification
will drastically increase the danger of incorrect handling of
exceptions.

"Incorrect handling of exceptions is the cause of most of the bugs I
have seen in novice Java programmers' code."
http://www.java-zone.com/free/articles/Kabutz03/Kabutz03-1.asp


Helmut

PS: A good decision criteria might be some empiricial evidence
how many exceptions can be handled locally.
Statically checked exception specification will help
if most exceptions can be handled locally;
OTOH if most exceptions have to be rethrown,
statically checked exception specification will increase the
error rate.

Peter Dimov

unread,
Feb 20, 2002, 9:24:45 AM2/20/02
to
ka...@gabi-soft.de (James Kanze) wrote in message news:<d6651fb6.02021...@posting.google.com>...
> pdi...@mmltd.net (Peter Dimov) wrote in message
> news:<7dc3b1ea.02021...@posting.google.com>...
> > ka...@gabi-soft.de (James Kanze) wrote in message
> > news:<d6651fb6.02021...@posting.google.com>...
> > > May I ask what actual experience you have in large scale projects
> > > using statically checked exception specifications.
>
> > None. I also have no experience with statically checked domain
> > specifications, and no experience with other static program
> > verification techniques.
>
> > > Because everyone else I've talked to who has actually used them
> > > swears by them.
>
> > Then, perhaps, you could explain what, exactly, those people find
> > appealing in static ESes, and what problems do they solve.
>
> My real answer should be to ask them, not me. I've never used
> staticly checked exception specifications either. All I notice is
> that everyone I know who has used them evaluates them very positively,
> and everyone I know who criticizes them has never actually used them.

There might be another reason for this correlation. It isn't trivial
to acquire "static ES C++" experience since compilers that check, and
standard libraries that pass those compilers, are surprisingly hard to
find. I might speculate that people that use statically checked
exception specifications don't use modern C++.

> In practice, of course, I can think of some probable reasons. What
> exceptions a function may throw is part of the contract. If this part
> of the contract can be validated by the compiler, so much the better.

Yes, so much is clear. My question is, what is gained by making the
list of possible exceptions part of the contract, and what specific
problems are caught by the compiler when it enforces this part of the
contract.

I can think of one possible bug, calling the wrong function / overload
by mistake. But this doesn't have anything to do with exceptions. It
simply uses the exception specification as a kind of semantic
identifier for the function.

> Note too that I say, may throw, rather than does throw. I may specify
> that a function may throw any exception derived from std::exception,
> for example, or even that a function may throw anything. Nothing
> requires me to lock myself into a straight jacket up front, unless I
> feel that such requirements are necessary, in order to increase the
> utility of the class. Typically, the most important contractual
> guarantee that I can give is that the function throws nothing. If I
> change this guarantee, I would really like it for the compiler to
> complain, because it is likely to break client code.

Static ES checking doesn't solve this problem. It isn't possible for
the compiler to determine that function f() depends on the nothrow
guarantee that g() provides, except in the special case where f() is
nothrow itself.

Besides, changing a nothrow guarantee is a capital offense. It's like
changing the Effects clause. It simply Must not be done(tm). :-)

The compiler may help in cases where a function with a nothrow
guarantee is implemented in such a way so that it can throw, and this
would be extremely valuable; but it isn't necessary to introduce
strict ES checking for this. Even runtime ES enforcement isn't
necessary.

> More importantly, an empty exception specification ensures that the
> function will not throw.

Yes. On the other hand, reading a specification that says "Throws:
nothing" ensures the same thing. Breaking a Throws clause and breaking
a throw() specification have similar results (in the current
language.)

> > A: The current exception specifications don't solve this problem
> > anyway, and I wonder why this kind of error is perceived as more
> > important than other, much more common and important, errors.
>
> The current specifications don't solve this problem because they
> aren't statically enforced.

Yes, of course, it's simply that the argument is inadequate (given the
current status quo.)

My real question above is why is an exception without catch() a worse
error than dereferencing a NULL pointer, double delete, out of bounds
indexing, or - my favorite - passing the wrong iterator to set::erase.
Does it happen more often in practice? Not in my experience.

> > 2. Exceptions are chaotic; every function may throw everything and
> > the programmer has no control over them.
>
> As we know, this is not true. If it were, exception safety would not
> be possible. Which exceptions are thrown may vary (although I would
> expect some standards on any project), but if I guarantee that a
> function will not throw, it won't throw. And I can often make this
> guarantee in C++.

It might or might not be true. As I said, this is a 'pro-ES' argument
that I infer from the D&E text. My interpretation may be incorrect.

> The use of statically checked exception specifications don't make the
> code exception safe. They do ensure that if a function which didn't
> throw is modified to throw, all client code must be adapted to meet
> the new constraints.

As I said, changing "Throws: nothing" to "Throws: X" is equivalent (in
damage potential) to modifying the Effects clause. It simply must not
be done without thoroughly reviewing all code that depends on the
function; an easy way to do this is to change the function name as
well.

Changing "Throws: X" to "Throws: X or bad_alloc" is a different story.
It shouldn't affect the exception safety of the code.

> If the false positives were frequent, I could have some sympathy for
> your argument. In my experience, such cases as the above are rather
> rare. (But perhaps this depends on the application domain. There are
> certainly rare in the domains I've worked in.)

I get the impression that some Java programmers have quickly learned
to litter their code with dummy catch blocks, without thinking, simply
to make the compiler happy.

This is dangerous.

The whole thing boils down to: optional or mandatory checking at
compile time? I say "optional, let the market decide what degree of
static checking is best." You say "mandatory." Fine, but what about
the other optional checks? Why are exception specifications a
priority?

(The current situation is equivalent to "no checking at compile time"
BTW since the behavior is well defined.)

Peter Dimov

unread,
Feb 20, 2002, 9:27:05 AM2/20/02
to
Francis Glassborow <francis.g...@ntlworld.com> wrote in message news:<hA4tnGCf...@robinton.ntlworld.com>...

> In article <7dc3b1ea.02021...@posting.google.com>, Peter
> Dimov <pdi...@mmltd.net> writes
> >I don't see how requiring a function to be "strict ES correct" is a
> >step towards this goal. Making violations undefined behavior allows
> >the compiler/tool to emit a diagnostic only when the degree of
> >confidence is more than a certain threshold. When 85% of the
> >diagnostics turn out to be actual bugs, the tool output will be
> >respected.
>
> Undefined behaviour that comes about because of my incompetence,
> ignorance or plain laziness is my fault. However the problem with
> uncaught exceptions is that they are outside my direct control.

A third-party library function says "Throws: X" but throws Y? First,
the undefined behavior is at their end, if undefined behavior
exception specs are used, and second, if said library function says
"Effects: X" but does Y, how is _that_ under your direct control?

> Exceptions are often raised in code that I not only did not write but
> may even be code that I cannot see (because it is delivered as object
> code).

And bugs often live in code that you didn't write (of course!) and
cannot see. No difference.

Plus, having exception specs (of any kind) doesn't solve the problem
with sourceless third-party code throwing Y.

It is loading more messages.
0 new messages