Topic: exception specifications: std::terminate() why?


Author: Frank Flintstone <kshaw@sadmail.com>
Date: Fri, 22 Dec 2000 19:07:02 GMT
Raw View
Hi,

I'm curious to know why exception specifications are handled the way
they are.  I've read explanations in the design and evolution book,
and in the third edition Stroustrup c++ book, and in the ARM. But the
reasons seem non-sensical to me.

The reason I read for having terminate called by default I interpret
as: an unhandled exception should be considered serious by default and
your program should not be able to continue.

This seems out of character with other reasoning I've read regarding
the design of C++, and it seems like a bad decision to me. Blowing up
is not a reasonable way to handle errors, I think...

The main reason for not doing compile time checking that I've read
(e.g. on pg. 396 of design and evolution) I interpret as: compile time
checking could require massive recompilation of large programs if a
function in a library changes it's exception specification.

I don't understand. I thought the possibility of an unhandled
exceptions was considered such a severe problem that the default
behavior for violations of an exception specification was to stop the
program dead in it's tracks!

If an exception specification changes in a library and there is a
matching prototype in a header file, then a compiler could flag an
error or warning. Isn't this good? If it's specified, shouldn't it be
rather important, so you'd want to recompile if possible? If it were a
warning, you could simply ignore the warning if recompilation were not
feasible.

If the function is buried in the library such that there is no
prototype for the compiler to read, then it could miss reporting
errors about exception specifications for those functions. But, this
is no worse than having no compile time checking of exception
specifications at all.

It seems to me that it would be desirable for runtime behavior to be
the same for these:

void asdf() throw(E) { throw Q; }
void asdf() { throw Q; }

or for the first to be equivalent to:

void asdf() { throw std::bad_exception; }

I would think that the value of exception specifications would be in
compile time checking (like function prototypes). At runtime, it seems
to me that there should be a reasonable way of recovering from
unhandled exceptions, as the default. The standard libraries
implementation seems backward to me (no compile time checking and your
program blows up if an unhandled exception gets thrown).

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Ron Natalie <ron@sensor.com>
Date: Fri, 22 Dec 2000 20:14:59 GMT
Raw View

> I'm curious to know why exception specifications are handled the way
> they are.  I've read explanations in the design and evolution book,
> and in the third edition Stroustrup c++ book, and in the ARM. But the
> reasons seem non-sensical to me.
>
My making an exception specification, you are making a promise not
to throw anything else.  Violating that promise is one of the C++
mortal sins and the penalty is terminate.

> It seems to me that it would be desirable for runtime behavior to be
> the same for these:
>
> void asdf() throw(E) { throw Q; }
> void asdf() { throw Q; }
>
> or for the first to be equivalent to:
>
> void asdf() { throw std::bad_exception; }

But the caller of asdf() was promised that only E execeptions would
get this far.  No other exceptions are allowed.  You are changing
the behavior by saying that bad_exception is magic exception that
may leak out of a clause that can't throw one.

As others have pointed out, without rigorous accross translation unit
verification of the throw, the whole concept is bad.  Your example is
clearly trivial but if there are objects or subroutine calls made inside
the function with the throw exception, the same guarantees need apply
to them.  If you want to work around this in the terms of the standard
the way is clear:

void asdf() throw(E, std::bad_exception) {
    try {
 FunctionThatMayThrowSomethingOtherThanE();
    } catch(E) {
 throw;
    } catch(...) {
 throw std::badexception;
    }
}

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Pete Becker <petebecker@acm.org>
Date: Fri, 22 Dec 2000 23:46:13 GMT
Raw View
Ron Natalie wrote:
>
> As others have pointed out, without rigorous accross translation unit
> verification of the throw, the whole concept is bad.

Even with across translation unit verification the concept is bad. In
the latest issue of The Java Report one columnist recommends using
unchecked exceptions instead of checked exceptions because having to
deal explicitly with exception specifications leads to bad code. (Mostly
because when the compiler complains that some exception hasn't been
handled the reaction is often to simply add a try block and an empty
catch clause for that exception).

(Oddly enough, that issue also has an article about memory management,
which garbage collection was supposed to save programmers from, and an
article about how to write conditional code in Java with a hand-written
preprocessor)

--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Contributing Editor, C/C++ Users Journal (http://www.cuj.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Frank Flintstone <kshaw@sadmail.com>
Date: Sat, 23 Dec 2000 07:24:57 CST
Raw View
Ron Natalie <ron@sensor.com> writes:

> My making an exception specification, you are making a promise not
> to throw anything else.  Violating that promise is one of the C++
> mortal sins and the penalty is terminate.

But, I don't understand why this was thought to be a good idea. I
noticed in the design and evolution book that it mentions design rules
for C++, one of which is "Don't try to force people", where it says
essentially (as I interpret it) this tactic is not useful and will
fail anyway, because programmers are smart and will workaround what is
not useful for their particular problem. And, I've encountered
numerous similar comments through out various Stroustrup books.

It seems to me that it is useful to have a compiler help you find
syntax and logic errors, but it's not useful to have a feature where
your program blows up if you or someone else make a mistake.

What's wrong with just letting unspecified exceptions get handled as
any unhandled exception is? That way, you have the option to catch the
exception within some context appropriate to your program, and
possibly even recover from the error.

> > void asdf() throw(E) { throw Q; }
> > void asdf() { throw Q; }
> >
> > or for the first to be equivalent to:
> >
> > void asdf() { throw std::bad_exception; }
>
> But the caller of asdf() was promised that only E execeptions would
> get this far.  No other exceptions are allowed.  You are changing
> the behavior by saying that bad_exception is magic exception that
> may leak out of a clause that can't throw one.

Yes. Or, take the first example, where the unspecified exception is
not prevented at runtime. I don't see how it's useful to ask to be
crashed if a function you call breaks it's promise.

> As others have pointed out, without rigorous accross translation unit
> verification of the throw, the whole concept is bad. Your example is
> clearly trivial but if there are objects or subroutine calls made inside
> the function with the throw exception, the same guarantees need apply
> to them.

If I missing something, can you elaborate? For external functions that
are directly called, there would be a header file to check. For the
functions those functions call, where there is no header file to
check, the missed error would be no worse a problem than if there was
no checking at all in the first place.

> void asdf() throw(E, std::bad_exception) {
>     try {
>  FunctionThatMayThrowSomethingOtherThanE();
>     } catch(E) {
>  throw;
>     } catch(...) {
>  throw std::badexception;
>     }
> }

I think it indicates a design flaw that I might think I would need to
repeat this pattern over and over again to account for the default
behavior.

Pete Becker <petebecker@acm.org> writes:

> Even with across translation unit verification the concept is bad. In
> the latest issue of The Java Report one columnist recommends using
> unchecked exceptions instead of checked exceptions because having to
> deal explicitly with exception specifications leads to bad code. (Mostly
> because when the compiler complains that some exception hasn't been
> handled the reaction is often to simply add a try block and an empty
> catch clause for that exception).

I'll see if I can read that column. But, I still don't get the idea,
from what you've said here. Is the complaint that empty catch clauses
cause clutter? Why not have the compiler emit a warning about
unspecified exceptions? Then you can simply ignore the warning.

It seems to me that having the compiler warn about violations of an
exception specification is at worst the same as if there is no
checking and at best useful enough to help catch some coding problems.

Syntax checking good. Runtime termination bad.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Stephen Clamage <stephen.clamage@sun.com>
Date: Sat, 23 Dec 2000 11:59:24 CST
Raw View
On Sat, 23 Dec 2000 07:24:57 CST, Frank Flintstone <kshaw@sadmail.com>
wrote:

>Ron Natalie <ron@sensor.com> writes:
>
>> My making an exception specification, you are making a promise not
>> to throw anything else.  Violating that promise is one of the C++
>> mortal sins and the penalty is terminate.
>
>But, I don't understand why this was thought to be a good idea. I
>noticed in the design and evolution book that it mentions design rules
>for C++, one of which is "Don't try to force people", ...
>
>It seems to me that it is useful to have a compiler help you find
>syntax and logic errors, but it's not useful to have a feature where
>your program blows up if you or someone else make a mistake.
>
>What's wrong with just letting unspecified exceptions get handled as
>any unhandled exception is?

You are not required to use exception specifications in your code in
any C++ program.

If you choose to use an exception specification, the language
definition guarantees that no other exception will escape from the
function. Without that guarantee, what would the exception
specification mean? "These exceptions might be thrown, and oh, by the
way, some other undocumented exception might also be thrown." You can
already express that concept by omitting the exception specification!

Thus, if you do use an exception specification, you must take care
that it is not violated. Although compilers are not required to
diagnose potential or actual violations at compile time, a compiler
can issue a warning, and I believe some do.


---
Steve Clamage, stephen.clamage@sun.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Frank Flintstone <kshaw@sadmail.com>
Date: Mon, 25 Dec 2000 00:51:27 GMT
Raw View
Stephen Clamage <stephen.clamage@sun.com> writes:

> On Sat, 23 Dec 2000 07:24:57 CST, Frank Flintstone <kshaw@sadmail.com>
> wrote:
>
> >What's wrong with just letting unspecified exceptions get handled as
> >any unhandled exception is?
>
> If you choose to use an exception specification, the language
> definition guarantees that no other exception will escape from the
> function.

So, the guarantee is that only these exceptions will be thrown, or
else we'll crash your program. I think that is a poorly formed
guarantee. The word "threat" seems more accurate to me than
"guarantee".

>From what I've read this seems obviously contrary to the spirit in
which C++ was created.

Having the specification be a guarantee is fundamentally wrong I
think. Because the type of guarantee that would be useful, can't be
made.

It would clearly be inappropriate to define "new" as a guarantee that
the requested storage will be allocated, or else your program
crashes. Or to have a library function that's guaranteed to open a
file, or crash your program.

With the implication that normal execution can be expected, a useful
guarantee concerning exception specifications would be that only
specified exceptions will be thrown. But, it's impossible to make such
a guarantee, because it's likely to be outside the control of the
individual programmer (because of external functions for example).

As with new, all that can be asserted is an expectation of some
behavior.

> Without that guarantee, what would the exception specification mean?
> "These exceptions might be thrown, and oh, by the way, some other
> undocumented exception might also be thrown."

Why couldn't it be considered an assertion? Like other possibly absurd
assertions contained in a program, this one would assert that only the
listed exceptions would escape from the function. If the assertion
turns out to be bogus at runtime, then it would be useful and possible
to have a practical solution to the problem. Solving the problem is
what is important, not getting punished for breaking a promise.

> You can already express that concept by omitting the exception
> specification!

I don't think it is the same as the definition you give above, or that
it would need to be. Omitting an exception specification could mean
the same thing it does now, as I understand it: any exception may be
thrown.

> Thus, if you do use an exception specification, you must take care
> that it is not violated. Although compilers are not required to
> diagnose potential or actual violations at compile time, a compiler
> can issue a warning, and I believe some do.

Well, that's great. But, I wish it were a requirement that at least
simple checking be done. Otherwise, how is an exception specification
useful? Even if checking were done by all compilers, there would still
be a problem as it is, because the runtime behavior is
counter-productive.

You want a programming language to be useful, right? So, how are
exception specifications useful?

My points:

1. Exception specifications seem to me to be at least useless as they
are now, given that compilers can be expected not to check for
violations at compile-time. In fact, exception specifications are
probably harmful, because by default the only remaining functionality
provided is a feature whereby you are denied an opportunity to recover
from certain errors. By default, you are specifically not helped at
compile time, and you are harmed at runtime.

2. The way the exception specifications are defined in the language
seems absolutely contrary to cited design principles and various
wording by Stroustrup at least.

3. A better idea, is to have a requirement for simple checking of
exception specifications at compile-time, and no special run-time
reaction to run-timme exceptions, unless it is to throw another
exception.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: "David Abrahams" <abrahams@mediaone.net>
Date: Mon, 25 Dec 2000 02:11:18 GMT
Raw View
"Frank Flintstone" <kshaw@sadmail.com> wrote in message
news:87g0jdeed0.fsf@pokey.pacbell.net...

> So, the guarantee is that only these exceptions will be thrown, or
> else we'll crash your program. I think that is a poorly formed
> guarantee. The word "threat" seems more accurate to me than
> "guarantee".

<snip>
While I agree with most of what you say about exception-specifications, I
don't think this characterization is fair. A call to std::terminate() is
very different from a program crash. It's probably not different enough for
most purposes, but let's at least be honest about what's happening.

> 3. A better idea, is to have a requirement for simple checking of
> exception specifications at compile-time, and no special run-time
> reaction to run-timme exceptions, unless it is to throw another
> exception.

There are two problems with your suggestion:

1. I believe that programs written with minimal rigorous
exception-specifications will become very difficult to maintain over time,
with little real benefit in robustness. I'm not going to outline all the
arguments here, but you can look at
    http://www.deja.com/getdoc.xp?AN=643994095
and
    http://www.deja.com/getdoc.xp?AN=636740048
if you care

2. It would make certain optimizations illegal which are currently allowed.
A compiler can use the fact that a function has an empty exception
specification to omit the generation of unwinding info.

I think it might be great if implementations had an optional switch (not
required by the standard) which caused some kind of compile-time checking.
Maybe a little real-world experience with that feature would be enough to
change the minds of all the people who think these checks would be
worthwhile ;-)

probably-they-will-get-stockholm-syndrome-instead-ly y'rs,
dave




---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: kanze@gabi-soft.de
Date: Tue, 26 Dec 2000 18:00:28 GMT
Raw View
Pete Becker <petebecker@acm.org> writes:

|>  Ron Natalie wrote:

|>  > As others have pointed out, without rigorous accross translation
|>  > unit verification of the throw, the whole concept is bad.

|>  Even with across translation unit verification the concept is
|>  bad. In the latest issue of The Java Report one columnist recommends
|>  using unchecked exceptions instead of checked exceptions because
|>  having to deal explicitly with exception specifications leads to bad
|>  code. (Mostly because when the compiler complains that some
|>  exception hasn't been handled the reaction is often to simply add a
|>  try block and an empty catch clause for that exception).

Anything is bad if it is done wrong, then used incorrectly.
Verification is good, in that it helps enforce the contract of the
function.  It is also requires additional effort on the part of the
programmer.  So a compromiss is involved -- for some things, the
benefits will outweigh the cost, for others not.

Verification is bad if it is only done halfway, as in Java.  Unless it
is total, you get the extra effort, without any of the benefits.  In the
case of C++, I haven't heard of a good solution for templates.  Without
it, we could only do halfway, too.  So we're better off without it.

--=20
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh=FCttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: kanze@gabi-soft.de
Date: Tue, 26 Dec 2000 18:07:10 GMT
Raw View
"David Abrahams" <abrahams@mediaone.net> writes:

|>  I think it might be great if implementations had an optional switch
|>  (not required by the standard) which caused some kind of
|>  compile-time checking.  Maybe a little real-world experience with
|>  that feature would be enough to change the minds of all the people
|>  who think these checks would be worthwhile ;-)

Interestingly, all of the people I've ever talked to who have used
compile time exception specifications in a language which got them right
(like Modula-3) swear by them.

--=20
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh=FCttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: kanze@gabi-soft.de
Date: Tue, 26 Dec 2000 18:57:44 GMT
Raw View
Frank Flintstone <kshaw@sadmail.com> writes:

|>  Stephen Clamage <stephen.clamage@sun.com> writes:

|>  > On Sat, 23 Dec 2000 07:24:57 CST, Frank Flintstone <kshaw@sadmail.c=
om>
|>  > wrote:

|>  > >What's wrong with just letting unspecified exceptions get handled =
as
|>  > >any unhandled exception is?=20

|>  > If you choose to use an exception specification, the language
|>  > definition guarantees that no other exception will escape from the
|>  > function.

|>  So, the guarantee is that only these exceptions will be thrown, or
|>  else we'll crash your program. I think that is a poorly formed
|>  guarantee. The word "threat" seems more accurate to me than
|>  "guarantee".

The intent of the guarantee is that it be something like a return type:
if I declare a function to return an int, it returns an int, and not a
double.  It's true that C++ carries over the design error of C, and a
double converts implicitly to int -- this philosophy would probably mean
just ignoring the exception.  But when C compatibility has not been an
issue, C++ tries to do things right, as far as reasonable.  If the
function declares that it will only throw an X, and nothing else, the
caller of the function knows that he only need to cope with an X.  And
if the function declares that it will not throw anything, the caller has
a guarantee that allows ignoring exception safety.

In an ideal world, there would be verification, and a compile time
error, if the exception specification was violated.  In the real world,
modern compiler technology isn't advanced enough to allow this without
introducing significant added overhead in some cases.  The result is,
like most things in the language, a compromise.

|>  From what I've read this seems obviously contrary to the spirit in
|>  which C++ was created.

|>  Having the specification be a guarantee is fundamentally wrong I
|>  think. Because the type of guarantee that would be useful, can't be
|>  made.

|>  It would clearly be inappropriate to define "new" as a guarantee
|>  that the requested storage will be allocated, or else your program
|>  crashes. Or to have a library function that's guaranteed to open a
|>  file, or crash your program.

It would clearly be wrong to define "new" as either returning a valid
pointer, or throwing bad_alloc, and then have an implementation throw
char const*.  A contract is a contract.

|>  With the implication that normal execution can be expected, a useful
|>  guarantee concerning exception specifications would be that only
|>  specified exceptions will be thrown. But, it's impossible to make
|>  such a guarantee, because it's likely to be outside the control of
|>  the individual programmer (because of external functions for
|>  example).

|>  As with new, all that can be asserted is an expectation of some
|>  behavior.

In the case of new, if the implementation is conform, it will not
violate the contract.

--=20
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh=FCttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Steve Clamage <stephen.clamage@sun.com>
Date: Wed, 27 Dec 2000 01:44:38 GMT
Raw View

Frank Flintstone wrote:
>
> Stephen Clamage <stephen.clamage@sun.com> writes:
>
> > On Sat, 23 Dec 2000 07:24:57 CST, Frank Flintstone <kshaw@sadmail.com>
> > wrote:
> >
> > >What's wrong with just letting unspecified exceptions get handled as
> > >any unhandled exception is?
> >
> > If you choose to use an exception specification, the language
> > definition guarantees that no other exception will escape from the
> > function.
>
> So, the guarantee is that only these exceptions will be thrown, or
> else we'll crash your program.

Not crash, but terminate. It's comparable to throwing an exception that
isn't caught.

> It would clearly be inappropriate to define "new" as a guarantee that
> the requested storage will be allocated, or else your program
> crashes.

Again, the situation with "new" is comparable.

Unless you take to steps to prevent it, a failure of "new" will
terminate
your program. You can choose to detect the failure yourself and try to
cope.

If you choose to use an exception specification, you are saying that
violating the specification renders your program inoperable. The
difference with an uncaught exception is that you don't unwind to the
beginning. If you don't want such immediate termination, you can take
care that the specification isn't violated, or simply not use the
specification at all.

But you have a third option as well. You can add bad_exception to the
exception specification. If an exception not in the list attempts to
escape, it gets converted to bad_exception, and escapes in that form.
It seems to me that is what you are asking for; you already have that
behavior available.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: jkauffman@my-deja.com
Date: Thu, 28 Dec 2000 14:34:11 CST
Raw View
Consider this:

I have some resource, lets say a logical connection to some peripheral,
which i wish to allow clients to access and consequently refer to by
some handle value. I might write a function like this:

HANDLE GetConnection()
{
  Connect(); // Might throw

  try
  {
    return GetHandle();
  }
  catch(...)
  {
    Disconnect(); // Also might throw
    throw;
  }
}

Given a declaration of Disconnect that looks like this:

void Disconnect();

my code is not exception safe, what happens if Disconnect throws? If
the disconnection fails (this example is a simplified version of a real
life project BTW) by throwing an exception which is then caught by my
client who carries on regardless, i've got a resource leak - a
connection with no handle with which it can be released.
I really don't want this to happen, it means that the system is in an
inconsistent state. On top of this i KNOW for a fact that the current
implementation of Disconnect cannot throw, so it isn't a problem.
But i want a guarantee that if it DID throw, for some reason, the
program would terminate. That is the best thing for it to do, i don't
want a client to be able to handle the error, because i know for a fact
that they can't. Therefore i will declare

void Disconnect() throw();


Sent via Deja.com
http://www.deja.com/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Jerry Leichter <jerrold.leichter@smarts.com>
Date: Thu, 28 Dec 2000 14:34:22 CST
Raw View
| Interestingly, all of the people I've ever talked to who have used
| compile time exception specifications in a language which got them
| right (like Modula-3) swear by them.

C++ exception handling differs from Modula-3's in only one way:  If you
provide no exception specification in Modula-3, you're saying the
function can throw no exceptions; while in C++, you're saying it can
through any exception at all.  The difference *seems* trivial, but like
the difference between C's meaning for "int f()" - f can take any set of
argument - and C++'s - f takes *no* arguments - the practical effect on
the semantics and usage of the language are profound.  If C++ had
retained C's meaning for an empty argument list, and a large body of
existing code had used empty argument lists, function call argument
checking would have been impossible in a large fraction of expressions.
Overloading for some functions in combination with so many other
functions that took arbitrary combinations of arguments would have lead
to programs that no one could understand, much less maintain.

Unfortunately, this is pretty much the situation with respect to
exception specifications in existing C++ code.  No code that was written
before exception handling declares exception specifications.  You might
think this is fine, since it obviously doesn't throw any exceptions -
but that's not true, since it can call exception-aware code that *does*
throw exceptions.  Even most code that *is* exception-aware doesn't
provide exception specifications.  C++ compilers *could* easily be
written to flag code that code that violates its exception specifica-
tions, but in practice they would very often find so many calls with no
exception specification that they would almost always have to say "this
code could throw anything".  This pretty much guarantees errors for
every piece of code that declares an exception specification - pretty
much guaranteeing that programmers won't bother (or will use catch(...)
to ignore exceptions they don't know about, which shuts the compiler up
the same say an old-style cast does for a type mismatch:  It eliminates
the symptom while leaving the problem.)

On the other hand, if C++ had gone the strict route and said that "no
exception specification means no exceptions can be thrown", *new* code
written to this standard could easily be thoroughly checked for excep-
tion correctness during compilation (as was the case with Modula-3,
which started with no existing body of code); however, virtually no
existing C++ code would have compiled without significant modification.
No one would have accepted that.  So, in practice, C++ ends up with a
hack of very limited usefulness.

       -- Jerry

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Anthony Williams <anthony_w@my-deja.com>
Date: Tue, 2 Jan 2001 20:52:06 GMT
Raw View
In article <92fisu$ao0$1@nnrp1.deja.com>,
  jkauffman@my-deja.com wrote:
> Consider this:
>
> I have some resource, lets say a logical connection to some
peripheral,
> which i wish to allow clients to access and consequently refer to by
> some handle value. I might write a function like this:
>
> HANDLE GetConnection()
> {
>   Connect(); // Might throw
>
>   try
>   {
>     return GetHandle();
>   }
>   catch(...)
>   {
>     Disconnect(); // Also might throw
>     throw;
>   }
> }
>
> Given a declaration of Disconnect that looks like this:
>
> void Disconnect();
>
> my code is not exception safe, what happens if Disconnect throws? If
> the disconnection fails (this example is a simplified version of a
real
> life project BTW) by throwing an exception which is then caught by my
> client who carries on regardless, i've got a resource leak - a
> connection with no handle with which it can be released.
> I really don't want this to happen, it means that the system is in an
> inconsistent state. On top of this i KNOW for a fact that the current
> implementation of Disconnect cannot throw, so it isn't a problem.
> But i want a guarantee that if it DID throw, for some reason, the
> program would terminate. That is the best thing for it to do, i don't
> want a client to be able to handle the error, because i know for a
fact
> that they can't. Therefore i will declare
>
> void Disconnect() throw();
>

The problem comes if you have a third party library, which contains a
function gethandle()

int gethandle() throw(exception1);

I then write a function like your example:

int dostuff() throw(exception1)
{
  connect(); // throw()
  try
  {
    return gethandle();
  }
  catch(...)
  {
    disconnect(); // throw()
    throw;
  }
}

In the next release, they decide that exception1 didn't provide enough
information in certain cases, so they change the signature of gethandle
():

int gethandle() throw(exception1,exception2)

This is fine, provided the switch to the new version is something I am
aware of, and I have documentation that says gethandle() now throws two
types of exception, and I remember (or can find) everywhere where
gethandle() is called, and check the exception specifications. This can
be a long-winded task, but at least I know about it.

However, if I don't know about the change - for example, my over-
zealous system administrator decides to update it without telling me,
or the documentation doesn't mention it - then my previously working
program will now crash for no apparent reason, due to an exception
specification violation, usually without any more meaningful message
than "Abort. (Core dumped)". Now I have to check every call into the
library (assuming I know the library has been changed) before finding
that it is the call to gethandle() that is causing the problem.

Admittedly, this is really a good argument to say that library vendors
shouldn't change the exception specification on their functions ( at
least, not to widen it), but it would be good to have some assistance
from the compiler in identifying the problem.

I was thinking about what James Kanze was saying about exception
specifications and templates wrt compile-time checking of exception
specifications. I can't see any reason why the exception specifications
of a template can't be checked at the point of instantiation, just like
every other check made on templates.

As for the other arguments about exception specifications, and the
difficulty in using with code that doesn't use them - the standard
should specify exception specifications for every function in the
standard library. Obviously, implementations are free to specify
tighter exceptions specifications (fewer exceptions allowed), but not
to loosen them, or omit them. The compiler vendor could then implement
compile-time checking, knowing that the standard library would not give
rise to "possible exception specification violation" warnings, due to
absent throw clauses.

The biggest problem I see is anything that calls user-written code via
a function pointer. Unless the exception specification is part of the
function pointer type, then any exception can be thrown by the pointed-
to-function, and thus the calling code must have no exception
specification, or have a catch(...) clause. This includes virtual
functions, called through a base class pointer or reference.

The solution: make the exception specification part of the function
type, and thus part of the function pointer type. Functions that have a
tighter exception specification can be assigned to pointers with a
loose one, but not vice-versa. Thus a throw() function can be assigned
to any function pointer that matches the rest of the signature, but one
without an exception specification can only be assigned to pointers
without an exception specification.

This works for virtual functions too, if we think of the Vtable entry
as a function pointer - derived classes can have tighter exception
specifications on the virtual functions, but not looser ones.

I cannot see any problems with this, particularly if we make such
violations merely mandatory warnings, rather than errors, and leave the
run-time behaviour as at present. This way, all current code will work
as at present, but with the possibility of a swathe of warnings about
the new exception-specification checking. Most likely, programmers
would be grateful of the assistance in identifying possible sources of
program failure, where they have used exception specifications, and it
would encourage people to use exception specifications, knowing that
they would be warned if they didn't think of everything.

You could also add a warning for "catch(...)" clauses that didn't
contain a "throw;", but that would lead to errors where people put
the "throw;" in a called function which does centralized exception
handling.

Anthony
--
alink@anthonyw.cjb.net -- Questions relating to ALINK
anthony@anthonyw.cjb.net  -- Non-ALINK questions
anthonyw.cjb.net -- ALINK home page
PGP Key at: i3.yimg.com/3/c7e5ee24/g/68fc2307.asc


Sent via Deja.com
http://www.deja.com/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]