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