[exceptions] no-throw guarantee for trivial functions?

M

Maik

Hello,

I try to make more use of exceptions. One question which came to my
mind is when to use the no-throw guarantee or not. Consider this:

void do_something_harmless() throw() // good practice?
{ ... }

void do_something_harmful_but_will_handle_it() throw()
{ ... }

My feeling is that only functions (and ctors/dtors) whom bodies
contain the catch em all statement "catch(...)" i.e
{
try { /*danger!*/ }
/*other catch statements*/
catch(...) { }
}
should be decorated with throw().

Am I right?

Thanks,
-- Maik
 
M

Maxim Yegorushkin

I try to make more use of exceptions.  One question which came to my
mind is when to use the no-throw guarantee or not.

This does not buy you much.
Consider this:

  void do_something_harmless()  throw() // good practice?
  { ... }

  void do_something_harmful_but_will_handle_it() throw()
  { ... }

My feeling is that only functions (and ctors/dtors) whom bodies
contain the catch em all statement "catch(...)" i.e
  {
    try { /*danger!*/ }
    /*other catch statements*/
    catch(...) { }
  }
should be decorated with throw().

Am I right?

Read the full story: http://www.gotw.ca/publications/mill22.htm
 
J

James Kanze

I try to make more use of exceptions. One question which came
to my mind is when to use the no-throw guarantee or not.
Consider this:
void do_something_harmless() throw() // good practice?
{ ... }
void do_something_harmful_but_will_handle_it() throw()
{ ... }
My feeling is that only functions (and ctors/dtors) whom
bodies contain the catch em all statement "catch(...)" i.e
{
try { /*danger!*/ }
/*other catch statements*/
catch(...) { }
}
should be decorated with throw().
Am I right?

No. Most of the time, the functions for which a no-throw
guarantee is appropriate are leaf functions, which only use
basic operations which are garanteed not to throw (like
assignment to a scalar type). No need for a try/catch block in
those.
 
D

DerTopper

On 14 Jan., 15:29, Maxim Yegorushkin <[email protected]>
wrote:

[snipped question about exception specifications]

Whoa, that sounds devastating. I thought that the compiler would treat
the exception specifications a bit different. I thought that if you
specify that your function only throws a certain set of exceptions,
then the compiler will not compile your function as long as your code
is not guaranteed to throw only such exceptions:
// foo will compile, since all statements inside it won't throw
anything.
int foo (int Param) throw (A)
{
return Param + 1;
}

// goo will compile, since all statement inside it will throw at most
A.
int goo (int Param) throw (A)
{
return foo (Param) + 1;
}

// joo won't compile: The used function hoo is not guaranteed to throw
at most A.
int hoo ();
int joo () throw (A)
{
return hoo ();
}

// koo will compile: Though it uses a function that may throw
anything, this call
// is guarded by a try-catch-statement.
int koo () throw (A)
{
try
{
return hoo ();
}
catch (...)
{
}
return 0;
}

This way your compiler would help you to write code with exception
specifications without any performance penalties, which are pretty
useless anyway. Isn't that the way how Ada95 uses exception
specifications?

Regards,
Stuart
 
J

James Kanze

On 14 Jan., 15:29, Maxim Yegorushkin <[email protected]>
wrote:
[snipped question about exception specifications]
Whoa, that sounds devastating.

Yes, but it's very dated, and doesn't correspond to the general
consensus today.
I thought that the compiler would treat the exception
specifications a bit different. I thought that if you specify
that your function only throws a certain set of exceptions,
then the compiler will not compile your function as long as
your code is not guaranteed to throw only such exceptions:

And how do you guarantee that?
This way your compiler would help you to write code with
exception specifications without any performance penalties,

Exceptions specifications don't incure a performance penalty
with a decent compiler, and can, in a few cases, even lead to
improved optimization (probably only for empty ones, however).
which are pretty useless anyway. Isn't that the way how Ada95
uses exception specifications?

Ada is a different language, with different constraints than
C++.

In practice, there are two useful "guarantees" with regards to
exceptions:

-- The function never throws anything. This guarantee is more
or less necessary in a few critical places if you want to
write exception safe code. In C++, it's expressed by using
the "throw()" exception specifier.

-- The function *will* throw a specific exception for a
specific error condition. I've never seen any language
support for this, in any language.
 
M

Maik

No.  Most of the time, the functions for which a no-throw
guarantee is appropriate are leaf functions, which only use
basic operations which are garanteed not to throw (like
assignment to a scalar type).  No need for a try/catch block in
those.

Thanks for your answer.

So far I got the impression, that throw() makes most sense if the
function does something harmfull, but ensure to handle everything.

From what you wrote to dertop
<quote>
-- The function never throws anything. This guarantee is more
or less necessary in a few critical places if you want to
write exception safe code. In C++, it's expressed by using
the "throw()" exception specifier
</quote>
I get you as follows: The decision when to decorate with throw()
isn't dependent on whether the function body is covered by an catch
(...) or is harmless by nature. It depends on whether I have to be
exception safe or not.

If this is true, I have to read more on this topic to be able do make
this decision. Do you know some good readings on this?

Thanks,
-- Maik
 
R

red floyd

Thanks for your answer.

So far I got the impression, that throw() makes most sense if the
function does something harmfull, but ensure to handle everything.

From what you wrote to dertop
<quote>
 -- The function never throws anything.  This guarantee is more
    or less necessary in a few critical places if you want to
    write exception safe code.  In C++, it's expressed by using
    the "throw()" exception specifier
</quote>
I get you as follows:  The decision when to decorate with throw()
isn't dependent on whether the function body is covered by an catch
(...) or is harmless by nature.  It depends on whether I have to be
exception safe or not.

If this is true, I have to read more on this topic to be able do make
this decision.  Do you know some good readings on this?

Herb Sutter, "Exceptional C++"
 
J

James Kanze

Thanks for your answer.
So far I got the impression, that throw() makes most sense if
the function does something harmfull, but ensure to handle
everything.

Or doesn't do anything harmful, so there is nothing to handle.
The classic use of throw() is on a member function swap, where
the member function simply swaps pointers (which can't fail),
rather than doing any sort of deep copy (which presumably could
fail, with std::bad_alloc, if nothing else).

Rether than worrying about such low-level details, you really
want to start by studying the notions of exception safety, and
the different possible guarantees you can offer. Only once
you've understood what you're trying to acheve (and what you can
acheve) will the rules become understandable. (Try googling for
"exception safety guarantee", and look for anything written by
David Abrahams. For starters:
http://www.boost.org/community/exception_safety.html, although
that may already assume a certain knowledge.)
From what you wrote to dertop
<quote>
-- The function never throws anything. This guarantee is more
or less necessary in a few critical places if you want to
write exception safe code. In C++, it's expressed by using
the "throw()" exception specifier
</quote>
I get you as follows: The decision when to decorate with throw()
isn't dependent on whether the function body is covered by an catch
(...) or is harmless by nature. It depends on whether I have to be
exception safe or not.

It's a contractual guarantee. It depends on what you want to
guarantee for the client code. Including in possible future
versions. A throw() restricts the possible evolution of the
function, but allows certain uses in client code that wouldn't
be possible otherwise.

Exception safety is a larger issue. *All* code must be
exception safe, period. What it means to be exception safe,
however, depends on the application, and at the class level,
what guarantees you choose to offer (which is a design
decision).
If this is true, I have to read more on this topic to be able
do make this decision. Do you know some good readings on
this?

Most of what I know has come from discussions with David
Abrahams, in news groups. Herb Sutter has also written on the
subject at various times. Be aware that our understanding of
the issues has evolved. Some earlier writings by Herb and by
Tom Cargill paint a far more negative picture than what we now
understand (although these writings played an important role in
triggering our understanding).
 
D

DerTopper

And how do you guarantee that?

I guessed my example with the functions foo, goo, hoo, joo, and koo
pretty much demonstrated how such exception specifications could be
treated by the compiler. All it had to do would be making compile-time
checks that you fulfill your "throw"-promise.
What do you think, would such a scheme make sense?


Ada is a different language, with different constraints than
C++.

Yes, sometimes one can envy the guys that can use such a good compiler
(working in the industry, I'm pretty much bound to compilers that are
understood by the majority of programmers).

Thanks,
Stuart
 
J

James Kanze

I guessed my example with the functions foo, goo, hoo, joo,
and koo pretty much demonstrated how such exception
specifications could be treated by the compiler. All it had to
do would be making compile-time checks that you fulfill your
"throw"-promise. What do you think, would such a scheme make
sense?

It might have made sense had exception specifications been there
from the beginning. Today, there are too many existing
functions which in fact can't throw, but don't have empty
exception specifications.

There's also the problem that some functions can only throw if
passed specific arguments; if you check for those arguments
before hand, and don't pass them, the function can't throw.
Personally, I'd have nothing against requiring them to be in a
try block anyway (with an abort in the catch clause, since we
know it can't happen), but I'm afraid I'm in a minority here.
Yes, sometimes one can envy the guys that can use such a good
compiler (working in the industry, I'm pretty much bound to
compilers that are understood by the majority of programmers).

The quality of the compiler is often independent of the quality
of the language. But in general, there seems to be a rule that
a well designed language can't become wide spread. Much of
C++'s popularity stems from the fact that it derives from C.
Many of its problems, too.
 
R

Rune Allnor

Rether than worrying about such low-level details, you really
want to start by studying the notions of exception safety, and
the different possible guarantees you can offer.  Only once
you've understood what you're trying to acheve (and what you can
acheve) will the rules become understandable.  (Try googling for
"exception safety guarantee", and look for anything written by
David Abrahams.  For starters:http://www.boost.org/community/exception_safety.html, although
that may already assume a certain knowledge.)

Any suggestions for more material on exception safety?

Rune
 
M

Maxim Yegorushkin

On 14 Jan., 15:29, Maxim Yegorushkin <[email protected]>
wrote:
[snipped question about exception specifications]
Whoa, that sounds devastating.

Yes, but it's very dated, and doesn't correspond to the general
consensus today.

It would be interesting to know what the general consensus today is.

[]
Exceptions specifications don't incure a performance penalty
with a decent compiler,

Could you post compiler names please?
and can, in a few cases, even lead to
improved optimization (probably only for empty ones, however).

Could you show those cases please?

[]
In practice, there are two useful "guarantees" with regards to
exceptions:

 -- The function never throws anything.  This guarantee is more
    or less necessary in a few critical places if you want to
    write exception safe code.  In C++, it's expressed by using
    the "throw()" exception specifier.

Are you talking about overriding std::exception::what() or something
else?

What are those few critical places?
 
J

James Kanze

On Jan 15, 4:28 pm, (e-mail address removed) wrote:
On 14 Jan., 15:29, Maxim Yegorushkin <[email protected]>
wrote:
[snipped question about exception specifications]
On Jan 14, 1:41 pm, Maik <[email protected]>
wrote: Read the full
story:http://www.gotw.ca/publications/mill22.htm
Whoa, that sounds devastating.
Yes, but it's very dated, and doesn't correspond to the
general consensus today.
It would be interesting to know what the general consensus
today is.

That no-throw exception specifications are generally advisable,
when applicable.
[]
Exceptions specifications don't incure a performance penalty
with a decent compiler,
Could you post compiler names please?

Could you post one where it does have a performance penalty. I
don't know of one. (It certainly has none with Sun CC, nor
g++.)
Could you show those cases please?

Not off hand, but as always, the more information a compiler
has, the better it can optimize. Calling a function without a
no throw exception specification introduces an additional flow
path into the program.
[]
In practice, there are two useful "guarantees" with regards
to exceptions:
-- The function never throws anything. This guarantee is more
or less necessary in a few critical places if you want to
write exception safe code. In C++, it's expressed by using
the "throw()" exception specifier.
Are you talking about overriding std::exception::what() or
something else?

Of course not.
What are those few critical places?

Just about all destructors, for starters. swap() member
functions, if you're using the swap idiom for assignment. More
generally, any time you have to temporarily invalidate the
invariants.
 
K

Kai-Uwe Bux

James Kanze wrote:

[snip]
Could you post one where it does have a performance penalty. I
don't know of one. (It certainly has none with Sun CC, nor
g++.)



Not off hand, but as always, the more information a compiler
has, the better it can optimize. Calling a function without a
no throw exception specification introduces an additional flow
path into the program.
[snip]

And so does calling a function with a no throw exception specification
because, if I recall correctly, the specification is not enforced at
compile time but at runtime. Should an unlisted exception try to escape the
function, unexpected() will be called. That means, the control flow
machinery is still in place, just the target for the jump is different.

On the same note: the compiler does not really have _more_ information. It
still has no guarantee that no exceptions will be thrown. It just has to
take different measures to deal with them.


Best

Kai-Uwe Bux
 
J

James Kanze

James Kanze wrote:
[snip]
Exceptions specifications don't incure a performance
penalty with a decent compiler,
Could you post compiler names please?
Could you post one where it does have a performance penalty.
I don't know of one. (It certainly has none with Sun CC,
nor g++.)
and can, in a few cases, even lead to improved
optimization (probably only for empty ones, however).
Could you show those cases please?
Not off hand, but as always, the more information a compiler
has, the better it can optimize. Calling a function without
a no throw exception specification introduces an additional
flow path into the program.

And so does calling a function with a no throw exception
specification because, if I recall correctly, the
specification is not enforced at compile time but at runtime.

But it's enforced by the usual exception handling mechanisms, in
the called function. When compiling the caller, the compiler
doesn't have to consider the possibility that the function may
return somewhere else (e.g. a catch clause, or whatever is used
to call the destructors).

And of course, the usual exception handling mechanism doesn't
have any runtime cost unless an exception is actually thrown.
Should an unlisted exception try to escape the function,
unexpected() will be called. That means, the control flow
machinery is still in place, just the target for the jump is
different.

You can look at it that way, but the effective result is the
same as if the called function had called abort. Unlike the
case when there's no exception specification, there are no
additional paths that are relevant in the calling function.
On the same note: the compiler does not really have _more_
information. It still has no guarantee that no exceptions will
be thrown. It just has to take different measures to deal with
them.

It knows that no exceptions will perculate out of the functions,
and thus, that there cannot be a precipitous jump to the
destructors of local variables when the function is called.

As an example of how this could affect optimization, imagine a
class with all of its functions inline. The compiler can keep
parts of the class in registers in some parts of the client
function, because it can see all use of them. If you call a
function which might throw, however, it has to somehow ensure
that the class data is where the destructor expects it, and not
just where it should be at the point of return from the
function.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,905
Latest member
Kristy_Poole

Latest Threads

Top