Topic: Exceptions: enforcement of throw() signatures?


Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Thu, 31 Mar 1994 19:29:56 GMT
Raw View
Ulf Schuenemann (schuenem@Informatik.TU-Muenchen.DE) wrote:

: After reading lots and lots of "Re: Exceptions: enforcement of throw() signatures?"
: it is difficult to keep my thoughts together. Before all arguments I read
: get mixed up in my mind, I have to try to write down my own opinion:


: 1. SIGNATURE CHECKING:  STATIC

: I want exceptions, I want a save C++, I don't want unexpected().

: With these demands, I think the only correct way out (i.e. to abolish
: unexpected) is to have a full, real strict STATIC CHECKING of throw-
: signatures.


: 2. DEFAULT SIGNATURE:  ANY

: I believe even after the introduction of exceptions many (small,
: uncritical) programs woun't use them. I agree that the programmer should
: not be forced to bother about throw-signatures if s/he doesn't want
: to use exceptions (i.e. try/throw/catch). But with static checking (1.)
: you are forced to handle any case where an out-of-sig exception could
: be thrown.

: So I think a reasonable solutions is to have default = any.

: The ones that want to use exceptions and are interested in throw-signatures
: (like me) have to use throw().
: Maybe default = none would be even better for robust software, but I don't
: think that this would be accepted by the majority of the C++ users.

If you abolish unexpected() then you are saying that you are also abolishing
the dynamic signature check.  Therefore, the signatures are only needed
when compiled in static exception checking mode.  Since, only people who
use static checking need write signatures, then the default may as well be
what the static checkers want ... default=none.  Again, I will point out that
the dynamic checkers would not use signatures, and would not be bound by
them even if they did.  So, in effect they would get default=any.  Remember
this is _if_ dynamic checking is abolished.

: For real static checking FUNCTION-POINTERS must have throw-signtures
: like functions.
: For correctness (declaration <-> objectfile) of static checking throw-
: signatures must be encoded in the objectfile: "signature-save linkage"
: (like typesave linkage). (This does not mean overloading by throw-
: signature!)

Maybe I just don't understand this argument yet, but I see no reason for
"signature-safe-linkage" unless you are going to allow overloading based
on exception clauses.  The checking can be done the same way function
arguments are done in ANSI C.

: There are 2 problems remaining: TEMPLATES and delay-of-the-standard.

: If we do not want to restrict template-instatiations to a fixed set
: of exceptions (e.g. throw(x,y,z) ) and if we do not want to have to
: allow throw any, than we have to introduce a kind of throwof(A) to
: be able to add the throw-signature of A when instatiating the template.
: But I am afraid the discussion about something like 'throwof' would
: definately delay the standard.

I agree, I think this issue can be resolved at a later date, let's get
the basic first.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 1 Apr 1994 18:41:05 GMT
Raw View
In article <1994Mar31.192956.9566@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
> : For real static checking FUNCTION-POINTERS must have throw-signtures
> : like functions.
> : For correctness (declaration <-> objectfile) of static checking throw-
> : signatures must be encoded in the objectfile: "signature-save linkage"
> : (like typesave linkage). (This does not mean overloading by throw-
> : signature!)
>
> Maybe I just don't understand this argument yet, but I see no reason for
> "signature-safe-linkage" unless you are going to allow overloading based
> on exception clauses.  The checking can be done the same way function
> arguments are done in ANSI C.

Wouldn't you have a problem if the *declaration* of the function didn't
match the *definition*?  For instance, Joe Programmer decides to add a
throw signature to his function Foo().  Don Quixote Programmer has code
that contains a call to Foo, but doesn't have the right signature, so he
decides to change his declaration of Foo back to the old signature.

Not only does this introduce a potential unexpected exception (which the
static checkers say is what they are trying to avoid), it also could render
the call to Foo just plain wrong, depending on the exception
implementation.  It is certainly *possible* that a compiler could implement
exception propagation in a way that depends on the throw signature, if
provided, being correct.  (In other words, the generated code would be
correct only if either there were no signature or if the signature is
correct, but not otherwise.)

> I agree, I think this issue can be resolved at a later date, let's get
> the basic first.

I think many people feel that either (a) there may not be an acceptable
resolution to that problem (short of jettisoning signatures altogether), or
(b) the resolution may affect the syntax or semantics in a way that would
not be upward compatible.  I, for one, would be reluctant to accept static
checking unless these issues are resolved now.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: djones@megatest.com (Dave Jones)
Date: Mon, 4 Apr 1994 04:51:15 GMT
Raw View


Author: harrison@sp10.csrd.uiuc.edu (Luddy Harrison)
Date: Mon, 4 Apr 94 15:32:58 GMT
Raw View
djones@megatest.com (Dave Jones) writes:

>I don't know anything about how exceptions are being implemented, but
>I would have thought that the overhead would be isolated in the called
>function. Requiring different calling conventions for functions
>that can throw exceptions would create a really big mess, wouldn't it?

There is every manner of implementation technique for exceptions;
I have heard of or read about at least a dozen variations.  My point
is that if we know that a function cannot
be exited by a throw, that we don't need to make any provision for
the destruction of automatic objects in scope at a call to the
function.  I attribute this overhead to the call, because it is
the possibility of exception exit from the call that makes such a provision
necessary.  I didn't mean to imply that the calling convention itself
(i.e., register conventions, activation format, etc.) would necessarily be
different for such a call.

I understand that in some implementations of exceptions
this is a minor overhead, similar to vtable entries.  But in other
implementations (I'm not speaking of hypothetical ones but of ones
that I have seen in commercial compilers) it is a more significant overhead.
If vtable space is truly analogous, then even a penalty in space
alone is sometimes quite significant in terms of object code size.
The compilers that exist today vary, they don't use a single implementation
of exceptions and unwinding, and it seems likely to me that this will persist,
because a variety of factors bear upon the choice of an exception
implementation, including debuggers and multiprocessing and so forth.
If default signatures are taken to be "none", then whatever overhead must be
paid for the exceptional destruction of automatic objects, whether
space or time or both or whatever, can be eliminated (even for simple-
minded exception implementations).  This, it seems to me, is most in the
spirit of "pay for it only if you use it".

-Luddy Harrison




Author: schuenem@Informatik.TU-Muenchen.DE (Ulf Schuenemann)
Date: 30 Mar 1994 11:48:33 GMT
Raw View
After reading lots and lots of "Re: Exceptions: enforcement of throw() signatures?"
it is difficult to keep my thoughts together. Before all arguments I read
get mixed up in my mind, I have to try to write down my own opinion:


1. SIGNATURE CHECKING:  STATIC

I want exceptions, I want a save C++, I don't want unexpected().

With these demands, I think the only correct way out (i.e. to abolish
unexpected) is to have a full, real strict STATIC CHECKING of throw-
signatures.


2. DEFAULT SIGNATURE:  ANY

I believe even after the introduction of exceptions many (small,
uncritical) programs woun't use them. I agree that the programmer should
not be forced to bother about throw-signatures if s/he doesn't want
to use exceptions (i.e. try/throw/catch). But with static checking (1.)
you are forced to handle any case where an out-of-sig exception could
be thrown.

So I think a reasonable solutions is to have default = any.

The ones that want to use exceptions and are interested in throw-signatures
(like me) have to use throw().
Maybe default = none would be even better for robust software, but I don't
think that this would be accepted by the majority of the C++ users.

Default = any is similar to 'const' not being the default. If you don't want
to bother about pointer to const you don't use 'const' and everything compiles
fine. If you are interested in constness you use 'const' and the compiler
checks the const-correctness.

To reduce the pool of library-functions that are throw-any it would be a
possiblity to say that for EXTERN "C" functions the DEFAULT = NONE as
C-functions cannot throw exceptions by themselves.


For real static checking FUNCTION-POINTERS must have throw-signtures
like functions.
For correctness (declaration <-> objectfile) of static checking throw-
signatures must be encoded in the objectfile: "signature-save linkage"
(like typesave linkage). (This does not mean overloading by throw-
signature!)

There are 2 problems remaining: TEMPLATES and delay-of-the-standard.

If we do not want to restrict template-instatiations to a fixed set
of exceptions (e.g. throw(x,y,z) ) and if we do not want to have to
allow throw any, than we have to introduce a kind of throwof(A) to
be able to add the throw-signature of A when instatiating the template.
But I am afraid the discussion about something like 'throwof' would
definately delay the standard.

Ulf Sch   nemann

--------------------------------------------------------------------
Ulf Sch   nemann
Institut f   r Informatik, Technische Universit   t M   nchen.
email: schuenem@informatik.tu-muenchen.de
WWW:   http://hphalle2/~schuenem  (currently not available from outside)





Author: harrison@sp10.csrd.uiuc.edu (Luddy Harrison)
Date: 30 Mar 1994 14:15:30 GMT
Raw View
Ulf Schuenemann writes:

>> 2. DEFAULT SIGNATURE:  ANY
>>
>> I believe even after the introduction of exceptions many (small,
>> uncritical) programs woun't use them. I agree that the programmer should
>> not be forced to bother about throw-signatures if s/he doesn't want
>> to use exceptions (i.e. try/throw/catch). But with static checking (1.)
>> you are forced to handle any case where an out-of-sig exception could
>> be thrown.

The arguments posted here concerning the interpretation of default
signatures (that is, functions with no exception signature), whether
from the "any" camp or from the "none" camp, have focused upon the
questions of static checking, programmer convenience, and so on.
There is another aspect to the problem, of course, and that is the
efficiency of generated code.  If the compiler can establish by
compile- and link-time checking that a function cannot exit by
exception throw, then the code to invoke that function can have fewer
instructions and take less time.  If "default=any", then by default
function calls will include exception-handling code.  If on the
other hand "default=none", then by default functions are assumed to
exit normally, and the calls to them can be made simpler and faster.

-Luddy Harrison






Author: olaf@cwi.nl (Olaf Weber)
Date: Wed, 30 Mar 1994 18:32:40 GMT
Raw View
In article <HARRISON.94Mar30081531@sp10.csrd.uiuc.edu>, harrison@sp10.csrd.uiuc.edu (Luddy Harrison) writes:

> The arguments posted here concerning the interpretation of default
> signatures (that is, functions with no exception signature), whether
> from the "any" camp or from the "none" camp, have focused upon the
> questions of static checking, programmer convenience, and so on.
> There is another aspect to the problem, of course, and that is the
> efficiency of generated code.  If the compiler can establish by
> compile- and link-time checking that a function cannot exit by
> exception throw, then the code to invoke that function can have fewer
> instructions and take less time.  If "default=any", then by default
> function calls will include exception-handling code.  If on the
> other hand "default=none", then by default functions are assumed to
> exit normally, and the calls to them can be made simpler and faster.

This is in fact somewhat backward.  The administration for stack
unwinding is (as a rule) done at the `try', `catch' and `throw', and
has little impact on the efficiency of function calls.  After all,
exceptions are rare compared with function calls.  A `try' is a fairly
expensive statement.

Under both static and dynamic checking, the default `throw (...)'
means that functions without an exception signature won't suffer the
overhead of an implicit `try' around the invocation.

With static checking, and errors on a failure to catch an exception,
such code should never be needed, as the checks are explicitly present
in the code.  However, since a `helpful' implementation might throw
exceptions for such things as integer overflow, nearly every function
would either contain a `try' or throw an exception.

The current system of dynamic checks implies that the check has to be
added to just about every function with an exception signature.  This
might make inlining of these functions impossible for some compilers.

Given the C++ philosophy of `if you don't use it, you don't pay for
it', the default `throw (...)' seems the most reasonable.

-- Olaf Weber




Author: harrison@sp10.csrd.uiuc.edu (Luddy Harrison)
Date: 30 Mar 1994 20:28:46 GMT
Raw View
Olaf Weber writes:
>>This is in fact somewhat backward.  The administration for stack
>>unwinding is (as a rule) done at the `try', `catch' and `throw', and
>>has little impact on the efficiency of function calls.  After all,
>>exceptions are rare compared with function calls.  A `try' is a fairly
>>expensive statement.

class A { ... ~A(); ... };
class B { ... ~B(); ... };
extern void g();
void f()
{
 A a[100];
 B b[100];
 ...
 g();
 ...
}

In this example, does the code generated for f have to account for the
possibility that g will exit by exception throw and therefore that a[]
and b[] must be destroyed prior to the normal return f?  If so, then a
price is being paid for the fact that g might throw an exception.  If
not, and if there is nothing else in the body of f that might cause it
to be exited by an exception throw, then the overhead can be eliminated.

The overhead of a "try" is a different matter altogether.  It seems to
me that the real cost of exceptions comes in providing that all of the
constructed objects between the throw and the catch are destroyed as
the stack unwinds.  This overhead is affected greatly by exception
signatures, or at least it can be.

-Luddy Harrison




Author: matt@physics2.berkeley.edu (Matt Austern)
Date: 30 Mar 1994 21:53:48 GMT
Raw View
In article <1994Mar24.195815.1@arc.ug.eds.com> jpsa@arc.ug.eds.com (John Aldridge) writes:

>     The standard should therefore not probibit the specification
>     of signatures on function pointer types, although it will
>     ascribe no semantics to such a signature.
>
>     There seems to be a real problem with templates - can anyone
>     suggest a solution which doesn't require a lot of extra
>     semantic-free syntax to be added to the standard?

Please bear in mind that templates aren't the only place where there's
a real problem here!  I presented the example with templates just
because I thought it was the clearest example of a place where static
checking of exception signatures would present severe design problems,
but you have very similar problems with callback functions.  There,
just as with templates, static checking of exception signatures would
force programmers to make exception declarations that are either too
narrow or too broad, because the signature declaration mechanism is
insufficiently broad to capture the true semantics of many real-world
problems.

The problem, I think, is that we're conflating two distinct notions of
"static".  (I suppose conflating meanings for "static" is a C and C++
tradition!)  I'd like to distinguish between two different concepts
here.  First, compile-time checking.  Second, typographically explicit
signatures.  What I mean by the second concept is that the exceptions
a function may throw are determined just by looking at a single list
that appears in the source code for that function.  Both of these are
in a sense static, but they are very different ideas.

I suggest, in fact, that strict compile-time checking is fundamentally
incompatible with typographically explicit signatures: there are too
many times when the sensible set of exceptions that a function can
throw can't be captured in a typographically explicit declaration.  If
people want strict compile-time checking of exceptions signatures,
they'll have to come up with a more general mechanism for declaring
them.
--
Matthew Austern                       Never express yourself more clearly
matt@physics.berkeley.edu             than you think.    ---N. Bohr




Author: matt@physics2.berkeley.edu (Matt Austern)
Date: 30 Mar 1994 22:11:03 GMT
Raw View
In article <2nbouh$kt0@hpsystem1.informatik.tu-muenchen.de> schuenem@Informatik.TU-Muenchen.DE (Ulf Schuenemann) writes:


> I want exceptions, I want a save C++, I don't want unexpected().
>
> With these demands, I think the only correct way out (i.e. to abolish
> unexpected) is to have a full, real strict STATIC CHECKING of throw-
> signatures.

Another way to abolish unexpected() is just not to use exception
specifications at all.  That's what I do, in my C++ code.

Strict compile-time checking of signatures, though, is a major change
to the language---several major changes, in fact.  I suspect that a
well thought out proposal for this would be at least as complicated as
the original proposal to introduce exceptions in the first place.

Start with the easy change: exception signatures have to be added to a
function's declaration, as well as its definition.  You'd have to decide
how this interacts with overloading and ambiguity resolution.

Now the somewhat harder change: exception specifications become part
of the type of a function pointer.  This is much harder, because
you'll have to figure out how this change in the definition of
function pointer types affects when you can and can't do assignment
and conversion involving function pointers, both with and without
explicit casts.  There are a lot of issues to resolve here, including,
again, ambiguity resolution in argument matching.  And then the same
problems are repeated, again, for pointers to member functions.

Finally, the really hard part: a new declaration mechanism for
exception specification lists.  In my opinion, it has been
demonstrated pretty conclusively here that templates present serious
design problems if you have strict compile-time checking and you don't
introduce some new mechanism.  (Summary of the problem: often, the
only sensible declaration for the exceptions thrown by Foo<T>::f() is
"all of the exceptions thrown by T::g().")  Maybe that mechanism
should involve new syntax and new keywords, or maybe, as some people
have suggested, it should involve some degree of inference by the
compiler.  Either way, though, compile-time exception checking would
be badly flawed if it didn't provide some such mechanism.  Designing
this new mechanism will require quite a lot of careful thought about
alternatives, implementation details, and so on.  It'll probably also
require some evidence about real-world experience.

I, at least, am not volunteering to do the enormous work required to
submit a complete, well thought out extension proposal for all of
these things.  I also don't think there would any hope at all of
getting something so complicated, and so radical, passed.
--
Matthew Austern                       Never express yourself more clearly
matt@physics.berkeley.edu             than you think.    ---N. Bohr




Author: chase@Think.COM (David Chase)
Date: 30 Mar 1994 23:12:26 GMT
Raw View
harrison@sp10.csrd.uiuc.edu (Luddy Harrison) writes:
  [concerning default=any vs default=none for exception signatures.]
|> There is another aspect to the problem, of course, and that is the
|> efficiency of generated code.  If the compiler can establish by
|> compile- and link-time checking that a function cannot exit by
|> exception throw, then the code to invoke that function can have fewer
|> instructions and take less time.  If "default=any", then by default
|> function calls will include exception-handling code.  If on the
|> other hand "default=none", then by default functions are assumed to
|> exit normally, and the calls to them can be made simpler and faster.

This is not literally true, for quality implementations of exception-
handling.  Those use either PC-range or PC-hash tables to identify those
call sites where exceptions might appear, and to specify the action taken
in the event of an exception.  However, the possible transfers of control
must be made apparent to an optimizer, and they typically will not
increase the amount of optimization that can be performed, so there
could be a cost, though it will vary from call site to call site and
depending upon the optimization level.  For debuggable code, there
should be no cost.

Obviously, these tables must have little or no initialization costs,
which (in the case of shared libraries) means that they should be
position-independent.  This is not rocket science -- you can buy
compilers that use tables like this today (e.g., Sun's C++ 4.0).

David Chase, speaking for myself
Thinking Machines Corp.




Author: objfactory@aol.com (ObjFactory)
Date: 30 Mar 1994 23:40:02 -0500
Raw View
In article <HARRISON.94Mar30081531@sp10.csrd.uiuc.edu>,
harrison@sp10.csrd.uiuc.edu (Luddy Harrison) writes:

>There is another aspect to the problem, of course, and that is the
>efficiency of generated code.  If the compiler can establish by
>compile- and link-time checking that a function cannot exit by
>exception throw, then the code to invoke that function can have fewer
>instructions and take less time.  If "default=any", then by default
>function calls will include exception-handling code.  If on the
>other hand "default=none", then by default functions are assumed to
>exit normally, and the calls to them can be made simpler and faster.

Not necessarily. You seem to be assuming something about the implementation
that is, as the lawyers say, not in evidence. Functions which take no explicit
action on exceptions have only the requirement to invoke destructors for live
objects on the stack. Since exceptions can only be thrown by function calls (or
the equivalent) in a function body, it would seem that compile-time analysis
could yield all the information necessary to clean up if an exception is
thrown, without the need for run-time overhead. What do you think?

Bob Foster
Object Factory




Author: djones@megatest.com (Dave Jones)
Date: Sat, 26 Mar 1994 00:53:46 GMT
Raw View
A couple of times I've asked you net.people to show me what
good dynamic checking of exceptions can possibly do. A couple of
attempts have been posted, but frankly I didn't understand them. It may
just be that I am dense, but to me the postings sounded like Mammy Yoakum's
argument that Good is better than Evil: Because it is nicer. So I asked for
specific examples to illustrate the benefits of dynamic exception signature
checking. David Chase has come up with a situation where they might be of
some help during program debugging, and has provided an example. Thanks,
David. Let's take a look.



Author: chase@Think.COM (David Chase)
Date: 28 Mar 1994 15:46:32 GMT
Raw View
[Followup on Dave Jones questions about example demonstrating
"why dynamic checking is necessary".]

>   G() throw (g) {
>       try { H(); } catch (h) { ... }
>   }

>  Now, consider H:
>
>   H() throw (h) {
>       throw (i); // An exceptional exception.
>   }

> So, now, what exceptions can propagate out of G?  If we don't dynamically
> turn this "i" into "fail", then H (and G) will throw "i", contrary to what
> we wanted to be true.  This also confines the error to near its origin,
> namely "H" [...]

To understand why dynamic checking helps you with more than just
debugging, you must put on your language lawyer's hat.  (Hmmm, I'm not
sure I'm on solid ground here, because my understanding is that an
out-of-signature exception merely calls "unexpected" and I don't know
what, if any, constraints are placed on "unexpected", meaning that it
could raise its own weird exceptions.  Pretend that people don't get
too creative within "unexpected".)

Anyhow, suppose it was almost always the case that the signature on "G"
was supposed to guarantee that only a "g" would be thrown out of a call
to "G".  This means that (from G's point of view, as well as G's author)
that "H" had better only throw what is in its signature.  But, look at
what is inside "H" -- it throws "i".  Can we (in current C++) reject "H"
as an illegal program?  No, not allowed.  May we issue a warning?  Yes,
absolutely -- standards don't constrain compilers on what they may or may
not warn about.  A warning *MAY* be issued, depending upon the compiler.

So, what protects the poor author of G?  Dynamic checking.  If H throws
that exception, it will at least call "unexpected" (in C++).  In other
languages, there is some other well-defined somebody-screwed-up behavior.
The language permits H to be written badly -- there's nothing that you or
I can do about it.  You cannot write compilers and run-time systems that
only run "pretty programs" -- you must also make the ugly ones work, at
least to the minimum specified by the standard.

Now, it is possible that dynamic checking will never help you, and never
help me, but it is necessary to ensure that the language has the
well-defined property.  The reason I prefer (in the absence of
templates, which means that this really doesn't matter) default=none is
because it becomes very hard to write warning-free code -- supposed I
wrote "H" above as:

extern void print_something(char * s);

H() throw(h) {
    print_something("Hello world\n");
}

So, what does "print_something" throw?  How do I write this code to
ensure that it only throws "h"?  If someone changed the implementation
of print_something so that it now threw something in addition to "h",
how would I ever know?  The choice of "default=none" is intended to make
such subroutines rarer, and to make it clear that throwing any exception
at all is indeed unusual.  In this case, to get rid of the warning,
I'll have to insert a catch-all block, which is (in my opinion) an
extremely blunt instrument.

Note that in this (default=any) situation, you've got to have dynamic
checking if exception signatures are to have any meaning at all.  And,
though I may prefer default=none, it is much more important that
exception signatures have some sort of meaning, so I absolutely want
dynamic checking to stay in.

David Chase, speaking for myself
Thinking Machines Corp.




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 18 Mar 1994 17:58:14 GMT
Raw View
In article <1994Mar17.085542.17410@tid.es>, pascual@peggy.tid.es (Pascual Juan) writes:
> It was told in the net that current signatures are unsafe even for game
> coders (I won't play a game whith unexpected crashes). It can be generalized
> to COMERCIAL code (no one will buy a program wrote in a language who puts
> potential bombs inside and can't guarantee there is no unexpected crash).

That's a heck of a generalization!  I guess that means that no one has
bought a program in the last 40 years, because almost all of them were
written in C, Fortran, or COBOL.  None of those languages guarantees there
is no unexpected crash.

I guess all that money that Microsoft has made must just be illusion. :-)

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 19 Mar 1994 18:45:35 GMT
Raw View
In article <9403141750.AA05337@tdat.ElSegundoCA.NCR.COM> swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen) writes:
|In a previous job we had some software that check-summed itself periodically,
|and went into emergency mode if the check-sum didn't match.  How is this
|any different than catch(...)?  It had *no* *way* of knowing what actually
|was wrong - the failed check-sum could indicate anything from a major
|hardware failure to a random, temporary, bit-toggle due to cosmic rays.

Sure, in either case you don't know what caused the error.  But that's
where the similarity ends--in all other respects, the two cases
couldn't be more different.

If you get a checksum error (I assume for the code and constant
tables), that clearly shows that some operation with undefined
behavior has been executed or a hardware failure has occurred.  You
can assume nothing about the state of your program, and you cannot
recover safely.

A "catch(...)", on the other hand does not indicate "undefined
behavior".  Instead, it indicates clearly defined behavior: an
exception has been raised somewhere, and any intermediate recovery
actions (destructors, other catches) have been executed.  If your code
is written properly, you can safely continue executing the program.

    Thomas.




Author: djones@megatest.com (Dave Jones)
Date: Sun, 20 Mar 1994 00:50:00 GMT
Raw View


Author: oren@marganit.weizmann.ac.il (Ben-Kiki Oren)
Date: Sun, 20 Mar 1994 14:44:21 GMT
Raw View
Pascual Juan (pascual@peggy.tid.es) wrote:
> In article <1994Mar16.120538.21607@wisipc.weizmann.ac.il>, oren@white.weizmann.ac.il (Ben-Kiki Oren) writes:
> |> IMHO we have two types of code:
> |>
> |> Normal    Safe

> I don't like the word normal. ANSI people are defining the "normal" use of
> the language. The first column can be named "current", and fits with coders
> who have no need of safety in their code.

How about `non-critical' vs. `critical' code? The point is these types of code
don't depend on the language's rules; these are two types of attitudes towards
program failure:

      NON-CRITICAL                             CRITICAL
      ------------                             --------
     Tries to survive common errors Tries to survive all errors

     Can die gracefully on rarer ones Should in principle always go on

     Should report failure to the If dies, should guarantee safety
     calling environment, and not of environment so it can `go on'
     mess it up beyond repair  somehow

> |> No signatures   Has signatures

> |> Each function throws anything Each function throws from the
> |>      signature only

> |> Does not need checks  Needs static checks

> It was told in the net that current signatures are unsafe even for game
> coders (I won't play a game whith unexpected crashes). It can be generalized
> to COMERCIAL code (no one will buy a program wrote in a language who puts
> potential bombs inside and can't guarantee there is no unexpected crash).

Please... I emphasised the fact that the only use of the `unexpected' dynamic
mechanism is for the critical-code people as a temporary measure limited for
the testing phase and should be disabled for the released version. This is a
limited use, but it _is_ a use (to answer Dave) and as long as we don't have
static checks its better then nothing.

> Current signatures is only valid for "Hello World" and other academic
> programs. But when students go to reallity, they will have to get along
> to AVOID what you name "normal" use of the language.

Current signatures are there so people can start writing code that needs
static checking. They can (they don't have to!) use dynamic checking as a
temporary replacement to static checking (that is, limited to the testing
phase) in the released version. Definitely not "Hello World" programs!

However, such programs are the minority. Most programs are allowed to die, as
long as its done reasonably gracefully. Therefore in reality, most programmers
will be happy with the non-critical/normal/current signature-less programming
style. They'll be avoiding the demands of _critical_ code, not `normal' one.

> |> Every time a `safe' function calls a `normal' one, it should use a
> |> `catch(...)'. This will often happen when such `safe' code is forced
> |> to invoke third-party software.

> One of the "normal" coders shouted (and he was in reason) that he don't
> wants to put "throw(...)" all over his functions prototyping. This
> very first proposal was rejected.

I said that critical code calling non critical code should add a `catch(...)'
because I think that the default=any rule _makes sense_ and therefore
`throw(...)' is _not_ needed. What is your point?

> "Safer" coders will shout you the
> same way, because your proposal will make they to change all their code
> when static checking arrives.

Why? The default=any rule won't change if static checking arrives. So the
`catch(...)' around calls to such routines won't go away. The code already
written with signatures will be checked, and a few (one hopes :-) bugs will
have to be fixed, but what other changes are required?

Anyway, its not `my proposal', its the way things are right now. And they can
shout at me as much as they like, I'm not in the committee :-)

> Your proposal means that ALL the COMERCIAL code will have to adopt to
> "normal" (current) code. If it can be avoided (you hope it in second version
> of the ANSI draft) it has to be done AS SOON AS POSSIBLE to avoid lots of
> changes it can produce. The sooner it arrives, the less changes it will.

> Let's make a safe language NOW.

Why not `a perfect language last year?' :-)

Look, adding static exception checking (which is a Good Thing(Tm), don't get
me wrong) will delay the standard. Nobody really knows how exactly to do it in
templates, for example. It will definitely delay implementations. Only a few
people need it. These people can start working with the current version (with
less compiler support).

Yes, they will have to adapt to the way most of us write code, or only use the
code written by people like them. They are in minority and there is no reason
in the world the rest us should pay for the features only they need. Not in
delayed standard availability, not in delayed and more expensive
implementations, nor in the added overhead of maintaining signatures for code
which does not need them.

Now, those people needing static checking can lobby their favorite compiler
writers into adding it. This will establish prior art for pushing it through
in the next version of the standard. They can also add it to g++ themselves.
But it does not belong in the first standard version.

       Oren.

--
Life is tough and the hard ships are many.




Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 19 Mar 1994 19:46:50 GMT
Raw View
Someone writes:
|Even with default=any, signatures are useful, because you can still
|program in a fully annotated style (or you could, if someone on the
|committee would get around to making exception signatures part of a
|function's type), and static checking will catch many errors sooner
|than not performing static checking.

Let me state it clearly: I think reliance on static checking of
exception signatures is not only useless, I think it is dangerous.
Demand for it strongly suggests to me that the way exceptions are
being used is not robust.

People keep saying that they want to know "which" exceptions can occur
when calling some piece of code.  But an exception signature is
not enough to resolve this question: even with static checking, you
have to know in detail "which" exceptions of that static type can
occur dynamically inside a piece of code.  Consider:

 void f() throw(end_of_file) {
  stream1 >> x;
 }

 void g() {
  try {
   f();
  }
  catch(end_of_file) {
   ...
  }
 }

Now, someone goes in and decides to modify "f":

 void h() throw(end_of_file) {
  stream2 >> y;
 }

 void f() throw(end_of_file) {
  h();
  stream1 >> x;
 }

Well, the program still passes static exception signature checking.
Nevertheless, there may be an unexpected exception occurring in "g".
The recovery action for an end_of_file on stream1 is likely to be
completely inappropriate for an end_of_file exception raised inside
"h".  So, static checking didn't help us--we need to rely on other
design techniques in order to keep unexpected exceptions from
occurring.

The conclusion is that if you want your code to be highly robust (and
that's supposedly the reason for adding static exception signature
checking in the first place), then either you need a detailed
understanding of the dynamic behavior of your program, or you need to
be able to recover from arbitrary exceptions.  Preferably both.

I get the feeling that a lot of this commotion about exception
signatures is due to the idea that people want to use exceptions for
all error handling in their code.  That idea is probably misguided.
Exceptions are just a low-level control structure that is useful in
the implementation of some error handling code, somewhat like a
"goto".  But exceptions and exception classes are not sufficient as an
error handling system.

If you need to handle specific errors in a specific way, is may often
be better to use techniques like error codes or callbacks.  Then,
after you have finished your specific recovery action, you can use an
exception to unwind the stack and go back to some general point where
you can, say, start the next iteration in a main loop.

     Thomas.




Author: fjh@cs.mu.OZ.AU (Fergus Henderson)
Date: Sun, 20 Mar 1994 07:18:29 GMT
Raw View
jjb@watson.ibm.com (John Barton) writes:

>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>|> jjb@watson.ibm.com (John Barton) writes:
>|>
>|> >Matt's fourth option would be to allow exception specifications to include
>|> >signatures of other functions.  For example, we would allow
>|> >void  List<T>::Add(const T&) throw(xalloc, T::T(const T&)) { ... }
>|> >This declaration would mean "throws xalloc and any exception thrown
>|> >by T::T(const T&)".  When Add() is instantiated, T::T(const T&) will
>|> >be known and the specification can be converted to a list of types
>|> >and checked like xalloc.
>|> >I suggest that this is the solution, but that compilers of template,
>|> >not programmers should add the "T::T(const T&)" part implicitly.
>|>
>|> Ah, so now we have an advocate for type inference ;-).
>
>No inference is involved.  The compiler simply fills in the throw
>specification and checks it.  It compiles or it does not.

If the compiler just fills in the appropriate value for `T' and expands
the exception specification `throw(xalloc, T::T(const T&))' into xalloc
plus the exceptions thrown by `T::T(const T&)', then it's just polymorphism.
But if the compiler is supposed to automatically add the `T::T(const T&)' to
the exception specification, then it is inferring what it thinks is
the right exception specification based on the function body.

This would be particularly difficult if the function body isn't
available to the compiler, for example if it hasn't been written yet.
In general the function body should only need to be available at
link-time, not at compile time.  Your suggestion would therefore
require that static checking of exception signatures be delayed until
link-time, in the case of functions which call template functions.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au
--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Mon, 21 Mar 1994 15:51:32 GMT
Raw View
Ben-Kiki Oren (oren@marganit.weizmann.ac.il) wrote:
:       NON-CRITICAL                             CRITICAL
:       ------------                             --------
:      Tries to survive common errors Tries to survive all errors

:      Can die gracefully on rarer ones Should in principle always go on

:      Should report failure to the If dies, should guarantee safety
:      calling environment, and not of environment so it can `go on'
:      mess it up beyond repair  somehow

: > |> No signatures   Has signatures

: > |> Each function throws anything Each function throws from the
: > |>      signature only

: > |> Does not need checks  Needs static checks

[...]

: Please... I emphasised the fact that the only use of the `unexpected' dynamic
: mechanism is for the critical-code people as a temporary measure limited for
: the testing phase and should be disabled for the released version. This is a
: limited use, but it _is_ a use (to answer Dave) and as long as we don't have
: static checks its better then nothing.

[...]

: Current signatures are there so people can start writing code that needs
: static checking. They can (they don't have to!) use dynamic checking as a
: temporary replacement to static checking (that is, limited to the testing
: phase) in the released version. Definitely not "Hello World" programs!

[...]

: However, such programs are the minority. Most programs are allowed to die, as
: long as its done reasonably gracefully. Therefore in reality, most programmers
: will be happy with the non-critical/normal/current signature-less programming
: style. They'll be avoiding the demands of _critical_ code, not `normal' one.

[...]

: Look, adding static exception checking (which is a Good Thing(Tm), don't get
: me wrong) will delay the standard. Nobody really knows how exactly to do it in
: templates, for example. It will definitely delay implementations. Only a few
: people need it. These people can start working with the current version (with
: less compiler support).

: Yes, they will have to adapt to the way most of us write code, or only use the
: code written by people like them. They are in minority and there is no reason
: in the world the rest us should pay for the features only they need. Not in
: delayed standard availability, not in delayed and more expensive
: implementations, nor in the added overhead of maintaining signatures for code
: which does not need them.

: Now, those people needing static checking can lobby their favorite compiler
: writers into adding it. This will establish prior art for pushing it through
: in the next version of the standard. They can also add it to g++ themselves.
: But it does not belong in the first standard version.

I find a great deal of sanity in the above statements, but I would like to
point out several things.

I see no use at all for dynamic checking.  The so-called "dynamic checkers"
typically use no throw clauses (actually I'd wager they never use throw
clauses), thus undermining the dynamic checks (making them such
advocates "no checkers").

The "static checkers", I being one of them, typically use the pass_through
function to undermine the dynamic checks.  So, in real-life no one
even uses the dynamic checking.  I find the idea of using the dynamic
checking for test-time work interesting, but since it is not fail-safe
I would be leary (and thus would stay with the pass_through technique).   I
believe removal of the dynamic checking (but not throw clauses) is the first
step toward satisfying everyone.  Once removed the "no checkers" (formally
the "dynamic checkers") would not care what the rules for throw clauses were
since they would not write them, and their compiler would not check them.
The "static checkers" with some of the modifications to the language form
already put forth, could get a compiler, a compiler switch, or the obligatory
lint++ to perform a complete static check.

Here again I agree with your assessment that this could only happen over
time, but I'm not sure the C++ committee shares your direction toward static
checking.  Also, I'd like to see the dynamic checking aspect removed now (but
not the throw clauses) so the contention between the "no checkers" and
the "static checkers" can be resolved.  This would allow the language form
to begin changing to support static checking (again with no impact to the
"no checker" community).

P.S. Just to be pro-active to those who'd like to comment, I do realize
that the changes I suggest make throw clauses, under the current standard,
a formalized comment (since the language does not support static checking).
But this formailzed comment can be put to good use with static checking
utilities (lint++) in the short term, and _if_ the C++ language ever moves
toward static checking, the language form would already directly support it.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: chase@Think.COM (David Chase)
Date: 21 Mar 1994 15:51:48 GMT
Raw View
> > Even with default=any, signatures are useful, because you
> > can still program in a fully annotated style

> Default=any is obviously the only way to go. But I am not clear on
> what you mean by being "useful" as annotation. How does it help if it
> is dynamically checked? How is it better than a comment? Again, an
> example of a case where dynamically checked exception signatures
> are helpful would be greatly appreciated. I honestly do not see any
> use whatsoever for exception signatures that are checked at runtime. None.
> Zip! All I see is the danger of very nasty program failures. Please explain
> the benefits. Perhaps an example?

It's not obvious to me, but I'm tired of arguing the point.  As an
annotation, signatures may be used by a compiler to statically let
you know (with a warning) when a dynamic check will be inserted.  No
warnings means no dynamic checking (that is, no dynamic conversions
to "failure").

The reason dynamically checked signatures are useful is that nobody
seems to feel that iron-clad checking is a good enough idea to put
it into a language.  Not C++, not Modula-2+, not Modula-3 (I'd have to
check my Eiffel book, but I believe that their scheme is similar).

I'll try an example.  Suppose F calls G calls H, and their signatures
claim that they throw f, g, and h, respectively.  I code G something
like this:

  G() throw (g) {
      try { H(); } catch (h) { ... }
  }

This should compile with no warnings, right?  I would like it very much
that compilation with no warnings is a guarantee that G will only throw
"g", or else "fail".  Now, consider H:

  H() throw (h) {
      throw (i); // An exceptional exception.
  }

With iron-clad static checking, this won't compile, but we don't have
iron-clad static checking.  So, all we get is a warning.  And we ignore it.
So, now, what exceptions can propagate out of G?  If we don't dynamically
turn this "i" into "fail", then H (and G) will throw "i", contrary to what
we wanted to be true.  This also confines the error to near its origin,
namely "H" (conversion of an uncaught exception to "fail" or a call to
"unexpected" is the sort of thing that a debugger should point out for you).

"F", in turn, has no reason to catch "i", so if it isn't dynamically
checked, it will propagate out of there as well.

So, dynamic checking is there to protect you from sloppy coding (speaking
broadly and judgementally).  If you use exception signatures everywhere,
and clean up all the warnings, there won't be any dynamic checking.

(In practice, the machinery for the dynamic checking is all there anyway,
so it's not like you'll save great whacking hunks of time by avoiding
it, assuming that you have a decent implementation of exception-handling
to begin with.)

The transition to this style is somewhat obnoxious, but not impossible.
Even without source code, you can still write wrappers for libraries that
declare what you think the libraries are supposed to raise (and if you're
wrong, then you'll find out about it).  Until then, you'll get lots of
spurious warnings with a default=any signature rule from header files
lacking signatures.  (And one reason I believe that default=none is better
is because it centralizes the obnoxious work of getting the proper
signatures on things -- otherwise, you, me, and every other
user-of-exception-handling-who-dislikes-dynamic-checking will do the work
all by ourselves, over and over again, making stupid little errors along the
way.)

Does this clarify things somewhat?

David Chase, speaking for myself
Thinking Machines Corp.




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Mon, 21 Mar 94 09:16:52 PST
Raw View
In article <1994Mar15.171653.617@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|> Stan Friesen (swf@tdat.ElSegundoCA.NCR.COM) wrote:
|> : In article <1994Mar9.210645.26317@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|> : |> Stan Friesen (swf@tools3teradata.com) wrote:
|> : In the case of a serious unrecoverable error, you have two choices,
|> : stop doing anything (i.e. exit), or go on in minimal mode.  ...
|>
|> You said it ... "unrecoverable".  Our systems do not ship with "unrecoverable"
|> (software/resource contention) errors.  All failures with respect to
|> exceptions can be planned for if the tools provide enough information about
|> unplanned exception (ie. out-of-sig. exceptions).

Oh, and just how do you 'recover' from a disk drive off line?
Or the network connection to your server daemon dropping (due to the
remote machine crashing)?
Or ...?

What you *can* do is *handle* the error, so you do not have to exit,
but you *cannot* actually recover, at least not in the sense I meant
the term.  (You, apparently, use 'recover' to mean "not exit", I use it
to mean "adjust the the situation so the program can continue operating
as if nothing had ever gone wrong").

What I was trying to say was that if a situation comes up that is mostly
*beyond* *the* *control* of your program that involves resources nexessary
for normal operation you have two choices:
 1. exit - preferably with a clear error message.
 2. go into a reduced operations mode that does not need
    the particular resource that has gone bad.

I maintain that if #2 is required, it can be accomplished securely with
intelligently placed "catch(...)" clauses.  Note, they must be placed so
that you have some idea of what resource is unavailable - but that can
be done by placing them around resource acquisition and critical usage
segments.

|> This is a hardware failure.

Most of what I call "unrecoverable error" *is* hardware failure,
or software failure in *another* *program* (where you can do nothing
about it).

I suppose an invalid pointer/reference where it is impossible to determine the
correct pointer, and where the item refered to is necessary for the normal
full functionality of the program could be considered 'unrecoverable'
in the sense I am talking aobut, but not much less.

|> A piece of medical equipment which threw an out-of-sig. exception, would not
|> fare well so with such agencies.

Well, if there are no exception signatures, then no out-of-sig. exceptions
can be thrown :-)

|> A hardware failure and even exceptions
|> thrown due to such failures can and probably should cause software to go to
|> whatever possible minimal, sane state it can.  An exception generated by
|> software, or resource contention can and should be explicitly planned for.

Yep, and you don't need exception signatures to do so.

If you are really paranoid about such things, you can put a "catch(..)"
around every resource allocation, and treat any exception *at* *that* *point*
as a "resource temporarily unavailable" signal, except for those you know
mean something else.

Yes, it takes careful planning to set this up right.  That is what design
meetings are for.

One purpose of using "catch(...)" is to deal with situations you had no
way of anticipating.  If you could anticipate it, you should use a specific
catch clause (except for catch's for the sole purpose of clean-up).

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Mon, 21 Mar 1994 18:01:59 GMT
Raw View
Thomas M. Breuel (tmb@arolla.idiap.ch) wrote:
: Let me state it clearly: I think reliance on static checking of
: exception signatures is not only useless, I think it is dangerous.
: Demand for it strongly suggests to me that the way exceptions are
: being used is not robust.

I must have missed something.  Is this one of those "less is more" things.

: People keep saying that they want to know "which" exceptions can occur
: when calling some piece of code.  But an exception signature is
: not enough to resolve this question: even with static checking, you
: have to know in detail "which" exceptions of that static type can
: occur dynamically inside a piece of code.  Consider:

:  void f() throw(end_of_file) {
:   stream1 >> x;
:  }

:  void g() {
:   try {
:    f();
:   }
:   catch(end_of_file) {
:    ...
:   }
:  }

: Now, someone goes in and decides to modify "f":

:  void h() throw(end_of_file) {
:   stream2 >> y;
:  }

:  void f() throw(end_of_file) {
:   h();
:   stream1 >> x;
:  }

: Well, the program still passes static exception signature checking.
: Nevertheless, there may be an unexpected exception occurring in "g".
: The recovery action for an end_of_file on stream1 is likely to be
: completely inappropriate for an end_of_file exception raised inside
: "h".  So, static checking didn't help us--we need to rely on other
: design techniques in order to keep unexpected exceptions from
: occurring.

Following that logic, then we can eliminate static type checking as well,
since a variable returned from a function might not be a value we can handle.

Example:

int g() { return(99); }

void f() {
 int a[3];
 int y = a[g()];
}

Please.  The idea of static checking is to test the class (if you will) not
the object.  In both static type checking and static exception checking the
class level info. is the thing being checked, not the object.  As you have
pointed out in your code/comments, static exception checking will not
completely eliminate related error, but will, to the same extend as static
type checking does for types, prevent the vast majority of these errors.

: The conclusion is that if you want your code to be highly robust (and
: that's supposedly the reason for adding static exception signature
: checking in the first place), then either you need a detailed
: understanding of the dynamic behavior of your program, ...
: or you need to
: be able to recover from arbitrary exceptions.  Preferably both.

The objective of static exception checking is to eliminate the need to test
(understand) the dynamic behavior of a system.  The static checking will
enable potenial time-bombs to be uncovered.  This understanding is necessary
since humans can not perform this checking easily and the need to check may
be quite frequent.

: or you need to
: be able to recover from arbitrary exceptions.  Preferably both.

In the dynamic world you propose, I concur.

: I get the feeling that a lot of this commotion about exception
: signatures is due to the idea that people want to use exceptions for
: all error handling in their code.  That idea is probably misguided.
: Exceptions are just a low-level control structure that is useful in
: the implementation of some error handling code, somewhat like a
: "goto".  But exceptions and exception classes are not sufficient as an
: error handling system.

Huh, I think here you are outside even the "dynamic checkers" concept of
exception handling.  "goto" ????

: If you need to handle specific errors in a specific way, is may often
: be better to use techniques like error codes or callbacks.  Then,
: after you have finished your specific recovery action, you can use an
: exception to unwind the stack and go back to some general point where
: you can, say, start the next iteration in a main loop.

I appreciate your opinion on exception handling _strategies_, but the mechanism
is intended to provide facility to implement many strategies, not just yours.

Frankly, I am becoming tired of "dynamic checkers" telling the "static checkers"
the "appropriate" strategy to employ.  Let's discuss mechanisms, not
strategies.  Also, I think the need and benefits of static exception checking
has been thoroughly demonstrated for those of us who need it (I am not saying
everyone needs it).  I think that removal of dynamic checking is the key to
unlocking exception mechanisms that satisfy both groups.  I have not heard
one "dynamic checker" state the he/she uses throw clauses.  Rather they want
to depend on "default=throws any" (I think that's reasonable) which insures
that no dynamic checking is performed.
The static checkers want nothing to do with the dynamic checking of throw
clauses (I think that's reasonable) and thus typically use some form of the
pass_through strategy (yes I said strategy) to undermine it.  So no one is
using the dynamic checking.  Eliminating the dynamic checking allows both
groups to go about solving their problems independently.  "Dynamic checkers"
get default=throw any by nature of the fact that no checking will be done.
Static checkers are free to push for language reform in the area of
throw clauses and tools to statically check them.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 21 Mar 1994 23:01:11 GMT
Raw View
In article <1994Mar21.180159.4727@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
> Thomas M. Breuel (tmb@arolla.idiap.ch) wrote:
> : Well, the program still passes static exception signature checking.
> : Nevertheless, there may be an unexpected exception occurring in "g".
> : The recovery action for an end_of_file on stream1 is likely to be
> : completely inappropriate for an end_of_file exception raised inside
> : "h".  So, static checking didn't help us--we need to rely on other
> : design techniques in order to keep unexpected exceptions from
> : occurring.
>
> Following that logic, then we can eliminate static type checking as well,
> since a variable returned from a function might not be a value we can handle.
>
> Please.  The idea of static checking is to test the class (if you will) not
> the object.  In both static type checking and static exception checking the
> class level info. is the thing being checked, not the object.  As you have
> pointed out in your code/comments, static exception checking will not
> completely eliminate related error, but will, to the same extend as static
> type checking does for types, prevent the vast majority of these errors.

I think the point that Thomas was trying to make is this: The
static-checking advocates say they want static checking so that they can be
sure that their program is ready to handle any exception that can possibly
be thrown.  Fine so far.  But some of those advocates go further, and say
that they want to be sure their application correctly *recovers* from those
exceptions.

Thomas is saying (I think) that you can't tell how you should recover from
an exception if all you know is some base class of that exception.  Without
examining the code, in detail, you will never know whether someone derived
a new exception from some existing class that your "generic" handler
doesn't expect.  I am using "expect" in the sense that the handler *thinks*
it has enough information to recover from the exception, when in fact it
does not.

It is true that the exception will be caught, but will the program do the right
thing?  Who knows?  It may go merrily on its way, thinking it has been very smart
in recovering from this exception, only to die later because it is using corrupt
data.

Is this a design error?  COULD BE!  Is it caught by static checking?  NO.
Yet some of the static-checking advocates say that static checking will
ELIMINATE errors related to unexpected exceptions.  I think Thomas was
trying to show that that claim is patently false, and I applaud him for it.

> : The conclusion is that if you want your code to be highly robust (and
> : that's supposedly the reason for adding static exception signature
> : checking in the first place), then either you need a detailed
> : understanding of the dynamic behavior of your program, ...
> : or you need to
> : be able to recover from arbitrary exceptions.  Preferably both.
>
> The objective of static exception checking is to eliminate the need to test
> (understand) the dynamic behavior of a system.  The static checking will
> enable potenial time-bombs to be uncovered.  This understanding is necessary
> since humans can not perform this checking easily and the need to check may
> be quite frequent.

Thomas's point is that you cannot eliminate that need without detailed
design and code reviews, which you should be doing anyway.  And if you do
that, do you really need static checking?

> : or you need to
> : be able to recover from arbitrary exceptions.  Preferably both.
>
> In the dynamic world you propose, I concur.

Even in the static world, because you don't know what new classes of
exceptions have been derived from classes you already expected, but which
you aren't in fact prepared to deal with.

I would expect that, if static checking were implemented, deriving new
exceptions from old classes (even if inappropriate) would become quite
prevalent; consider the programmer who's trying to get a bug fix out in a
hurry, and he finds he has to change the exception signatures on 2000
functions!  "But wait", he says, "I'll just derive this new exception from
one of the classes on my signature list, and it'll all just work!"  And
maybe it does, because he knows (or thinks he knows) that the catch clauses
for that class *currently* do the right thing.  Of course, whether they
will do the right thing after 50 more programmers work on it, who knows?

There just aren't any magic bullets for bugs. :-(

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Mon, 21 Mar 1994 23:58:31 GMT
Raw View
Stan Friesen (swf@tdat.ElSegundoCA.NCR.COM) wrote:
: In article <1994Mar15.171653.617@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |> Stan Friesen (swf@tdat.ElSegundoCA.NCR.COM) wrote:
: |> : In article <1994Mar9.210645.26317@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |> : |> Stan Friesen (swf@tools3teradata.com) wrote:
: |> : In the case of a serious unrecoverable error, you have two choices,
: |> : stop doing anything (i.e. exit), or go on in minimal mode.  ...
: |>
: |> You said it ... "unrecoverable".  Our systems do not ship with "unrecoverable"
: |> (software/resource contention) errors.  All failures with respect to
: |> exceptions can be planned for if the tools provide enough information about
: |> unplanned exception (ie. out-of-sig. exceptions).

: Oh, and just how do you 'recover' from a disk drive off line?
: Or the network connection to your server daemon dropping (due to the
: remote machine crashing)?
: Or ...?

Did you read my reply, you know the part you deleted.  It said that hardware
failures are not recoverable items.  The FDA is not going to shutdown a
medical company because a reasonable hardware error occurred, they will
shutdown a company that has failures due to an unknown exception (one which
could have been detected at compile time).

: What you *can* do is *handle* the error, so you do not have to exit,
: but you *cannot* actually recover, at least not in the sense I meant
: the term.  (You, apparently, use 'recover' to mean "not exit", I use it
: to mean "adjust the the situation so the program can continue operating
: as if nothing had ever gone wrong").

I am not going to debate hardware failure/recovery strategies.  For that
matter I'm not going to debate exception strategies.  This debate is about
mechanisms not strategies.

: What I was trying to say was that if a situation comes up that is mostly
: *beyond* *the* *control* of your program that involves resources nexessary
: for normal operation you have two choices:
:  1. exit - preferably with a clear error message.
:  2. go into a reduced operations mode that does not need
:     the particular resource that has gone bad.


: I maintain that if #2 is required, it can be accomplished securely with
: intelligently placed "catch(...)" clauses.  Note, they must be placed so
: that you have some idea of what resource is unavailable - but that can
: be done by placing them around resource acquisition and critical usage
: segments.

Strategy.

: |> This is a hardware failure.

: Most of what I call "unrecoverable error" *is* hardware failure,
: or software failure in *another* *program* (where you can do nothing
: about it).

: I suppose an invalid pointer/reference where it is impossible to determine the
: correct pointer, and where the item refered to is necessary for the normal
: full functionality of the program could be considered 'unrecoverable'
: in the sense I am talking aobut, but not much less.

: |> A piece of medical equipment which threw an out-of-sig. exception, would not
: |> fare well so with such agencies.

: Well, if there are no exception signatures, then no out-of-sig. exceptions
: can be thrown :-)

       :-(

: |> A hardware failure and even exceptions
: |> thrown due to such failures can and probably should cause software to go to
: |> whatever possible minimal, sane state it can.  An exception generated by
: |> software, or resource contention can and should be explicitly planned for.

: Yep, and you don't need exception signatures to do so.

Strategy.

: If you are really paranoid about such things, you can put a "catch(..)"
: around every resource allocation, and treat any exception *at* *that* *point*
: as a "resource temporarily unavailable" signal, except for those you know
: mean something else.

: Yes, it takes careful planning to set this up right.  That is what design
: meetings are for.

Tick, tock, tick, tock, ... STRATEGY!

: One purpose of using "catch(...)" is to deal with situations you had no
: way of anticipating.  If you could anticipate it, you should use a specific
: catch clause (except for catch's for the sole purpose of clean-up).

I could "anticipate" a lot more if I had tools which would tell me about
new exceptions in my system.  Again, catch(...) throws away the exception
object.

Let's get back to mechanisms not strategies. And to start the ball rolling,
here's a question which I have yet to hear a good answer to.

Can anyone give me a reason for checking exception signatures at run-time?
Now wait, let me finish.  I understand and respect the "dynamic checkers"
opinions presented, but I think they are really arguing for "no checking".
Thus far all of the so called "dynamic checkers" have suggested that their
use of the exception mechanism would not use exception signatures and that
they would like default=throw anything.  Thus they are not using/arguing
for dynamic checking, they are arguing against static checking.  The static
checkers certainly don't want the dynamic checking, since it is not robust
enough.  This leads to my question, who wants true dynamic checking
(In order to qualify you must want/need to use throw clauses, and
a need to have them checked after compilation)?

We can satisfy both groups by simply annihilating the dynamic checking
(allowing "no checking"), and spec'ing the throw clause to allow static
checking tools to provide complete checking for those who want it.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.




Author: pascual@peggy.tid.es (Pascual Juan)
Date: Mon, 21 Mar 1994 08:35:31 GMT
Raw View
In article <1994Mar20.144421.10109@wisipc.weizmann.ac.il>, oren@marganit.weizmann.ac.il (Ben-Kiki Oren) writes:
|>
|> I said that critical code calling non critical code should add a `catch(...)'
|> because I think that the default=any rule _makes sense_ and therefore
|> `throw(...)' is _not_ needed. What is your point?
|>
|> Look, adding static exception checking (which is a Good Thing(Tm), don't get
|> me wrong) will delay the standard.
|>        Oren.

I have to apologize. The day I wrote my post was a hard day (I've been out
of the net for a few days due to an excess of work).

You are in reason. When I started to read your post I thought "Hey, this
post has good sense!", but my brain got "unexpected()" and I wrote an stupid
followup. I've been reading answers to my last postings and I can realize
that my point of view has been getting extremist in this thread, so I have
to apologize all the persons I replied in my last postings.

I got this thread last week like a "personal war" and It makes me feel a
little bit shy :-( but as I said in a previous post "humans can be wrong".

                              SORRY :-)
------------------------------------------------------------------------------
By the way, your posting gave me an idea on how to help "critical code" with
current definition of the language:

#define extra_safe                                                     \
  catch (...)                                                          \
  {                                                                    \
    log(__FILE__, __LINE__); // Use your favorite log function to      \
  }                          // say where an "unexpected" was catched.

Critical coders will have to put a call to this macro at the end of every
"try" block and test the product forcing all the "known" exceptions to be
forced in testing cycle.  If no unexpected has been logged through
"extra_safe" macro, it can be said that "no known exception thrown is
unexpected" (this is not a complete safety, but it is more than nothing).

Notice that critical coders will have to do the work that had to be done by
the compiler. Static checking is still needed, don't loose the point.

Let's go on making a safer language.
--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sun, 13 Mar 1994 23:38:44 GMT
Raw View
chase@Think.COM (David Chase) writes:

>The committee should consider
>at least providing syntax for exception signatures on function types,
>even if they have no semantic force, because many programmers (Steve
>Cipolli, me) would find them useful.

Such syntax is already provided by the grammar in the ARM.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: jjb@watson.ibm.com (John Barton)
Date: Mon, 14 Mar 1994 13:05:05 GMT
Raw View
In article <MATT.94Mar11140536@physics16.berkeley.edu>, matt@physics16.berkeley.edu (Matt Austern) writes:
|> In article <CMEH6J.14L0@hawnews.watson.ibm.com> jjb@watson.ibm.com (John Barton) writes:
|>
|> > Matt's fourth option would be to allow exception specifications to include
|> > signatures of other functions.  For example, we would allow
|> > void  List<T>::Add(const T&) throw(xalloc, T::T(const T&)) { ... }
|> > This declaration would mean "throws xalloc and any exception thrown
|> > by T::T(const T&)".  When Add() is instantiated, T::T(const T&) will
|> > be known and the specification can be converted to a list of types
|> > and checked like xalloc.
|> >
|> > I suggest that this is the solution, but that compilers of template,
|> > not programmers should add the "T::T(const T&)" part implicitly.  Thus
|> > the exception specification for a member function template would be
|> > xmsg plus any explicitly declared exceptions plus any exceptions
|> > specified by parameterized function calls.
|>
|> I'm not at all sure that I like the idea of a compiler automatically
|> putting additional exception in the exception specification.  These
|> sorts of inferences can often be dangerous (people even get bitten by
|> automatically generatated assignment operators and copy constructors),
|> and I'm nervous about having them added without some very good reason.

More dangerous than ignoring the exceptions altogether? (You supplied
the good reason by the way.)

|>
|> In this case, specifically, not every programmer will want to have the
|> exception signature of every template member function include the
|> signature of every parameterized function call.  This is appropriate
|> in the specific example that I gave (the exception signature of
|> List<T>::Add(const T&) ought to include the exceptions thrown by
|> T::T(const T&)), but it isn't always appropriate.  Sometimes you
|> really do want a Foo<T>::Bar() that, independent of T, will only throw
|> a certain set of exceptions.
|>

So use a try block and trap out all the called exceptions.
C++ also provides template specialization to handle unusual
template cases.

|> The reason I brought up the List<T>::Add() example, in any case, was
|> just to demonstrate that the exception signature mechanism isn't
|> expressive enough to allow static checking in all circumstances unless
|> you impose severe restrictions on other aspects of the language, such
|> as templates.  It looks like most people agree, at least in this
|> particular case, so we've seen a few different suggestions for
|> generalizing exception signatures.  That's one possible response; my
|> own response is to give up on the idea of statically checked exception
|> signatures, and to use exception signatures sparingly, if at all.
|> --
|> Matthew Austern                       Never express yourself more clearly
|> matt@physics.berkeley.edu             than you think.    ---N. Bohr

I think we should explore the static idea to the limit including
testing it in implementations because in our own experience the
current runtime mechanism is not useful.

--
John.

John J. Barton        jjb@watson.ibm.com            (914)784-6645
H1-C13 IBM Watson Research Center P.O. Box 704 Hawthorne NY 10598




Author: chase@Think.COM (David Chase)
Date: 14 Mar 1994 16:54:40 GMT
Raw View
djones@megatest.com (Dave Jones) writes:

|> .... But many people, (myself included) now believe
|> that dynamaic checking is totally useless at best and can introduce horrible,
|> nonrepeatable bugs at worst. I think one can make a very good case for
|> ditching exception signatures all together. I read an article that claimed
|> they were only proposed in the first place because of some bullying by
|> compiler vendors. Why they would want to make more work for themselves
|> is a puzzlement.

To my knowledge, it was not bullying by compiler vendors that led to
exceptions being in the language.  My understanding is that some people
at Sun working on Spring wanted them in (for reasons that I agree with)
but those people are not compiler vendors.

As to why compiler vendors want more work or not, there are many ways
to view this:

1. you are drastically overestimating the relative difficulty of implementing
   exceptions, and implementing their run-time system (I speak as someone who
   has implemented or assisted in the implementation of three different
   exception systems).  There are some interesting tricks involved, but
   in the grand scheme of things, it's not much.

2. compiler writers are also programmers.  Some of us have used exception
   handling in the past, and think it is a very good idea (and it is unlikely
   that I'm going to pay much attention to "reasons to not use exception
   handling" or discussions of EH style from someone who has never even
   tried it, except to try to understand what the common misunderstandings
   are).  Understand too that former colleagues of mine wrote large amounts
   of code with EH, and experimented with different styles, and got feedback
   from users on what their compilers did and did not do.

3. there's a tremendous amount of leverage in work put into the compiler.
   Depending upon the vendor, a given compiler will have hundreds, thousands,
   or tens of thousands of users.  A little effort at compiler-writing time
   to simplify life for 1000 other people is not a bad tradeoff.

4. what's wrong with lifetime employment, anyway?  Compiler writers should
   lobby for some really difficult stuff.

|> I think a very good intermediate solution would be to implement only
|> two signatures: throws-any (default), and throws-none. I predict that after
|> people figure out what the signatures are actually good for, then only
|> those two signatures will typically be used anyway. The throws-none could be
|> checked (statically) very easily.

Your intermediate solution is completely unacceptable.  Listen to people who
have programmed in languages with exception-handling in them (instead of people
who have not).  Even with default=any, signatures are useful, because you
can still program in a fully annotated style (or you could, if someone on the
committee would get around to making exception signatures part of a function's
type), and static checking will catch many errors sooner than not performing
static checking.

> I'm giving a talk on exceptions for my company on the 25th. I will recommend
> in no uncertain terms that until such time as exception signatures are checked
> statically, they will be strictly outlawed in the company coding standard.

This seems peculiar to me -- what do you do with all your unadorned code when
you finally get a compiler with static checking?  Even if  you cannot find a
compiler that performs static checking (it could be added to gcc, after all),
I think that you should be able to find a style checker that would do this
(or could be made to do this) for you.

David Chase, speaking for myself
Thinking Machines Corporation




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Mon, 14 Mar 94 09:50:47 PST
Raw View
In article <1994Mar9.210645.26317@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|> Stan Friesen (swf@tools3teradata.com) wrote:
|>
|> I wish my world was as simple as yours.  That catch(...) should be a really
|> interesting piece of code.  You don't know what went wrong, nor why, nor at
|> what point in the code, but you're gonna march on anyway!?  Either you are
|> quite couragous or you are not planning to ever undergo surgery.

In the case of a serious unrecoverable error, you have two choices,
stop doing anything (i.e. exit), or go on in minimal mode.  The catch(...)
simply assumes that it has caught an arbitrary unrecoverable error, and
switches the program to minimal mode.

In a previous job we had some software that check-summed itself periodically,
and went into emergency mode if the check-sum didn't match.  How is this
any different than catch(...)?  It had *no* *way* of knowing what actually
was wrong - the failed check-sum could indicate anything from a major
hardware failure to a random, temporary, bit-toggle due to cosmic rays.
So, the software had to assume the worst - that essentially nothing was
trustworthy - and muddle on as best as possible (in this case lock the
system, so that the patient could not recieve an accidental overdose
of the medication being metered).

|> ...but seriously, the point being made is not that the program must keep
|> running (it definitely must), but that when an exception occurs it is caught
|> by code which expects to handle such an emergency.  The exception objects
|> may transmit enough information to completely recover or it may indicate
|> what minimum level of operation is possible.

Quite, and for every type of error for which recovery is *possible*,
you put in a *specific* catch to catch it, and the same goes for every
type of situation for which there is a specific avoidance/minimization
procedure.  It is *only* those exceptions for which you *have* no known
handling procedure that you allow to fall through to the catch(...).
It is an "oh shit, I'm lost" handler, not a normal exception handler.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: daniels@biles.com (Brad Daniels)
Date: Mon, 14 Mar 1994 20:34:41 GMT
Raw View
In article <9403141750.AA05337@tdat.elsegundoca.ncr.com>,
Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
...
>Quite, and for every type of error for which recovery is *possible*,
>you put in a *specific* catch to catch it, and the same goes for every
>type of situation for which there is a specific avoidance/minimization
>procedure.  It is *only* those exceptions for which you *have* no known
>handling procedure that you allow to fall through to the catch(...).
>It is an "oh shit, I'm lost" handler, not a normal exception handler.

One of the points behind having some kind of static checking would be to
avoid most if not all of the cases where you get lost because of unexpected
exceptions.  Emergency modes are rarely terribly useful, and should as a rule
be avoided where possible, especially since in cases where they exist, they
often simply perform some kind of safe, ordered shutdown.  Being able to use
a language feature to avoid one class of such shutdowns would be extremely
useful to those engaged in such programming.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: djones@megatest.com (Dave Jones)
Date: Tue, 15 Mar 1994 00:50:19 GMT
Raw View


Author: bs@alice.att.com (Bjarne Stroustrup)
Date: 15 Mar 94 01:08:34 GMT
Raw View

djones@megatest.com (Dave Jones @ Megatest Corporation) writes

 > I read an article that claimed
 > they <exception specifications> were only proposed in the first place
 > because of some bullying by compiler vendors.

Not true.

 > Why they would want to make more work for themselves
 > is a puzzlement.

Indeed.

 - Bjarne




Author: matt@physics16.berkeley.edu (Matt Austern)
Date: 15 Mar 1994 07:48:33 GMT
Raw View
In article <CMoLEI.HFG@megatest.com> djones@megatest.com (Dave Jones) writes:

> What does it accomplish? No one has yet to show me what dynamically
> checked signatures are good for. Somebody give me a hint. I am at a
> complete loss as to how to justify them.

The only purpose I can think of is that they're there for
documentation.  After struggling a bit, though, I decided that "//"
was a better way of documenting which exceptions my code was
throwing.
--
Matthew Austern                       Never express yourself more clearly
matt@physics.berkeley.edu             than you think.    ---N. Bohr




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Tue, 15 Mar 1994 17:16:53 GMT
Raw View
Stan Friesen (swf@tdat.ElSegundoCA.NCR.COM) wrote:
: In article <1994Mar9.210645.26317@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |> Stan Friesen (swf@tools3teradata.com) wrote:
: In the case of a serious unrecoverable error, you have two choices,
: stop doing anything (i.e. exit), or go on in minimal mode.  The catch(...)
: simply assumes that it has caught an arbitrary unrecoverable error, and
: switches the program to minimal mode.

You said it ... "unrecoverable".  Our systems do not ship with "unrecoverable"
(software/resource contention) errors.  All failures with respect to
exceptions can be planned for if the tools provide enough information about
unplanned exception (ie. out-of-sig. exceptions).

: In a previous job we had some software that check-summed itself periodically,
: and went into emergency mode if the check-sum didn't match.  How is this
: any different than catch(...)?  It had *no* *way* of knowing what actually
: was wrong - the failed check-sum could indicate anything from a major
: hardware failure to a random, temporary, bit-toggle due to cosmic rays.
: So, the software had to assume the worst - that essentially nothing was
: trustworthy - and muddle on as best as possible (in this case lock the
: system, so that the patient could not recieve an accidental overdose
: of the medication being metered).

This is a hardware failure.  The extent to which you can recover from such a
failure is questionable.  There is little one can do here...but at least
government agencies (FDA) would concede this (from a software point of view).
A piece of medical equipment which threw an out-of-sig. exception, would not
fare well so with such agencies.  A hardware failure and even exceptions
thrown due to such failures can and probably should cause software to go to
whatever possible minimal, sane state it can.  An exception generated by
software, or resource contention can and should be explicitly planned for.
Without static checking of exceptions, the ability to produce reliable
software (note: software not hardware) is diminished.

: |> ...but seriously, the point being made is not that the program must keep
: |> running (it definitely must), but that when an exception occurs it is caught
: |> by code which expects to handle such an emergency.  The exception objects
: |> may transmit enough information to completely recover or it may indicate
: |> what minimum level of operation is possible.

: Quite, and for every type of error for which recovery is *possible*,
: you put in a *specific* catch to catch it, and the same goes for every
: type of situation for which there is a specific avoidance/minimization
: procedure.  It is *only* those exceptions for which you *have* no known
: handling procedure that you allow to fall through to the catch(...).
: It is an "oh shit, I'm lost" handler, not a normal exception handler.

Static checking of exceptions provide a mechanism which allows developers to
identify exceptional cases where no explicit handling has been specified.
Without such a mechanism, the "no known handling" is "only" recognized at
run-time.

Again, I will restate, I can live with the current state of exception
handling, if static checking is mandated to produce warnings by default, and
errors by command line switches, but I would prefer that the changes
recommended by the "static checkers" be implemented and full checking
performed.  The "mandate" part is the important point here; if compiler
companies are not compelled to provide any static checking, then they
probably will not (at least not right away).  This is particularly true
of the embedded development world, since the market is so much smaller
(even though the need is so much greater).

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.




Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 22 Mar 1994 12:17:59 GMT
Raw View
In article <1994Mar21.180159.4727@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|> Frankly, I am becoming tired of "dynamic checkers" telling the "static
|> checkers" the "appropriate" strategy to employ.

It is the "static checkers" that want to force the "throws none"
default upon the rest of us.  That's the real problem, since it
interferes seriously with programming styles that couldn't care less
about exceptions, or that are inherently robust against unexpected
exceptions.

    Thomas.




Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 22 Mar 1994 12:26:04 GMT
Raw View
In article <1994Mar21.180159.4727@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|> Following that logic, then we can eliminate static type checking as well,
|> since a variable returned from a function might not be a value we can handle.

The analogy between type checking and exception checking doesn't work.

Type checking is necessary (but not sufficient) for guaranteeing that a
program has defined, implementation independent behavior.  Without type
checking, programs can accidentally get at the internal representation
of data types and/or corrupt the runtime system.

Exception signature checking has no such function.  A program with an
uncaught exception behaves in a perfectly well-defined way, even if
you may not like or expect the way it behaves.

    Thomas.




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Tue, 22 Mar 94 08:47:48 PST
Raw View
In article <1994Mar21.235831.4924@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|> Stan Friesen (swf@tdat.ElSegundoCA.NCR.COM) wrote:
|> : I maintain that if #2 is required, it can be accomplished securely with
|> : intelligently placed "catch(...)" clauses.  Note, they must be placed so
|> : that you have some idea of what resource is unavailable - but that can
|> : be done by placing them around resource acquisition and critical usage
|> : segments.
|>
|> Strategy.

Yep, I am saying strategy is more important than any particular mechanism.

|> Let's get back to mechanisms not strategies. And to start the ball rolling,
|> here's a question which I have yet to hear a good answer to.
|>
|> Can anyone give me a reason for checking exception signatures at run-time?

I can't think of any.

Even in a program that is *not* a critical application, that *can*
exit on error, I do not normally want the program to abort abruptly.
Even if I am planning on exiting eventually due to the problem, I
*still* usually want the *whole* stack unwound first, not just one
third of it.

This is because I plan to use the 'constructor is resource allocation'
mechanism alot in C++, and I want any file locks, temporary files,
and so on, removed prior to exit.

|> We can satisfy both groups by simply annihilating the dynamic checking
|> (allowing "no checking"), and spec'ing the throw clause to allow static
|> checking tools to provide complete checking for those who want it.

Sounds good to me.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 23 Mar 1994 18:09:21 GMT
Raw View
Bill Leonard (bill@amber.csd.harris.com) wrote:
: In article <1994Mar21.180159.4727@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: > Thomas M. Breuel (tmb@arolla.idiap.ch) wrote:
: > : Well, the program still passes static exception signature checking.
: > : Nevertheless, there may be an unexpected exception occurring in "g".
: > : The recovery action for an end_of_file on stream1 is likely to be
: > : completely inappropriate for an end_of_file exception raised inside
: > : "h".  So, static checking didn't help us--we need to rely on other
: > : design techniques in order to keep unexpected exceptions from
: > : occurring.
: >
: > Following that logic, then we can eliminate static type checking as well,
: > since a variable returned from a function might not be a value we can handle.
: >
: > Please.  The idea of static checking is to test the class (if you will) not
: > the object.  In both static type checking and static exception checking the
: > class level info. is the thing being checked, not the object.  As you have
: > pointed out in your code/comments, static exception checking will not
: > completely eliminate related error, but will, to the same extend as static
: > type checking does for types, prevent the vast majority of these errors.

: I think the point that Thomas was trying to make is this: The
: static-checking advocates say they want static checking so that they can be
: sure that their program is ready to handle any exception that can possibly
: be thrown.  Fine so far.  But some of those advocates go further, and say
: that they want to be sure their application correctly *recovers* from those
: exceptions.

No (at least that's not my understanding), the static-checking advocates are
saying they want to guarenttee enough information is provided by the exception
mechanism to (potentially) recover.  This is not to say that the information
will be organized and maintained in a way which will not cause the problem
Thomas sited; that's up to exception strategy not mechanism.  In the
current mechanism, the "critical application" people invariable wind up
with what amounts to catch(...); the information is inaccessible (I'm not
going to o through why you wind up here because its been done before).

: Thomas is saying (I think) that you can't tell how you should recover from
: an exception if all you know is some base class of that exception.  Without
: examining the code, in detail, you will never know whether someone derived
: a new exception from some existing class that your "generic" handler
: doesn't expect.  I am using "expect" in the sense that the handler *thinks*
: it has enough information to recover from the exception, when in fact it
: does not.

I think you and Thomas are referring to strategy not mechanism.  Any mechanism
can be used badly.  The static checkers are only looking for a mechanism which
will allow more ("more" is a relative term here) robust forms of exception
strategy.

: It is true that the exception will be caught, but will the program do the right
: thing?  Who knows?  It may go merrily on its way, thinking it has been very smart
: in recovering from this exception, only to die later because it is using corrupt
: data.

: Is this a design error?  COULD BE!  Is it caught by static checking?  NO.
: Yet some of the static-checking advocates say that static checking will
: ELIMINATE errors related to unexpected exceptions.  I think Thomas was
: trying to show that that claim is patently false, and I applaud him for it.

Here, I believe, you are taking liberties.  I have not seen any response which
makes such claims.  As a matter of fact, I believe it has been sited many
times that the changes advocated by the static checkers could never completely
eliminate errors.  As a matter of fact I believe this was stated by several
people responding to a comment of a similar nature made by you (I could be
wrong here).  Logic and design errors are not within the compilers domain
of understanding, we all admit that...let's move on.

: > : The conclusion is that if you want your code to be highly robust (and
: > : that's supposedly the reason for adding static exception signature
: > : checking in the first place), then either you need a detailed
: > : understanding of the dynamic behavior of your program, ...
: > : or you need to
: > : be able to recover from arbitrary exceptions.  Preferably both.
: >
: > The objective of static exception checking is to eliminate the need to test
: > (understand) the dynamic behavior of a system.  The static checking will
: > enable potenial time-bombs to be uncovered.  This understanding is necessary
: > since humans can not perform this checking easily and the need to check may
: > be quite frequent.

: Thomas's point is that you cannot eliminate that need without detailed
: design and code reviews, which you should be doing anyway.  And if you do
: that, do you really need static checking?

How many times a day to you hold code reviews?  Maintainence and upgrades
allow ample opportunity for addition/removal of throws, throw clauses, try's,
and catches.  If every time someone makes a change you do a complete code
review of the entire system, I commend you.  And as for "do you really need
static checking" if you do design/code reviews?  I must respond with a
question: Do you really need function prototypes/static argument checking?
The need is a matter of degree, you assumedly don't need it, we absolutely
need it, to the same degree we need static type checking.

: > : or you need to
: > : be able to recover from arbitrary exceptions.  Preferably both.
: >
: > In the dynamic world you propose, I concur.

: Even in the static world, because you don't know what new classes of
: exceptions have been derived from classes you already expected, but which
: you aren't in fact prepared to deal with.

This is a strategy issue, let us please stick to mechanisms.

: I would expect that, if static checking were implemented, deriving new
: exceptions from old classes (even if inappropriate) would become quite
: prevalent; consider the programmer who's trying to get a bug fix out in a
: hurry, and he finds he has to change the exception signatures on 2000
: functions!  "But wait", he says, "I'll just derive this new exception from
: one of the classes on my signature list, and it'll all just work!"  And
: maybe it does, because he knows (or thinks he knows) that the catch clauses
: for that class *currently* do the right thing.  Of course, whether they
: will do the right thing after 50 more programmers work on it, who knows?

I thought you did code reviews whenever someone touched the code?

: There just aren't any magic bullets for bugs. :-(

I concur, but there are a number of syntactic (mechanism) things that can be
done to prevent whole categories of errors.  If the acid test for adding/
modifying a language is whether the change will eliminate all errors,
we would be writing everything in assembly code (at best).

Let me again say that we should get back to mechanism not strategy.  There
are many ways to make even the most screwed up mechanism work.  The converse
is also true, it is entirely possible to completely misuse a perfectly sound
mechanism.  I am not saying C++ exception handling is either perfectly
sound nor total screwed up.  As a matter of fact I think the mechanism
is very good overall.

I would also like to say that I respect the "dynamic/no checkers" point of
view, and there needs.  I wish they would simply do the same for the
"static checkers".  I think static checkers have adequetely shown its
benefits, for those who need it.  It simply a matter of finding a solution
in which both sides get what they want, without over complicating the
language.  My proposal is to eliminate the "dynamic check" on throws, but
allow throw signatures for static checkers (ie lint++) to use.  The dynamic
check is not used by anyone (that I know of) that has responded.  The
dynamic checkers typically want no throw clauses and depend upon
default=throw any.  This in effect would be perserved since no checking
would be performed.  The static checkers with throw clauses and the
modifications proposed could get complete static checking by a secondary
language checker (lint++).

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 23 Mar 1994 18:13:02 GMT
Raw View
Thomas M. Breuel (tmb@arolla.idiap.ch) wrote:
: In article <1994Mar21.180159.4727@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |> Frankly, I am becoming tired of "dynamic checkers" telling the "static
: |> checkers" the "appropriate" strategy to employ.

: It is the "static checkers" that want to force the "throws none"
: default upon the rest of us.  That's the real problem, since it
: interferes seriously with programming styles that couldn't care less
: about exceptions, or that are inherently robust against unexpected
: exceptions.


I don't want to repeat my proposal here, I've recently sighted in response
to someone defending you in our exchange (forgot his name).  In short, it
says that you can get default=throws anything.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 23 Mar 1994 18:17:25 GMT
Raw View
Thomas M. Breuel (tmb@arolla.idiap.ch) wrote:
: In article <1994Mar21.180159.4727@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |> Following that logic, then we can eliminate static type checking as well,
: |> since a variable returned from a function might not be a value we can handle.

: The analogy between type checking and exception checking doesn't work.

: Type checking is necessary (but not sufficient) for guaranteeing that a
: program has defined, implementation independent behavior.  Without type
: checking, programs can accidentally get at the internal representation
: of data types and/or corrupt the runtime system.

: Exception signature checking has no such function.  A program with an
: uncaught exception behaves in a perfectly well-defined way, even if
: you may not like or expect the way it behaves.

The point made was that the exact same error you sited can occur with a
variable, and as you have sited, no one is advocating removal of the
static type checking because it doesn't solve the problem.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 23 Mar 1994 18:21:03 GMT
Raw View
Stan Friesen (swf@tdat.ElSegundoCA.NCR.COM) wrote:
: In article <1994Mar21.235831.4924@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |> Stan Friesen (swf@tdat.ElSegundoCA.NCR.COM) wrote:
: |> : I maintain that if #2 is required, it can be accomplished securely with
: |> : intelligently placed "catch(...)" clauses.  Note, they must be placed so
: |> : that you have some idea of what resource is unavailable - but that can
: |> : be done by placing them around resource acquisition and critical usage
: |> : segments.
: |>
: |> Strategy.

: Yep, I am saying strategy is more important than any particular mechanism.

: |> Let's get back to mechanisms not strategies. And to start the ball rolling,
: |> here's a question which I have yet to hear a good answer to.
: |>
: |> Can anyone give me a reason for checking exception signatures at run-time?

: I can't think of any.

: Even in a program that is *not* a critical application, that *can*
: exit on error, I do not normally want the program to abort abruptly.
: Even if I am planning on exiting eventually due to the problem, I
: *still* usually want the *whole* stack unwound first, not just one
: third of it.

: This is because I plan to use the 'constructor is resource allocation'
: mechanism alot in C++, and I want any file locks, temporary files,
: and so on, removed prior to exit.

: |> We can satisfy both groups by simply annihilating the dynamic checking
: |> (allowing "no checking"), and spec'ing the throw clause to allow static
: |> checking tools to provide complete checking for those who want it.

: Sounds good to me.

Thank you I really appreciate it.

Did some say concensis.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 9 Mar 1994 11:18:39 GMT
Raw View
fenster@age.cs.columbia.edu (Sam Fenster) writes:

>Matt said:
>> Compile-time exception specification checking would require very
>> severe restrictions on the use of templates, function pointers, and
>> virtual functions.  I don't think it's worth it.
>
>I don't see the problem.
>
> - Exception signatures should clearly be part of a function's type.

Yes.

> - The compiler would require the exception specification of a
>redefined virtual function to be compatible with the one in its base class.
>The same as for its (covariant) return type.

Yes, there is no problem at all here.

> - Template classes would be checked for exception legality at
>instantiation time.  The same as member access, etc.  Templates that are to be
>usable for arbitrary parameters would (obviously) either have members
>declaring throw(...) or that wrap T.f() in catch(...).

There *is* a problem here.  The problem occurs whenever you have a callback
from library code to client code.  Templates are just a particularly
nasty and common example of this.

The problem is that the static type system is not expressive enough.
You really want polymorphism of exception signatures.
That is, in the case of `qsort' you want to be able to write

 template <signature Sig> // new keyword "signature"
 void qsort(void *, size_t, size_t,
     int compar(const void *, const void *) throw (Sig))
 throw (Sig, xmsg)
 {
  // ...
 }

so that the exceptions throw by `qsort' are those thrown by
the comparison function you pass it, plus `xmsg'.
(Of course, you would also want to use a template type rather than
`void *', but that's a separate issue.)

Similarly, you might want to do things like

 template <class T, signature Sig>
 T call(T f() throw (Sig))
 throw (Sig)
 {
  return f();
 }

and

 template <class T1, class T2, signature Sig1, signature Sig2>
 T2 combine(T1 f() throw Sig1, T2 g(T1) throw Sig2) throw (Sig1, Sig2) {
  return g(f());
 }

For template classes, you could write something like

 template <class T, signature S>
 class Stack {
  Stack() throw (S, xalloc);
  void push(const T&) throw (S);
  T pop() throw (S);
  bool is_empty();
 };

and then instantiate it as

 Stack<int, ()> stack_of_int;
 Stack<Matrix, (xalloc)> stack_of_matrices;

assuming the copy-constructor of Matrix has the signature `throw (xalloc)'.

Of course, once the declarations get this hairy you might start looking
for a language with type inference - then, "no signature" could instruct
the compiler to _infer_ the appropriate signature for that function.
But that option is not a practical one as far as C++ is concerned.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: pascual@peggy.tid.es (Pascual Juan)
Date: Wed, 9 Mar 1994 12:56:27 GMT
Raw View
In article <CMD89u.H5K@biles.com>, daniels@biles.com (Brad Daniels) writes:
|> In article <9406802.27876@mulga.cs.mu.oz.au>,
|> Fergus Henderson <fjh@munta.cs.mu.OZ.AU> wrote:
|> >pascual@gonzo.tid.es (Pascual Juan) writes:
|> >
|> >>  3.- No exception signature means "throw xmsg" instead "throws unexpected"
|> >> (xmsg is the base class of standard exceptions propossed).
|> >
|> >I disagree with this for the same reason that Bill Leonard has stated
|> >and Andrew Koenig has hinted at: users should NOT be forced to use
|> >exception signatures if they don't want to.  I'm certainly not convinced
|> >that I will want to use them.
|>
|> It doesn't sound like you're responding to what he's trying to say, or maybe
|> I misunderstand what he's trying to say... Basically, "xmsg" would implicitly
|> be part of the exception signatures of all functions.  Any exception derived
|> from that class could then be passed without any checking.  Exceptions which
|> should not normally be checked should all inherit from xmsg.  Those wanting
|> to send type-checked exceptions would simply throw exceptions not derived
|> from xmsg.  This seems like a pretty low-cost approach all around.
|>
|> - Brad

SORRY! SORRY! SORRY! SORRY! SORRY! SORRY! SORRY! SORRY! SORRY! SORRY! SORRY!

I think in spanish, and it's hard to me to get the right translation of my
thoughts. When I said:

  3.- No exception signature means "throw xmsg" instead "throws unexpected"
      (xmsg is the base class of standard exceptions propossed).

I mean:

  3.-  No exception signature means "throw xmsg" instead "throws unexpected"
       (xmsg is the base class of the exceptions throwed by the standard
       library proposed, who includes iostreams, strings, aritmethics and
       other system-related functions like "operator new")

I don't want that all exceptions will be derived from xmsg. I just want that
"exceptions that are derived from xmsg" (I mean SYSTEM EXCEPTION) can be
throwed by any function without signature, in order to make EXISTING CODE
(who has NO EXCEPTIONS) compatible. Existing code WON'T have to touch any
line of code to work poperly, because "no signature" means "throw (xmsg)"
and that code WILL DO THROW exceptions as soon they use the ANSI "operator new"
or try an ANSI division by zero ( int a, b=3, c=0; /*...*/ a=b/c; ).

People who thinks "I don't use exceptions, so I don't put any signature,
and I want no signature will mean 'throw unexpected', using set_unexpected()
to ignore them" are acting as an ostrich, who hides his head in a hole
when it see a lion. When an exception are thrown SOMETHING BAD IS HAPPENING,
and in the case of system exceptions it can be WORSE. Ignoring the lion
you can die suddenly and you won't know why! BUT if our proposal goes on,
you can left no signature in your prototyping because it will avoid lions
to enter in your jail, only the zoo-keeper (the system) will, unless you
say compiler other thing with a signature.

I added this correction to point 3 in the collect, and I will re-post it
updated if somebody ask for it.
--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: jjb@watson.ibm.com (John Barton)
Date: Wed, 9 Mar 1994 13:51:54 GMT
Raw View
Lee Nackman and I used exception specifications as outlined in ARM and
implemented in the IBM C Set ++ compiler on a medium sized project.
Based on our experience---to much pain for too little gain---we
removed the specifications.  We also use templates a great deal, so
below I explore Matt's idea to resolve the problem with static
exception specification checking and templates.

In article <MATT.94Mar8151143@physics2.berkeley.edu>, matt@physics2.berkeley.edu (Matt Austern) writes:
|> In article <CBARBER.94Mar7103150@apricot.bbn.com> cbarber@bbn.com (Christopher Barber) writes:
|>
|> >     MA> (2) If you're using a template, then you don't know at compile time
|> >     MA>         what types you're dealing with, so you don't know what
|> >     MA>         functions are being called.  The only possible exception
|> >     MA>         signature for Vector<T>::Vector(int Length), for example,
|> >     MA>         is throw anything, because you don't know what exceptions
|> >     MA>         might be thrown by T's constructor.
|> >
|> > This is not really that much of a problem.  Many things about templates
|> > can't be checked until instantiation time.  For instance, I have several
|> > templates that assume that T has a particular method that gets called by
|> > one of the template methods; this can't be checked until the template is
|> > instantiated.  Same goes with exception signatures, but so what?  The
|> > checking still gets done at compile time...
|>
|> The checking isn't what I'm concerned about.  It certainly would be
|> possible to write a compiler that would refuse to instantiate a
|> template if the exception signatures weren't compatible; my concern is
|> that I can't think of any sensible way to declare the exceptions that
|> would actually do something useful with static checking.  The problem
|> of exception specifications and templates is, in my opinion, rather
|> more serious than most people have recognized.
|>
|> I'm going to be pedantic and go through an example of this in some
|> detail.  I might add, before I begin, that it was when I was trying to
|> do exactly this that I decided there was no good solution, and decided
|> not to use exception signatures in my current project.
|>
|> So: let's suppose I have a class List<T>, and let's suppose it has a
|> member function List<T>::Add(const T&).  This function takes a
|> reference to a T, makes a copy of it, and adds it to the list.  The
|> problem is: what should we use for the exception specification of
|> List<T>::Add(const T&)?  Well, pretty clearly Add() can throw xalloc,
|> and also it can throw any exceptions thrown by T::T(const T&).  How
|> should we express this in Add()'s exception specification?
|>
|> If we're going to use exception specifications, and if we demand that
|> List<T>::Add(const T&) not throw anything not in its specification, I
|> can see only three options:
|>
|>  (1) Declare that the exception signature is, say, (xalloc,foo),
|>      and refuse to instantiate the template if T::T(const T&)
|>      throws anything other than xalloc or foo, or, of course,
|>      something derived from them.  [And, of course, we could
|>      imagine that "foo" stands for a list of several acceptable
|>      exceptions, or for an empty list.]
|>  (2) Declare that the exception signature is (xalloc,foo), and
|>      put a catch(...) wrapper around the call to T::T(const T&).
|>      Any exceptions thrown in T's copy constructor will be
|>      rethrown as foo.
|>  (3) Declare that the exception signature is throw anything.
|>      Of course, it can't really throw anything---it can really
|>      only throw xalloc or something thrown by T::T(const T&)---
|>      but we can't express this any way other than by the overbroad
|>      "throw any".
|>
[...his arguments against 1-3 omitted]
|>
|> The problem is that exception specifications, as currently defined,
|> don't allow you to express the real truth about what exceptions
|> List<T>::Add(const T&) can throw: it can throw xalloc, plus anything
|> thrown by T::T(const T&).  Whatever we do declare as the exception
|> signature is wrong: either more restrictive than what we really want,
|> or less so.  I don't know which of those is worse; my solution, as I
|> said before, was to give up on the whole idea of using exception
|> specifications for a generic container class.
|>
|> We might imagine defining a new syntax for exception specification, so
|> that we really could define the exception signature of
|> List<T>::Add(const T&) to be "anything thrown by T::T(const T&), plus
|> xalloc".  Personally, I think that this new syntax would be annoyingly
|> complicated, and of doubtful benefit.
|> --
|> Matthew Austern                       Never express yourself more clearly
|> matt@physics.berkeley.edu             than you think.    ---N. Bohr

Matt's fourth option would be to allow exception specifications to include
signatures of other functions.  For example, we would allow
void  List<T>::Add(const T&) throw(xalloc, T::T(const T&)) { ... }
This declaration would mean "throws xalloc and any exception thrown
by T::T(const T&)".  When Add() is instantiated, T::T(const T&) will
be known and the specification can be converted to a list of types
and checked like xalloc.

I suggest that this is the solution, but that compilers of template,
not programmers should add the "T::T(const T&)" part implicitly.  Thus
the exception specification for a member function template would be
xmsg plus any explicitly declared exceptions plus any exceptions
specified by parameterized function calls.  There is no loss of safety
here because templates are completely determined at compile time.
There is no extra burden on template programmers.  There is no extra
burden on template instance users unless they use a type for T that
specifies an extra exception on its copy constructor. In that case,
their code will require try() or transitive exception specifications.
Such a programmer will be able to do this, guided by compiler error
messages.

There would be extra work for compiler writers.  Good.

--
John.

John J. Barton        jjb@watson.ibm.com            (914)784-6645
H1-C13 IBM Watson Research Center P.O. Box 704 Hawthorne NY 10598




Author: daniels@biles.com (Brad Daniels)
Date: Wed, 9 Mar 1994 14:14:26 GMT
Raw View
In article <MATT.94Mar8151143@physics2.berkeley.edu>,
Matt Austern <matt@physics.berkeley.edu> wrote:
...
>We might imagine defining a new syntax for exception specification, so
>that we really could define the exception signature of
>List<T>::Add(const T&) to be "anything thrown by T::T(const T&), plus
>xalloc".  Personally, I think that this new syntax would be annoyingly
>complicated, and of doubtful benefit.

I see your point, though if all the annoyingly complicated syntax were
removed from C++, templates themselves would be the first thing to go... :-)
It wouldn't have to be too horribly complex, though.  I would envision
something like:

template <class T> List<T>::Add(const T&)
 throw (xalloc, exceptionsof(T::T(const T&)))
{
//...
}

Which reminds me, did I read something about a typeof operator being added,
or was it just my overactive imagination?

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: pascual@peggy.tid.es (Pascual Juan)
Date: Wed, 9 Mar 1994 14:28:54 GMT
Raw View
In article <9406821.27897@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
|> [...]
|>  template <class T1, class T2, signature Sig1, signature Sig2>
|>  T2 combine(T1 f() throw Sig1, T2 g(T1) throw Sig2) throw (Sig1, Sig2) {
|>   return g(f());
|>  }
|> [...]

I agree with your observation, but ask for Cocrodile Dandee's big knife
if you want to convince everybody. You have a very hard audience. ;-)

By the way, you will have to fight with the committee to add a new reserved
word. I personally think that new reserved word "signature" can be avoided:

 template <class T1, class T2, class X1, class X2>
 T2 combine( T1 f() throw(X1), T2 g(T1) throw(X2) ) throw(X1, X2)
 {
   return g( f() );
 }

Where X1 and X2 are the base class of the exceptions throwerd by f() and g().
Realize that f() and g() has to be combined, and both have to be related
in some way, so maybe they coul have the same root class for they exceptions,
even X1 and X2 could be the same or derived one in other, doing a compile-
time error (with your model and with mine). It could be better sacrify
some generality to achieve more functionality:

        template <class T1, class T2, class X>
 T2 combine( T1 f() throw(X), T2 g(T1) throw(X) ) throw(X)
 {
   return g( f() );
 }

Or even further, forcing the exception throwed:

 template <class T1, class T2>
        T2 combine( T1 f() throw(Y), T2 g(T1) throw(Y) ) throw(Y)
        {
          return g( f() );
        }

If f() or g() throws something but Y's, template instantiation will be
rejected.

I do prefer your model, but I would be happy just avoiding unexpected
exceptions.

|> Of course, once the declarations get this hairy you might start looking
|> for a language with type inference - then, "no signature" could instruct
|> the compiler to _infer_ the appropriate signature for that function.
|> But that option is not a practical one as far as C++ is concerned.

Why not? Observations like yours are the best ones.

Let's make a safe C++.

P.D: Before somebody shout against it: Fergus' observation don't forces
     anybody to use it, neither to change his code, unless you are a C++
     wizzard and use templates like above (I don't), in such a case I hope
     you see its usefullness.
--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: cbarber@bbn.com (Christopher Barber)
Date: 09 Mar 1994 16:17:19 GMT
Raw View
>>>>> "swf" == Stan Friesen <swf@tools3teradata.com> writes:

    swf>  3. At the 'critical' points I would put in a "catch(...)"
    swf> where I would do what is necessary to safely allow continued
    swf> operation and then go on.

    swf> Voila.  The program will never die, and it will continue to
    swf> operate, perhaps in 'minimal function mode', even after a
    swf> completely unexpected exception.

You haven't thought this through.  Don't forget that when you write "catch
(...)"  you are essentially throwing away the exception.  You have no way
to access whatever information the exception object might contain that
would need to be used to recover.  You would have no way to tell the
difference from a probably fatal xalloc exception and a potentially
recoverable exception of some other type.

- Chris

--
Christopher Barber
(cbarber@bbn.com)




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 9 Mar 1994 21:06:45 GMT
Raw View
Stan Friesen (swf@tools3teradata.com) wrote:
: In article <1994Mar1.100008.20784@mole-end.matawan.nj.us>, mat@mole-end.matawan.nj.us writes:
: |> In article <1994Feb25.192710.18677@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |>  ...
: |> > If the dynamic checking responders are still not satisfied with the arguments
: |> > set forth by the static checkers, I would like to leave you with some food
: |> > for thought from my domain.  The company I work for is a manufacturer of
: |> > medical equipment.  When our system goes to unexpected(), not just the
: |> > application dies!  ...
: |>
: |> Yours is a very real dilemma.  Static checking is appropriate in your case,

: No, I don't think so.  What I would do in this case is:
:  1. Use *no* exception signatures.

:  2. If I *must* use a libarary that includes functions
:     with exception signatures, I would redefine unexpected()
:     to throw an "unchecked" exception somehow.  (Does the proposed
:     standard state that if unexpeced() throws an exception it is
:     passed on?) [I would first try to avoid libraries with
:     exception signatures].

:  3. At the 'critical' points I would put in a "catch(...)"
:     where I would do what is necessary to safely allow
:     continued operation and then go on.

: Voila.  The program will never die, and it will continue to operate,
: perhaps in 'minimal function mode', even after a completely unexpected
: exception.

: I do not see that there is really much problem with this.

I wish my world was as simple as yours.  That catch(...) should be a really
interesting piece of code.  You don't know what went wrong, nor why, nor at
what point in the code, but you're gonna march on anyway!?  Either you are
quite couragous or you are not planning to ever undergo surgery.

...but seriously, the point being made is not that the program must keep
running (it definitely must), but that when an exception occurs it is caught
by code which expects to handle such an emergency.  The exception objects
may transmit enough information to completely recover or it may indicate
what minimum level of operation is possible.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 9 Mar 1994 21:52:09 GMT
Raw View
Thomas M. Breuel (tmb@arolla.idiap.ch) wrote:
: In article <1994Mar7.164337.22230@datascope.com> sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: |... and they lived happily ever after (except for the poor sole whose life
: |depended on C++ exception handling).
: |[...]
: |Because the latter is the conservative solution.  By your own admission it is
: |no more difficult and we would err on the side of safety.  Also, when you say
: |"far fewer people" I assume you mean programmers, not the user.  A tendency
: |toward benefiting one self is a part of human nature, but so is respect for
: |human life.
: |[...]

: If you can't write safety critical applictions in C++ without static
: exception checking, you shouldn't be writing safety critical
: applications in any language.

I never said safety critical applications could not be written in C++ without
static exception checking.  We could all write code in assembly language, but
we write it in high-level languages instead.  We do so because it best suits
the problem space, and allows for more maintainable and reliable code (among
other reasons).  Any mechanism which uncovers potential problems makes the
production of quality code that much more possible.

: Static exception checking might, at best, reduce the cost of
: designing, verifying, and testing safety critical code.  But that
: reduction in cost needs to be weighed carefully against the increase
: in language complexity, compiler complexity, and maintenance
: complexity, and the associated costs and risks (!).

I agree, the facts must be weighed.  Language complexity as its been put
forth so far is no more complex than the current scheme.  Compiler complexity
would increase (but most people have conceded that compilers should provide
such facilities anyway).  I don't understand the last "complexity", so I can't
comment fully.  I think you mean will you have to update prototypes.  If so,
and you wanted to reap the benefits of statically checked exceptions, then
yes.  If you don't care to use static checking then depending on whose
scheme you choose you'll have to add nothing or throw(...) once.

: Evidently, many people (myself included) still believe that the costs
: associated with adding static exception checking are significantly
: larger than any potential benefit.

IMO the costs are small in comparision to the potential bugs they exterminate.
IMO I'd rather kill bugs than people.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 9 Mar 1994 23:00:40 GMT
Raw View
In article <rfgCMAFFq.A57@netcom.com>, rfg@netcom.com (Ronald F. Guilmette) writes:
>
> For example, if some banking application program or some credit reporting
> software goes off into the weeds, this could seriously impact one's
> financial well-being.  Thus, bugs in such programs are potentially dangerous.

That's why such organizations should have procedures for correcting such
errors.  Are you seriously suggesting that having statically-checked
exceptions will eliminate all software bugs?  Are you even suggesting that
we will eliminate all software bugs in our lifetime?  It won't happen, and
even if it does, we won't eliminate the problems caused by misuse,
intentional or not, of that software.

I say that to make this point: Use of complicated software will always
entail the kind of risks you enumerated, WHETHER OR NOT the software has
bugs in it.  The presence of bugs may, or may not, significantly increase
the probabilty of some of those risks.

> If the long-distance telephone network craps out, someone may not get the
> new kidney they have been dying for.

Sure, and if Adolph Hitler's mother had had an abortion...

Anyone can come up with an apocalyptic scenario.  But hardly anyone
considers whether that telephone network would even exist if we waited for
bug-free software.  Perhaps if the software were delayed for a year, that
kidney doctor wouldn't be able to get through anyway.

> I am often struck by how little people who are themselves *in* this business
> seem to appreciate how much effect this business has on peoples lives these
> days.  They ought to know better.

And I am often struck by how little those who advocate "safety uber alles"
think about the cost, both monetary and otherwise, of their advocacy.  All
technology carries a risk, but lack of that technology can, and usually
does, carry its own risks.  The only deciding factor should be which risks
are greater, not whether there are any risks at all or not.

People take risks every day, and will continue to do so forever.  We cannot
eliminate all risks.  Striving to reduce risks is, presumably, what the
safety advocates are doing, but their arguments seem to ignore the
practical considerations that reducing one set of risks may incur even
greater ones.  More importantly, their arguments carry a flavor of "I know
best, whether you think so or not."

>
> The safety of software should (by default) always be considered to be im-
> portant unless (and until) proven otherwise.  Over time, I expect to see
> this point driven home to more and more practitioners... generally via
> lawsuits.

Nobody said it wasn't important.  Some of us believe it shouldn't be the
*only* factor in decision-making.  And some of us believe that the difference
in safety afforded by statically-checked exceptions is not as great as others
believe.  If you want to prevail in the argument, you'll have to make
convincing arguments to counter those beliefs.

The people who have so far taken part in this debate are both intelligent
and concientious individuals who have legitimate concerns and opinions.
They are, at least in most cases, experienced software engineers who are
trying to make informed choices in a difficult and demanding world.  You
may not agree with the choices they make, but you should at least respect
their opinions and beliefs and try to appreciate the factors they
incorporate into their decision-making.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Wed, 9 Mar 94 16:10:53 PST
Raw View
In article <1994Mar9.125627.8467@tid.es>, pascual@peggy.tid.es (Pascual Juan) writes:
|> In article <CMD89u.H5K@biles.com>, daniels@biles.com (Brad Daniels) writes:
|>
|> I don't want that all exceptions will be derived from xmsg. I just want that
|> "exceptions that are derived from xmsg" (I mean SYSTEM EXCEPTION) can be
|> throwed by any function without signature, in order to make EXISTING CODE
|> (who has NO EXCEPTIONS) compatible. ...

Fine, but many users will still want to derive exceptions from xmsg,

Why? See below.

|> People who thinks "I don't use exceptions, so I don't put any signature,

It is the person who *does* use excpetions, but wants to avoid using
exceptions signatures, that will want to derive new exceptions from xmsg
under your scheme.

|> and I want no signature will mean 'throw unexpected', using set_unexpected()
|> to ignore them" are acting as an ostrich, who hides his head in a hole
|> when it see a lion.

NOT, ingore them - pass them on to where they can be dealt with.
The default unexpected() does a *very* nasty thing - it exits immediately
without calling any fyrther destructors, or doing any more stack unwinding.

Rethrowing the exception allows continued stack unwinding, releasing
remaining resources, and *also* allows one to actually *catch* these
exceptions (if one does not want the program to die when it reaches
main() with an uncaught exception).

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Wed, 9 Mar 94 16:31:36 PST
Raw View
In article <CBARBER.94Mar9111719@apricot.bbn.com>, cbarber@bbn.com (Christopher Barber) writes:
|> >>>>> "swf" == Stan Friesen <swf@tools3teradata.com> writes:
|>
|>     swf>  3. At the 'critical' points I would put in a "catch(...)"
|>     swf> where I would do what is necessary to safely allow continued
|>     swf> operation and then go on.
|>
|> You haven't thought this through.  Don't forget that when you write "catch
|> (...)"  you are essentially throwing away the exception.  You have no way
|> to access whatever information the exception object might contain that
|> would need to be used to recover.  You would have no way to tell the
|> difference from a probably fatal xalloc exception and a potentially
|> recoverable exception of some other type.

Of course, the catch(...) would only go after all known exceptions
for which there were known recovery processes.  Its sole purpose is
to keep the program from exiting.  Remember this is a critical program,
that is not *allowed* to have fatal errors - it *must* continue operating.

In that situation, upon recieving an unknown error - one you did not know was
possible - the *only* possible approach is to treat it as an 'serious' error,
log it, and continue in a 'safe' mode of operation.

I am not saying that catch(...) should be your *primary* means of dealing
with an exception - just that it is a better way of dealing with *unexpected*
exceptions than having the program simply abort at an arbitrary point.
[Since these exceptions are *unexpected*, they are intrinsically of an
unknown type - since if the type were knowable, you could make them
'expected' by simply catching them].

If I can tolerate the program terminating, I just let the unexpected
exception run all the way up the stack and out of main().

If I can tolerate the program aborting without cleaning up after itself,
I might even leave the default unexpected() handler in place.

Thus, catch(...) is used in only two situations:
 1.  Where the function must do some clean-up not handled by
     the destructors of stack variables, but will not otherwise
     handle the exception.    In this case the handler will
     rethrow the same exception after cleaning up.  (In this
     sense the original exception is *not* lost - since it can
     be rethrown).

 2.  Where the function/subsystem *must* continue operating
     no mater what happens (=> failure not allowed).  Then
     such a catch acts as a fail-safe to prevent unexpected
     exceptions from causing an involuntary failure.

In *all* other situations I would use a more specific catch clause
(probably with a reference parameter, to avoid excess copying).

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Thu, 10 Mar 1994 17:18:40 GMT
Raw View
jjb@watson.ibm.com (John Barton) writes:

>matt@physics2.berkeley.edu (Matt Austern) writes:
>|> The problem of exception specifications and templates is, in my
>|> opinion, rather more serious than most people have recognized.

I agree, it's quite a serious problem.  It's well known that strict
static type-checking is an almighty pain if the type system is not
flexible enough.  (This is a frequent criticism of Pascal, for example.)
And it's clear that the C++ type system is not flexible enough as far
as exception specifications go.

Given this, the compromise of run-time checking of exception signatures
appears more reasonable to me now than it did at first sight.  Static
checking of exception signatures would either be very restrictive, or
would significantly complicate the type system.  Nevertheless, run-time
checking has significant disadvantages.

>|> [...] pretty clearly Add() can throw xalloc,
>|> and also it can throw any exceptions thrown by T::T(const T&).  How
>|> should we express this in Add()'s exception specification?
>|>
>|> If we're going to use exception specifications, and if we demand that
>|> List<T>::Add(const T&) not throw anything not in its specification, I
>|> can see only three options:
>|>
>|>  (3) Declare that the exception signature is throw anything.
>|>      Of course, it can't really throw anything---it can really
>|>      only throw xalloc or something thrown by T::T(const T&)---
>|>      but we can't express this any way other than by the overbroad
>|>      "throw any".

In C++ as it currently stands, (3) is the best option.

In another article I suggested the possibility of extending C++
with "signature polymorphism", i.e. the ability to do

 template <signature S> ...

This has the advantage of making it possible to express exception signatures
such as the above, but the disadvantage of complicating the type system.
(And the syntax is rather cumbersome.)

>|> Whatever we do declare as the exception
>|> signature is wrong: either more restrictive than what we really want,
>|> or less so.  I don't know which of those is worse; my solution, as I
>|> said before, was to give up on the whole idea of using exception
>|> specifications for a generic container class.
>|>
>|> We might imagine defining a new syntax for exception specification, so
>|> that we really could define the exception signature of
>|> List<T>::Add(const T&) to be "anything thrown by T::T(const T&), plus
>|> xalloc".  Personally, I think that this new syntax would be annoyingly
>|> complicated, and of doubtful benefit.

Yes, that is a distinct possibility.

>Matt's fourth option would be to allow exception specifications to include
>signatures of other functions.  For example, we would allow
>void  List<T>::Add(const T&) throw(xalloc, T::T(const T&)) { ... }
>This declaration would mean "throws xalloc and any exception thrown
>by T::T(const T&)".  When Add() is instantiated, T::T(const T&) will
>be known and the specification can be converted to a list of types
>and checked like xalloc.
>I suggest that this is the solution, but that compilers of template,
>not programmers should add the "T::T(const T&)" part implicitly.

Ah, so now we have an advocate for type inference ;-).

>Thus the exception specification for a member function template would be
>xmsg plus any explicitly declared exceptions plus any exceptions
>specified by parameterized function calls.  There is no loss of safety
>here because templates are completely determined at compile time.
>There is no extra burden on template programmers.

This still doesn't solve the problem of the exception signature for `qsort'.
And, as you say:

>There would be extra work for compiler writers.  Good.

No, extra work for compiler writers means less reliable compilers for
everyone else.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 10 Mar 1994 19:54:21 GMT
Raw View
In article <CM59Js.89t@biles.com>, daniels@biles.com (Brad Daniels) writes:
> In article <CM3nHs.n7@tempel.research.att.com>,
> Did you ever review the possibility of having an unchecked exception class
> which is implicitly part of every exception signature in order to allow
> programmers to create exceptions which explicitly opt out of a static
> checking mechanism?  The more I think about such an approach, the more
> viable it seems to me.  If you rejected such an approach, why did you do
> so?

This still doesn't consider throw signatures as optional.  I'd rather not
use them at all.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 10 Mar 1994 20:00:53 GMT
Raw View
In article <CMAxCt.JrF@biles.com>, daniels@biles.com (Brad Daniels) writes:
> This approach is essentially equivalent to static checking with no
> signature meaning "throw anything".  I think we've discussed why that
> particular approach can be a major pain.

Well, yes you've discussed it, but not convincingly.  Your argument seems
to me to basically be that it would be a pain for you.  My counter argument
is that, if the default means "throw nothing", it will be a major pain for
me.  I have heard only one argument that speaks to the issue of why I
should bear the burden and not you, and that is "it will make programs
safer".

I remain unconvinced that throw signatures, dynamically checked or
statically checked, add anything to safety over not using them at all.  I
can see how dynamically-checked signatures might compromise reliability,
but I don't see how that relates to not using signatures at all.  I still
believe that adding exceptions to my application, which doesn't use them at
all now, will improve its reliability.  If I am *forced* to use throw
signatures, then I will never use exceptions in that application -- it just
won't ever become practical.  So I don't see how forcing signatures has
enhanced the reliability of *my* software.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: imp@boulder.parcplace.com (Warner Losh)
Date: Thu, 10 Mar 1994 20:32:01 GMT
Raw View
I'd like to point out that for my application (a user interface
builder) I want to be able to catch any and all exceptions that may
happen and at least try to safe the application under development.
Sure, I might not be able to do so reliably, and the app that is saved
may be malformed, but a high percentage of the time I've found that
even after a SEGV and BUS that the app will properly save out.

I don't want exceptions "assuming" they know what is best for me and
exiting the program.  This is a bad assumption....

Maybe my application will suffice to have an unhandled() function, but
it isn't clear to me that it will.  Also, since I link in shared
libraries, there may need to be some cleanup for the shared libraries
as well that I have no way of knowing at compile/link time.

Warner
--
Warner Losh  imp@boulder.parcplace.COM ParcPlace Boulder
I've almost finished my brute force solution to subtlety.




Author: daniels@biles.com (Brad Daniels)
Date: Thu, 10 Mar 1994 23:05:12 GMT
Raw View
In article <2lnu9l$p0v@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
>In article <CMAxCt.JrF@biles.com>, daniels@biles.com (Brad Daniels) writes:
>> This approach is essentially equivalent to static checking with no
>> signature meaning "throw anything".  I think we've discussed why that
>> particular approach can be a major pain.
>
>Well, yes you've discussed it, but not convincingly.  Your argument seems
>to me to basically be that it would be a pain for you.  My counter argument
>is that, if the default means "throw nothing", it will be a major pain for
>me.  I have heard only one argument that speaks to the issue of why I
>should bear the burden and not you, and that is "it will make programs
>safer".

I've been reading your messages for a little while without response in hopes
that you would either respond to the more reasonable suggestions coming
out here or go away.  You were clearly either not reading or ignoring my
later messages, as the above indicates.  In at least four messages, including
at least two in response to your messages, I have indicated that I think the
best approach would be to have a class of exceptions which is implicitly
part of the exception signature of every function.  I have since revised
that position slightly to supporting Pascal Juan's variation of having no
exception signature mean that it throws only exceptions of that implicit
class, while any exception specification would be considered complete.  E.g.,
no exception signature would be equivalent to "throw(xmsg)", while
"throw(foo)" would allow a function to throw exceptions only of class foo.
Exception classes derived from the implicit class would thereby be implicitly
unchecked in the absence of exception signatures.  It is certainly not an
onerous burden to add ": public unchecked" or ": public xmsg" to the
declaration of an exception class.  Such a small burden is certainly
justifiable even if it provides only a small measure of safety.

Please think about this approach, then try to reply constructively.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: fjh@cs.mu.OZ.AU (Fergus Henderson)
Date: Fri, 11 Mar 1994 15:48:09 GMT
Raw View
daniels@biles.com (Brad Daniels) writes:

>Tucker Taft <stt@spock.camb.inmet.com> wrote:
>>
>>There is yet another alternative, namely that static checking only
>>applies to functions that have explicit exception signatures
>>calling functions with explicit exception signatures, or
>>explicitly calling "throw()."  This seems to avoid the problems you
>>cited, while allowing those who are compulsive (;-) about exception
>>signatures to get full checking.
>
>This approach is essentially equivalent to static checking with no
>signature meaning "throw anything".

No, it's not: with Tucker Taft's approach, a function with an explicit
exception signature would be allowed to call a function with no signature
without putting it inside a `try' block.  Runtime checking would
still be required for those cases.

>I think we've discussed why that
>particular approach can be a major pain.

Well, we've discussed the pros and cons of "no signature means throw
anything", but you certainly haven't convinced me that it's a bad
idea.  Plenty of people, myself included, may want to use exceptions
without wanting to use exception signatures, and for those people, the
current default of "no signature means throw anything" is the right
choice.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 11 Mar 1994 19:46:14 GMT
Raw View
In article <9407101.1949@mulga.cs.mu.OZ.AU> fjh@cs.mu.OZ.AU (Fergus Henderson) writes:
|Well, we've discussed the pros and cons of "no signature means throw
|anything", but you certainly haven't convinced me that it's a bad
|idea.  Plenty of people, myself included, may want to use exceptions
|without wanting to use exception signatures, and for those people, the
|current default of "no signature means throw anything" is the right
|choice.

Also, people should keep in mind that plenty of people may not want to
use exceptions at all.  If the default were "throw nothing", then
those people could not use libraries that throw exceptions without
having to add exception signatures everywhere.

    Thomas.




Author: jjb@watson.ibm.com (John Barton)
Date: Fri, 11 Mar 1994 21:14:05 GMT
Raw View
In article <9407003.13125@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
|> jjb@watson.ibm.com (John Barton) writes:
|>
|> >Matt's fourth option would be to allow exception specifications to include
|> >signatures of other functions.  For example, we would allow
|> >void  List<T>::Add(const T&) throw(xalloc, T::T(const T&)) { ... }
|> >This declaration would mean "throws xalloc and any exception thrown
|> >by T::T(const T&)".  When Add() is instantiated, T::T(const T&) will
|> >be known and the specification can be converted to a list of types
|> >and checked like xalloc.
|> >I suggest that this is the solution, but that compilers of template,
|> >not programmers should add the "T::T(const T&)" part implicitly.
|>
|> Ah, so now we have an advocate for type inference ;-).

No inference is involved.  The compiler simply fills in the throw
specification and checks it.  It compiles or it does not.

|>
|> >Thus the exception specification for a member function template would be
|> >xmsg plus any explicitly declared exceptions plus any exceptions
|> >specified by parameterized function calls.  There is no loss of safety
|> >here because templates are completely determined at compile time.
|> >There is no extra burden on template programmers.
|>
|> This still doesn't solve the problem of the exception signature for
`qsort'.

Wasn't trying to.  The issue was templates, not function pointers.

|> And, as you say:
|>
|> >There would be extra work for compiler writers.  Good.
|>
|> No, extra work for compiler writers means less reliable compilers for
|> everyone else.

If you have any evidence that IBM's C++ compiler is unreliable, I
would like to hear it.

|>
|> --
|> Fergus Henderson - fjh@munta.cs.mu.oz.au

--
John.

John J. Barton        jjb@watson.ibm.com            (914)784-6645
H1-C13 IBM Watson Research Center P.O. Box 704 Hawthorne NY 10598




Author: djones@megatest.com (Dave Jones)
Date: Fri, 11 Mar 1994 21:21:23 GMT
Raw View


Author: djones@megatest.com (Dave Jones)
Date: Fri, 11 Mar 1994 21:28:48 GMT
Raw View


Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 11 Mar 1994 21:59:26 GMT
Raw View
In article <CMD89u.H5K@biles.com>, daniels@biles.com (Brad Daniels) writes:
> In article <9406802.27876@mulga.cs.mu.oz.au>,
> Fergus Henderson <fjh@munta.cs.mu.OZ.AU> wrote:
> >I disagree with this for the same reason that Bill Leonard has stated
> >and Andrew Koenig has hinted at: users should NOT be forced to use
> >exception signatures if they don't want to.  I'm certainly not convinced
> >that I will want to use them.
>
> It doesn't sound like you're responding to what he's trying to say, or maybe
> I misunderstand what he's trying to say...  Basically, "xmsg" would implicitly
> be part of the exception signatures of all functions.  Any exception derived
> from that class could then be passed without any checking.  Exceptions which
> should not normally be checked should all inherit from xmsg.  Those wanting
> to send type-checked exceptions would simply throw exceptions not derived
> from xmsg.  This seems like a pretty low-cost approach all around.

Yep, you misunderstood all right.  You're still assuming that I want to use
exception signatures -- I don't.  If the default signature is
"throw(xmsg)", that still means I have to use signatures OR I have to have
a particular inheritance heirarchy for my exceptions.  Neither choice is as
good as just not using signatures at all, IMO.

I don't particularly want to derive my exceptions from xmsg; I would
probably create my own exception class that better fits my application and
the types of recovery I need or want to do.  I may have some type of
exception class that could "embed" an xmsg inside it, so that I could
translate xmsg exceptions into my own type, with the concomitant extra
information my application needs to recover or print an intelligent
message.

In other words, I don't want to make the choice of checked or unchecked
exceptions on an exception-by-exception basis -- I want to make it for the
whole application.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: matt@physics16.berkeley.edu (Matt Austern)
Date: 11 Mar 1994 22:05:36 GMT
Raw View
In article <CMEH6J.14L0@hawnews.watson.ibm.com> jjb@watson.ibm.com (John Barton) writes:

> Matt's fourth option would be to allow exception specifications to include
> signatures of other functions.  For example, we would allow
> void  List<T>::Add(const T&) throw(xalloc, T::T(const T&)) { ... }
> This declaration would mean "throws xalloc and any exception thrown
> by T::T(const T&)".  When Add() is instantiated, T::T(const T&) will
> be known and the specification can be converted to a list of types
> and checked like xalloc.
>
> I suggest that this is the solution, but that compilers of template,
> not programmers should add the "T::T(const T&)" part implicitly.  Thus
> the exception specification for a member function template would be
> xmsg plus any explicitly declared exceptions plus any exceptions
> specified by parameterized function calls.

I'm not at all sure that I like the idea of a compiler automatically
putting additional exception in the exception specification.  These
sorts of inferences can often be dangerous (people even get bitten by
automatically generatated assignment operators and copy constructors),
and I'm nervous about having them added without some very good reason.

In this case, specifically, not every programmer will want to have the
exception signature of every template member function include the
signature of every parameterized function call.  This is appropriate
in the specific example that I gave (the exception signature of
List<T>::Add(const T&) ought to include the exceptions thrown by
T::T(const T&)), but it isn't always appropriate.  Sometimes you
really do want a Foo<T>::Bar() that, independent of T, will only throw
a certain set of exceptions.

The reason I brought up the List<T>::Add() example, in any case, was
just to demonstrate that the exception signature mechanism isn't
expressive enough to allow static checking in all circumstances unless
you impose severe restrictions on other aspects of the language, such
as templates.  It looks like most people agree, at least in this
particular case, so we've seen a few different suggestions for
generalizing exception signatures.  That's one possible response; my
own response is to give up on the idea of statically checked exception
signatures, and to use exception signatures sparingly, if at all.
--
Matthew Austern                       Never express yourself more clearly
matt@physics.berkeley.edu             than you think.    ---N. Bohr




Author: oren@white.weizmann.ac.il (Ben-Kiki Oren)
Date: Wed, 16 Mar 1994 12:05:38 GMT
Raw View
The order I receive this thread makes for very interesting reading :-)
Anyway, I would like to voice the heretical view that the current rules
are (gasp) reasonable, at least for the first version of the standard.

Dave Jones (djones@megatest.com) wrote:

> Against that was the fact that the introduction of a new entry
> in the throw signature by a software vendor or another programmer could cause
> a correct program to crash "unexpected"ly, and the case against dynamic
> checking was solid. Next case.

Almost true. The only use for dynamic checking is to serve as a `poor man's
static checking' until that is available. Of course you don't do it in the
final product, just in the testing phase, and you add a catch(...) where
appropriate `just in case'.

I suspect this is why the compiler vendors pushed it in - it prevents them
from needing to provide full static checking as part of the already complex
task of adding exceptions. Already we see that it has some non-trivial
interactions with templates, function pointers, and who knows what else. Would
you like to delay both the standard and the implementation by a year for that,
especially as most of us don't really need it?

> I tried defending static checking. Surprisingly (at least to
> me), I could not make much of a case for it either, but at least it was
> safe. I asked myself the question, "If I am a software developer seeking
> to write correct code that will stay correct in the face of implementation
> changes in the code I do not control, when does it help me to see a
> throw-signature on a function that I want to use?"

Isn't this an impossible goal? How can your code remain correct if some code
you do not control has changed? If you wish to qualify it by `changes only
involving the exception signatures' you may have a point, but I would argue
that it is still an unrealistic goal. The real goal should be `write correct
code which changes as little as possible' or something of that nature. The
point is that if it _needs_ to be changed you damn well _want_ to change it.

> It does not help me when I am prepared to deal with the listed exceptions but
> no others, because in that case I could deal with a throw-any function
> by just catching the exceptions that I am interested in, and passing the
> others down the line. That is part and parcel of the "resource allocation is
> initialization" discipline: A procedure very often does not need to know
> what exception is thrown. Typically it just cleans up its local frame
> (automatically), and returns. Sometimes it deals with certain exceptions
> that it can deal with, but passes the others through.

This tactic is indeed appropriate for most functions in most code; however,
eventually _someone_ should handle the exception - otherwise the program
crashes anyway. If you are writing critical code, that an exception _is_
handled and _where_ is very important, and checked signatures give you that.
So, if one function changes, everyone who calls it changes, and there is
no helping that, period. The power to subclass exceptions makes it much
less of a problem, anyway.

> Ah ha. I've got it. The only time that the signature helps is when
> a procedure can not safely abort and pass exceptions through. THAT'S IT!
> It helps when the calling procedure must deal with all exceptions
> right there on the spot. In that case, and that case only, it may help
> to have an exhaustive list of all possible exceptions.

Exactly. And such procedures are _necessary_ in critical software.

> Put another way, exception signatures are of use to programmers who are writing
> *exception unsafe* code -- code that does not follow the resource allocation
> is initialization discipline...

Or critical code that is not allowed to fail. In which case you need to handle
each and every error and be certain you have done so. If you want to call such
software `exception UNsafe' that is, of course, your privilege... But it seems
a strange use of words to me.

> Now it is at last obvious (to me):

Sigh... there's that word, `obvious', again :-)

> 1. Having no exception signatures would be perfectly acceptable.

For people writing non-critical software _only_ (it happens to be most of the
people, but still...) It would make C++ unacceptable for many applications.

> 2. Statically checked signatures that are part of a function's external type
>    would occasionally prove useful if understood and used correctly.

Not `occasionally'. For a well defined class of programs, those that `are not
allowed to fail'. In such a case both statically checked signatures and the
default=any make a lot of sense (because third party libraries may not be
checked, and will not have signatures, and may throw anything at all).

> 3. Two types of functions, "throw-any" and "throw-none", would accomplish
>    almost as much as full signatures.

Not for critical software. There you want a tight control on which errors are
possible, how and where they are handled, etc. They might be more useful for
`normal' code, but IMHO its a bad idea because it prevents implementations
from adding exceptions on overflow etc.

> 4. Dynamically checked signatures are useless and dangerous.

Definitely.

IMHO we have two types of code:

Normal    Safe
No signatures   Has signatures
Each function throws anything Each function throws from the signature only
Does not need checks  Needs static checks

Every time a `safe' function calls a `normal' one, it should use a
`catch(...)'. This will often happen when such `safe' code is forced to invoke
third-party software.

How does that mesh with the current rules?

- Default=all fits right in.

- Dynamic checking can, mercifully, be turned off. It can, of course, be used
  as `poor man's static checking' _for the testing phase only_. In the final
  product, always add `catch(...) { do_something_always_safe() }', until
  static-check compilers are available.

People who are writing `safe' code will need to pressure compiler writers into
doing the checks (if only as an option) and the committee into making the
changes required to make it possible (in the next version of the standard,
that is). Of course, vendors can start implementing such extensions without
waiting for the standard (this is how languages evolve). They should use
dynamic checking for the testing phase, but not in the final product.

People writing normal code, that is most of us, can start right away (well, at
least as soon as you have a compiler which supports exceptions, anyway :-)
`You don't use it, you don't pay for it'.

So I'd say put this to rest until the next draft of the standard. The current
rules make it possible both to write `normal' code and to have a go at writing
`safe' code. In the next standard, we can improve on this by adding static
checks and making it easier to write `safe' code.

> I'm giving a talk on exceptions for my company on the 25th. I will recommend
> in no uncertain terms that until such time as exception signatures are checked
> statically, they will be strictly outlawed in the company coding standard.
> I will also recommend that we not buy third party software that uses
> exception signatures, unless it is compiled with a compiler that does
> full static checking.

That is a bit excessive - people may want to start writing `safe' code using
whatever tools they have got, while waiting for static checking to be
available. As long as the dynamic checks are limited to the testing phase and
reasonable `catch(...)' clauses are provided in the final product, such
software is acceptable (at least until static checks become available).
Remember that it is still unclear just _how_ static checks should be performed
(templates, function pointers...)

BTW, is the standard supposed to be finalized this year, or the next?
Anybody has a tentative date?

      Oren.

--
Life is tough and the hard ships are many.




Author: pascual@peggy.tid.es (Pascual Juan)
Date: Thu, 17 Mar 1994 08:55:42 GMT
Raw View
In article <1994Mar16.120538.21607@wisipc.weizmann.ac.il>, oren@white.weizmann.ac.il (Ben-Kiki Oren) writes:
|>
|> IMHO we have two types of code:
|>
|> Normal    Safe

I don't like the word normal. ANSI people are defining the "normal" use of
the language. The first column can be named "current", and fits with coders
who have no need of safety in their code.

     CURRENT      DESIRED by safe coders
     -------      ----------------------
|> No signatures   Has signatures
|> Each function throws anything Each function throws from the
|>      signature only
|> Does not need checks   Needs static checks

It was told in the net that current signatures are unsafe even for game
coders (I won't play a game whith unexpected crashes). It can be generalized
to COMERCIAL code (no one will buy a program wrote in a language who puts
potential bombs inside and can't guarantee there is no unexpected crash).

Current signatures is only valid for "Hello World" and other academic
programs. But when students go to reallity, they will have to get along
to AVOID what you name "normal" use of the language.

|> Every time a `safe' function calls a `normal' one, it should use a
|> `catch(...)'. This will often happen when such `safe' code is forced
|> to invoke third-party software.

One of the "normal" coders shouted (and he was in reason) that he don't
wants to put "throw(...)" all over his functions prototyping. This
very first proposal was rejected. "Safer" coders will shout you the
same way, because your proposal will make they to change all their code
when static checking arrives.

Your proposal means that ALL the COMERCIAL code will have to adopt to
"normal" (current) code. If it can be avoided (you hope it in second version
of the ANSI draft) it has to be done AS SOON AS POSSIBLE to avoid lots of
changes it can produce. The sooner it arrives, the less changes it will.

Let's make a safe language NOW.
--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: djones@megatest.com (Dave Jones)
Date: Fri, 18 Mar 1994 00:21:43 GMT
Raw View


Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sat, 5 Mar 1994 15:56:07 GMT
Raw View
ark@tempel.research.att.com (Andrew Koenig) writes:

>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:

>> No, only calls to functions with no exception specification would
>> need to be wrapped.  I don't see this as a problem.
>
>No, calls to every function without an explicit empty exception
>specification would need to be wrapped.

No.  If the function has a signature throw(X, Y), and it is called
inside a function whose signature is throw(X, Z) where Z is a base
class of Y, then the function call doesn't need to be wrapped.

>Or do you think it's OK to propagate exceptions through { } that don't
>happen to be function bodies?

Yes.

>What justification is there for treating function bodies specially?

They are not treated specially.  The only time that it's not OK to
propagate exceptions through function bodies is when there is an
explicit throw signature for the function.  If there was a way to
specify the throw signature of a single block then the same principles
would apply there.

(N.B. I'm not one of those in favour of making no signature = throw nothing.)

>> So clearly static checking should not require that functions handle
>> these exceptions.  What's the problem?  Are you complaining that
>> this means that functions might throw exceptions that aren't in their
>> signature?  If so, then so what - they might also delete all your
>> files and blow up your monitor.  That's what undefined behaviour means:
>> all guarantees are void.
>
>I am pointing out that if static checking would make it impossible for
>an implementation to offer, as an extension, the ability for + to throw
>an exception on overflot, because that ability would impose a requirement
>to check for that exception.

Nonsense.  The ability would not impose any such requirement.  The
implementation could easily offer programmers a choice as to whether
they wanted a warning, an error, or silent acceptance whenever a
function which does integer arithmetic has an exception signature
doesn't include the `overflow' exception.  Programs written in pure
ANSI/ISO C++ should be written in a way that avoids the possibility of
overflow, and could be compiled with the `silent acceptance' option.
Programs that are not intended to be portable could be written in a way
that only works if the implementation throws `overflow' when
appropriate, and it would probably be best to compile such programs in
a mode where the compiler reported a diagnostic if a function performs
arithmetic but the function's signature doesn't include `overflow'.

>Meta-remark:  Not everyone on the world programs in the same style.
>One should be very careful when advocating language requirements
>that rule out particular styles of programming.

I agree.  But advocating static checking of exception signatures doesn't
rule out a programming style where exception signatures aren't used at all.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: Mon, 7 Mar 1994 06:49:55 GMT
Raw View
In article <john_werner-020394105304@debo.taligent.com> john_werner@taligent.com (John Werner) writes:

> > Safer code is always better.

> I think everyone here agrees with this.

Safer code is always better, if all other things are equal.

But all other things are rarely, if ever equal.  Safety normally has a
cost, and it is an engineering decision whether it is worth the price.
--
    --Andrew Koenig
      ark@research.att.com




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 7 Mar 1994 09:23:50 GMT
Raw View
In article <2kt444$eh2@travis.csd.harris.com> bill@ssd.csd.harris.com writes:
>In article <CLsyqF.JI5@biles.com>, daniels@biles.com (Brad Daniels) writes:
>
>> People keep harping on what a pain it might be to add exceptions if
>> the kind of static checking I've discussed were to be added.  The point
>> of this whole discussion, though, is that any other kind of checking is
>> *dangerous*.
>
>I think that is scare-mongering rather than a thoughtful argument.  How can
>using exceptions without checking be more dangerous than not using
>exceptions at all?  Besides, dangerous is a relative term and far too
>strong to be applied to most software.  If my software fails, no one is
>going to be injured or killed, so dangerous hardly seems applicable.

For the record, there are more things in this life which represent potential
`dangers' than just the threat of loss of life and/or limb.

For example, if some banking application program or some credit reporting
software goes off into the weeds, this could seriously impact one's
financial well-being.  Thus, bugs in such programs are potentially dangerous.

If the long-distance telephone network craps out, someone may not get the
new kidney they have been dying for.

If some mail-handling software decides to take a dump, one's private and
intimate recollections (e.g. about one's senate collegues) may wind up on
the front page tomorrow.

I am often struck by how little people who are themselves *in* this business
seem to appreciate how much effect this business has on peoples lives these
days.  They ought to know better.

The safety of software should (by default) always be considered to be im-
portant unless (and until) proven otherwise.  Over time, I expect to see
this point driven home to more and more practitioners... generally via
lawsuits.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: pascual@peggy.tid.es (Pascual Juan)
Date: Mon, 7 Mar 1994 14:05:38 GMT
Raw View
In article <CM59Er.Dtz@tempel.research.att.com>, ark@tempel.research.att.com (Andrew Koenig) writes:
|> In article <1994Mar2.173048.20330@datascope.com> sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|>
|> > I think this concept of "alternate return" vs. "unanticipated situations" can
|> > be easily muddled.  Maybe you could provide a clearer definition.
|>
|> Fair enough.
|>
|> This example is unrealistic in one tiny respect: a desire for backward
|> compatibility makes it unlikely that << will throw exceptions any time soon.
|>
|> However, suppose it did.  That is, suppose that ostream::operator<<(const char*)
|> were capable of throwing any of a variety of exceptions because of various
|> error situations it might encounter.
|>
|> Then what can we say about the following program?
|>
|>  main()
|>  {
|>   cout << "Hello world" << endl;
|>  }
|>
|> When writing this, I do not expect it to throw an exception.  Indeed,
|> it will do so only in exceptional circumstances.  If it does, I am
|> content to have my program terminate with an appropriate diagnostic
|> message.
|>
|> However, if << has an exception specification
|>   and the language requires static checking
|>   and the default is that functions throw no exceptions,

If no signature means "throw (xmsg)" then:

 ostream::operator<<(const char*) // Means "throw (xmsg)"

 try                     // Internal view
 {                       // of the default
   main();               // exception handling
 }                       // the compiler put
 catch (xmsg exception)  // arround "main()"
 {                       // function.
   // Default handling.  // There is no need of "catch (...) because
 }                       // main is declared as "throw (xmsg)".

 // USER CODE.
 void main() // Means "throw (xmsg)"
 {
   cout << "Hello Wolrd" << endl;
 }

|> then this program will fail to compile.  Whether you believe that's a good
|> thing or not, it certainly is not an acceptable change from current practice.
|> That means we have to change one of the last two things above, since
|> we are assuming for the sake of argument that << has an exception
|> specification.

If no signature means "throw (xmsg)" you haven't to touch any line of code.
That makes old code compatible and safe by recompiling.

|> Well, if you're in favor of static checking, your remaining choice is
|> to say that functions throw all exceptions by default.  But *that* means
|> that every time you wish to add an exception specification to a function,
|> you must also deal with every function that it calls, either by adding
|> an exception specification to it or by wrapping it in a try-catch block.

Sometimes I think people sees exceptions as a "fire alarm" in a program.
When an exception is thrown, everybody runs out of the building, no matter
what's happening. If it is a known exception, you can follow in an ordered
way the specific instructions (the "instance of alarm") distributed by the
loudspeaker. But if it is an unexpected exception, OK, lets jump through
the window! even if the exception deals with "there is no cofee".

|> In effect, the use of exceptions becomes mandatory in every part of
|> your program.  I don't think that's an acceptable change from current
|> practice either.

It's necessary to make mandatory exception handling when it is not part
of the system. Jumping through the window because there is no cofee is
just a confussion, a compiling error.

If no signature means "throw xmsg" then every exception is correctly handled,
even if your code does nothing with exceptions.

|> --
|>     --Andrew Koenig
|>       ark@research.att.com

Please, read my collect and join us.

--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: daniels@biles.com (Brad Daniels)
Date: Mon, 7 Mar 1994 14:29:40 GMT
Raw View
In article <CM59Er.Dtz@tempel.research.att.com>,
Andrew Koenig <ark@tempel.research.att.com> wrote:
...
> main()
> {
>  cout << "Hello world" << endl;
> }
>
>When writing this, I do not expect it to throw an exception.  Indeed,
>it will do so only in exceptional circumstances.  If it does, I am
>content to have my program terminate with an appropriate diagnostic
>message.
>
>However, if << has an exception specification
>  and the language requires static checking
>  and the default is that functions throw no exceptions,
>
>then this program will fail to compile.  Whether you believe that's a good
>thing or not, it certainly is not an acceptable change from current practice.

Agreed.  However, two solutions come to mind.  The first is that main()
is already a special case in many ways, and making it implicitly
throw anything is not inconsistent with the other magic things main()
declarations already do.  Second, as I've put forward a few times already,
there should probably be some kind of unchecked exception capability,
where all function signatures would implicitly include unchecked exceptions.
When changing a widely used standard function, you'd have to either add
the new exception as an unchecked exception, or derive the new exception
from some other exception class already part of the function's signature.

...
>Well, if you're in favor of static checking, your remaining choice is
>to say that functions throw all exceptions by default.  But *that* means
>that every time you wish to add an exception specification to a function,
>you must also deal with every function that it calls, either by adding
>an exception specification to it or by wrapping it in a try-catch block.
>In effect, the use of exceptions becomes mandatory in every part of
>your program.  I don't think that's an acceptable change from current
>practice either.

I definitely agree that's not acceptable, which is why I think "throw nothing",
or rather "throw nothing but explicitly unchecked exceptions", is the best
default.

I saw a recent message from Stroustrup where he mentions preferring dynamic
checking to either no checking or static checking.  I take it you are of the
same opinion?  I really don't understand your reasoning, if so.  Static
checking prevents you from throwing unexpected exceptions at the expense of
some work to maintain specifications.  No checking allows you to throw anything
with the requirement that you enforce programming discipline to catch what-
ever you throw at some point in the program.  Dynamic checking, however, can
cause a program to go into unexpected() at unpredictable times when changes
are made in any routine with such a specification.  It provides the worst
features of both static checking and no checking, in that you still have to
change your program to avoid throwing illegal exceptions, but the compiler
can't tell you where you need to add them, and you can't have catch-all
handlers toward the top of your call chain to handle exceptions you didn't
expect.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: stt@spock.camb.inmet.com (Tucker Taft)
Date: Mon, 7 Mar 1994 15:09:40 GMT
Raw View
In article <CM59Er.Dtz@tempel.research.att.com>,
Andrew Koenig <ark@tempel.research.att.com> wrote:
> . . .
>Well, if you're in favor of static checking, your remaining choice is
>to say that functions throw all exceptions by default.

There is yet another alternative, namely that static checking only
applies to functions that have explicit exception signatures
calling functions with explicit exception signatures, or
explicitly calling "throw()."  This seems to avoid the problems you
cited, while allowing those who are compulsive (;-) about exception
signatures to get full checking.

>    --Andrew Koenig
>      ark@research.att.com

S. Tucker Taft   stt@inmet.com




Author: cbarber@bbn.com (Christopher Barber)
Date: 07 Mar 1994 15:31:50 GMT
Raw View
>>>>> "MA" == Matt Austern <matt@physics2.berkeley.edu> writes:

    MA> In article <1994Mar2.205446.16481@tid.es> pascual@gonzo.tid.es
    MA> (Pascual Juan) writes:
    >> 4.- Pointers to function in general (member, non-member, static)
    >> will add new features in order to declare it with the exceptions
    >> thrown.

    MA> Function pointers aren't the only time when code is being called
    MA> indirectly.  Two other cases:

    MA>     (1) If you're using a virtual function, then you don't know, at
    MA>         compile time, which function is being called.

So you just make sure at compile time that methods have compatible
signatures with the same methods from the base class.  No problem.
[Compatible means that the exception signature of the derived method
contains only exceptions which are the same as or derived from exceptions
in the base class method.]

    MA> (2) If you're using a template, then you don't know at compile time
    MA>         what types you're dealing with, so you don't know what
    MA>         functions are being called.  The only possible exception
    MA>         signature for Vector<T>::Vector(int Length), for example,
    MA>         is throw anything, because you don't know what exceptions
    MA>         might be thrown by T's constructor.

This is not really that much of a problem.  Many things about templates
can't be checked until instantiation time.  For instance, I have several
templates that assume that T has a particular method that gets called by
one of the template methods; this can't be checked until the template is
instantiated.  Same goes with exception signatures, but so what?  The
checking still gets done at compile time...

    MA> Compile-time exception specification checking would require very
    MA> severe restrictions on the use of templates, function pointers, and
    MA> virtual functions.  I don't think it's worth it.

Doesn't seem that severe to me.

    MA> Andrew Koenig, one of the authors of the original exception
    MA> proposal, has said that he didn't want exception specifications to
    MA> be part of the language at all.  I think we ought to take his
    MA> opinion seriously: he has a good deal more experience with
    MA> exceptions than most of us.

I think that we all respect Andrew's opinion on the matter, but I think
that he is doing perfectly fine defending his views without our deferring
to him on the basis of his experience and role in the original proposal.
One could say the same thing about Bjarne, but he clearly has a different
view on the matter; so who are we to believe?

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: cbarber@bbn.com (Christopher Barber)
Date: 07 Mar 1994 15:48:30 GMT
Raw View
>>>>> "Me" == Christopher Barber <cbarber@bbn.com> writes:

    Me> I think that we all respect Andrew's opinion on the matter, but I
    Me> think that he is doing perfectly fine defending his views without
    Me> our deferring to him on the basis of his experience and role in the
    Me> original proposal.  One could say the same thing about Bjarne, but
    Me> he clearly has a different view on the matter; so who are we to
    Me> believe?

Whoops.  For some reason when I read Bjarne's post, I thought that his
point of view was different from Andrews.  Must be that I had not had my
coffee yet :-)

Anyway, I still think that it is ok to disagree with the gurus from time
to time.  If for no other reason, so that we learn by the debate.

- Chris

--
Christopher Barber
(cbarber@bbn.com)




Author: daniels@biles.com (Brad Daniels)
Date: Mon, 7 Mar 1994 15:50:52 GMT
Raw View
In article <CMAvG5.AD9@inmet.camb.inmet.com>,
Tucker Taft <stt@spock.camb.inmet.com> wrote:
>In article <CM59Er.Dtz@tempel.research.att.com>,
>Andrew Koenig <ark@tempel.research.att.com> wrote:
>> . . .
>>Well, if you're in favor of static checking, your remaining choice is
>>to say that functions throw all exceptions by default.
>
>There is yet another alternative, namely that static checking only
>applies to functions that have explicit exception signatures
>calling functions with explicit exception signatures, or
>explicitly calling "throw()."  This seems to avoid the problems you
>cited, while allowing those who are compulsive (;-) about exception
>signatures to get full checking.

This approach is essentially equivalent to static checking with no
signature meaning "throw anything".  I think we've discussed why that
particular approach can be a major pain.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Mon, 7 Mar 1994 16:43:37 GMT
Raw View
In article <2kvuob$46q@travis.csd.harris.com> you wrote:
: In article <1994Feb25.192710.18677@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
: > It seems to me that this whole discussion is less about static vs. dynamic
: > checking than it is about that age old issue of compatibility vs. usability.
: > Static type checking for exceptions is obviously the right choice.  Anyone
: > who denies that denies C++.

You left out the rest of my argument.  C++ as well as ANSI C are strongly typed
(statically typed) languages, therefore static exception checking is more C++
like than dynamic checking.  Exception are no less important than types,
therefore if the same guidelines which were used as the basis for C++, have not
changed then I see no reason for dynamic checking to be promoted.

: Things just aren't that well-defined.

I agree, its not "well-defined", that's why were discussing it.

: > If the dynamic checking responders are still not satisfied with the
: > arguments set forth by the static checkers, I would like to leave you
: > with some food for thought from my domain.  The company I work for is a
: > manufacturer of medical equipment.  When our system goes to unexpected(),
: > not just the application dies!  Those who have their fingers on the
: > keyboard ready to tell me how to change unexpected() to something which
: > "recovers", should think about what recovery is possible when an unknown
: > event occurs during a surgical procedure.

: Well I don't understand what all the fuss is about.  Nothing is stopping
: you from putting "throw()" on all your function signatures, and nothing is
: stopping your compiler from giving warnings if you call a function whose
: signature doesn't match the caller's.  In fact, the compiler can even offer
: an option that makes those warnings into errors -- it just can't claim
: standard conformance in the presence of that option.  (That's not unusual
: in the compiler world.)

: If your compiler doesn't produce the warnings, get another compiler.
: Surely vendors who want to sell to the "life-critical" market will be
: willing to add features designed to support it.

In the real world, their are few choices of compiler vendor.  Witness the
PC world, which has only a handful of C++ compilers.  Compiler vendors
for embedded system are not neccessarily "life-critical" vendors.  Also,
their market is minute in comparision to the general computing market and
their budgets likewise.  These vendors are not going to initiate static
checking R&D (at least not any time soon) if the C++ committee does not at
least mandate static checking (here I refer to the compromise position in
which exceptions are checked statically and produce warnings).

: So, you get your "static checking" that you want, as long as you pay
: attention to those warnings.  Meanwhile, those of us who aren't involved in
: developing life-critical applications don't pay for it if we don't want it.

... and they lived happily ever after (except for the poor sole whose life
depended on C++ exception handling).

: I think the burden of providing for "armor-plated" applications should be
: at least partially on the developers of those applications.  Now the
: static-checkers have said that I can get "unchecked" throw specifications
: by adding "throw(...)"  everywhere.  I'll explain in another post why that
: isn't always feasible.  But more importantly, why should I consider that a
: solution to the problem, when asking them to add "throw()" everywhere is an
: equally viable solution, and affects far fewer people?

Because the latter is the conservative solution.  By your own admission it is
no more difficult and we would err on the side of safety.  Also, when you say
"far fewer people" I assume you mean programmers, not the user.  A tendency
toward benefiting one self is a part of human nature, but so is respect for
human life.

: > I wish the "compatibles" would stop dismissing the "usables" as simply purists,
: > and stop introducing backward compatibility loop-holes which can undermine the
: > integrity of the applications built with the C++ language.

: In my opinion and experience, a language that tries to insure against all
: possible failure cannot achieve mainstream support.  It is simply too
: costly.

I'll dismissed the first sentence as unsubstantiated.  The second sentence ...
what costs?  We are talking about one alternative over another.

: That's okay, but it unfortunately means that developers of
: critical applications either have to use a different language or at least a
: different dialect of that language.  One might lament that situation, but
: lamentations are unlikely to change the outcome.

That's strange, I recall some lamenting about "const".  Perhaps you
should propose a dialect of C++ that does not have const.
I really can't phathom your logic here.  "Developers of critical applications"
should "use a different language", perhaps one with strong type checking, and
const protection.  C++ and ANSI C were designed with these "critical
applications" in mind.  Perhaps _you_ should consider Smalltalk or one of its
cousins, which do not have strong type checking and const mechanisms.

: On the other hand, if those developers are willing to put forth some
: effort, we can have a language that can *help* insure against failure,
: provided you follow certain guidelines (like never ignore a compiler
: warning, period).  I think that's a reasonable position to take.

Compiler developers are having a hard enough time keeping up with the C++
language, these compilers won't have static checking for some time (if ever).
I think a reasonable solution would be for programmers to put into their
code their intentions (explictly) and not have the code kill someone.

Just for the record, I'm all for compilers which will produce warnings/errors
at compile time... but it must be mandated by the C++ committee or it will
not be provided by compiler vendors (at least not the compiler vendors whose
clients need it most (embedded systems).


Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: swf@tools3teradata.com (Stan Friesen)
Date: 7 Mar 94 17:19:41 GMT
Raw View
In article <1994Mar1.100008.20784@mole-end.matawan.nj.us>, mat@mole-end.matawan.nj.us writes:
|> In article <1994Feb25.192710.18677@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|>  ...
|> > If the dynamic checking responders are still not satisfied with the arguments
|> > set forth by the static checkers, I would like to leave you with some food
|> > for thought from my domain.  The company I work for is a manufacturer of
|> > medical equipment.  When our system goes to unexpected(), not just the
|> > application dies!  ...
|>
|> Yours is a very real dilemma.  Static checking is appropriate in your case,

No, I don't think so.  What I would do in this case is:
 1. Use *no* exception signatures.

 2. If I *must* use a libarary that includes functions
    with exception signatures, I would redefine unexpected()
    to throw an "unchecked" exception somehow.  (Does the proposed
    standard state that if unexpeced() throws an exception it is
    passed on?) [I would first try to avoid libraries with
    exception signatures].

 3. At the 'critical' points I would put in a "catch(...)"
    where I would do what is necessary to safely allow
    continued operation and then go on.

Voila.  The program will never die, and it will continue to operate,
perhaps in 'minimal function mode', even after a completely unexpected
exception.

I do not see that there is really much problem with this.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: chase@Think.COM (David Chase)
Date: 7 Mar 1994 19:31:40 GMT
Raw View
tmb@arolla.idiap.ch (Thomas M. Breuel) writes:
|> Lots of people seem to be arguing that strictly enforced exception
|> signatures are important and do not interfere with development and
|> maintenance.  I'm curious: who has actually done a non-trivial project
|> in a language that enforces exception signatures?

Depends upon what you mean by "enforce".  There was a system (OS, compiler,
run-time) for the Acorn RISC machine that was programmed in Modula-2+,
and though the language used the "default=any" rule, the compiler would
issue warnings (but not reject) at places where an out-of-signature
exception might be thrown or propagated.  Mick Jordan and Steve Glassman
decided to conduct an experiment (you know, what you do in a "science")
to study the effects of a "default=none" rule in a language with exception
handling, and recompiled the whole wad with a tweaked compiler.  I don't
have the tech report in hand (and Olivetti Research Center is long defunct,
so good luck getting a copy) but the result was that the number of forced
repairs was not onerous, and they found about a half-dozen latent bugs
(where an exception was not anticipated, and would be converted to a
failure).  This tech report (Olivetti Software Technology Lab) could not
have appeared later than February of 1990, because that's when we were
all sacked.

Modula-3 originally used the default=any rule, though the Olivetti
compiler issued warnings for out-of-signature exceptions.  The language
went through two rounds of changes -- the first and second versions used
default=any, but the third version (after the "12 changes") adopted
default=none.  In this case, there was very little burden on the Olivetti-
derived code, because that group of people (besides having dispersed to
other employment) had always coded with full exception specifications,
and paid attention to warnings.  Coding in this style was not a problem,
and the people at DEC-SRC continue to generate code (window system
packages, window system interfaces, compilation tools, libraries) without
obvious complaining (and I know that they employ at least one person who
declared "I want to work someplace where the programming environment is
at least as good as the one that I had ten years ago at Xerox", so it's not
as if they all suffer in silence).

I recall several "discussions" along the same lines as those seen here
justifying the various points of view ("what if I/O starts throwing network
exceptions"), and the case for default=none was actually harder to make,
because exceptions in Modula-3 cannot be subtyped.  I'm not sure what
actually swayed the committee to change their mind -- perhaps it was decided
that additional updates forced by default=none (additional above and beyond
those you had to do anyway) were minor.  Perhaps it was the case that, using
the standards for reliability held by the community of M-3 programmers (at
the time, a fairly small and relatively homogenous group), it was believed
that there was no point not fixing the warnings pointed out at recompilation,
and that when an interface changed, it did indeed invalidate intermediate
libraries, so any argument that it should be possible to continue using
unrecompiled objects through which new exceptions would propagate was just
rejected out of hand.

|> ..... In standardizing C++, why not try to stick with
|> technology of which we know that it works and is useful?

True enough.  Some of us have different experiences, and different opinions
about what works and is useful.  I've done casual programming in Modula-3
and C++, and I've done serious (>100Kloc, CPU-years of testing, multiple
marketing fire-drills, benchmark races, goal of zero bugs) programming in
C++.  Exception handling did not hinder my casual M-3 programming at all
(after all, it's ok for a toy program to just fall over).  In the case of
C++, it was clear that exception handling in either flavor would be a Good
Thing, because it removes the need to insert references to
application-specific error routines in what should otherwise be reusable
code (why should my flow analysis code need to include stdio.h, just because
it might reject some programs?).

Note that there are major differences between the Modula-3 "templates"
and C++ "templates" and that (in my opinion) neither one works terribly
well.  This complicates the exception picture, and I haven't thought
through a solution to propose (nor do I care to).  Given this glitch
(which makes the two situations somewhat less comparable) it is probably
best to leave the C++ exceptions alone for now.  Nonetheless, I would
encourage compiler writers to issue warnings for uncaught exceptions,
and I would encourage library writers to avoid the use of throws-any
except where it is really necessary.  Subtyping of exceptions should
make such signatures rarely necessary.  The committee should consider
at least providing syntax for exception signatures on function types,
even if they have no semantic force, because many programmers (Steve
Cipolli, me) would find them useful.

David Chase, speaking for myself
Thinking Machines Corp.




Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 08 Mar 1994 07:39:55 GMT
Raw View
In article <1994Mar7.164337.22230@datascope.com> sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|... and they lived happily ever after (except for the poor sole whose life
|depended on C++ exception handling).
|[...]
|Because the latter is the conservative solution.  By your own admission it is
|no more difficult and we would err on the side of safety.  Also, when you say
|"far fewer people" I assume you mean programmers, not the user.  A tendency
|toward benefiting one self is a part of human nature, but so is respect for
|human life.
|[...]

If you can't write safety critical applictions in C++ without static
exception checking, you shouldn't be writing safety critical
applications in any language.

Static exception checking might, at best, reduce the cost of
designing, verifying, and testing safety critical code.  But that
reduction in cost needs to be weighed carefully against the increase
in language complexity, compiler complexity, and maintenance
complexity, and the associated costs and risks (!).

Evidently, many people (myself included) still believe that the costs
associated with adding static exception checking are significantly
larger than any potential benefit.

     Thomas.




Author: pascual@peggy.tid.es (Pascual Juan)
Date: Tue, 8 Mar 1994 13:50:11 GMT
Raw View
In article <TMB.94Mar8083956@arolla.idiap.ch>, tmb@arolla.idiap.ch (Thomas M. Breuel) writes:
|> In article <1994Mar7.164337.22230@datascope.com> sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
|> |[...]
|> |Because the latter is the conservative solution. By your own admission it is
|> |no more difficult and we would err on the side of safety.
|> |[...]
|>
|> If you can't write safety critical applictions in C++ without static
|> exception checking, you shouldn't be writing safety critical
|> applications in any language.

Hi Thomas, it's me again. I know you are a experienced exception user in
non-trivial systems. I think you can see the danger enbeded in the following
example where "->" means "calls" and "<-" means "propagates an exception":

  Main prog.            lib A             lib B         lib C
 _____________    _________________    __________    __________
 main() -> f() -> g() -> h() -> i() -> j() -> k() -> l() -> m()
                                unexpected
        <-     <-     <-     <-     <=     <-     <-     <- xC

Exception of the class xC are in the signature of k(), l() and m() functions,
but j() forgot to put it. As i() didn't expected such an exception, who had
to be catched by j(), it becomes in run-time to an unexpected who propagates
to main(), who stops warning by cerr:

 "Something was wrong, I don't know where, I don't know why!"

It really has no information to talk about. You don't have information to
fix the bug. The user hasn't lawyers enough to sue you for damages.

If it didn't still happened to you I really hope you keep such a luck, but
Murphy's laws plays against you.

Ok, you can ignore unexpected exceptions with set_unexpected(), but it can
make your program work for seconds to hours, dumping finally a core who
doesn't deals with xC nor m(). You'll get crazy looking for the bug.

It would have been avoided with compile-time checking. You are already
forced to put signatures if you want your signatures propagates correctly,
otherwise unexpected will be thrown just from each "throw".

I don't want big changes in the language, but a correct and orthogonal
behaviour of the existing exceptions proposed adding as less as possible.

|> Static exception checking might, at best, reduce the cost of
|> designing, verifying, and testing safety critical code.  But that
|> reduction in cost needs to be weighed carefully against the increase
|> in language complexity, compiler complexity, and maintenance
|> complexity, and the associated costs and risks (!).

How do you check your code? Are you sure you didn't forget a signature
in a function prototyping? I want it to be syntacticaly wrong, otherwise it
goes from computer science to FAITH.
--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Tue, 8 Mar 1994 16:33:17 GMT
Raw View
pascual@gonzo.tid.es (Pascual Juan) writes:

>    3.- No exception signature means "throw xmsg" instead "throws unexpected"
> (xmsg is the base class of standard exceptions propossed).

I disagree with this for the same reason that Bill Leonard has stated
and Andrew Koenig has hinted at: users should NOT be forced to use
exception signatures if they don't want to.  I'm certainly not convinced
that I will want to use them.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Tue, 8 Mar 94 08:54:21 PST
Raw View
In article <1994Mar8.135011.11910@tid.es>, pascual@peggy.tid.es (Pascual Juan) writes:
|> In article <TMB.94Mar8083956@arolla.idiap.ch>, tmb@arolla.idiap.ch (Thomas M. Breuel) writes:
|> |>
|> |> If you can't write safety critical applictions in C++ without static
|> |> exception checking, you shouldn't be writing safety critical
|> |> applications in any language.
|>
|> ... I think you can see the danger enbeded in the following
|> example where "->" means "calls" and "<-" means "propagates an exception":
|>
|>   Main prog.            lib A             lib B         lib C
|>  _____________    _________________    __________    __________
|>  main() -> f() -> g() -> h() -> i() -> j() -> k() -> l() -> m()
|>                                 unexpected
|>         <-     <-     <-     <-     <=     <-     <-     <- xC
|>
|> Exception of the class xC are in the signature of k(), l() and m() functions,
|> but j() forgot to put it.

So, DON'T USE EXCEPTION SIGNATURES!  Then everything works just fine,
thank you.  All you have to do is 'catch(...)' *somewhere* and restart
processing is 'emergency mode'.  I know that is standard in life-critical
applications, since my second job was for a firm that *did* life critical
applications, and when the software discovered a 'serious' fault, it switched
into 'do the safe thing' mode.
[In some cases the code check-summed itself periodically and went into
emergency mode if the check-sum check failed].

If you *must* use a library that has exception signatures, then register
an unexpected handler as follows:
 my_unexpected()
 {
     throw;
 }

Now you have effectively cancelled any exception signatures that have
been forced upon you.

|> As i() didn't expected such an exception, who had
|> to be catched by j(), it becomes in run-time to an unexpected who propagates
|> to main(), who stops warning by cerr:

Actually, as currently proposed, i think it is even worse than that!
unexpected() simply calls abort(), so you never *get* to main().
If you did, it would be simple enough to put a "catch(...)" there.

|> Ok, you can ignore unexpected exceptions with set_unexpected(), but it can
|> make your program work for seconds to hours, dumping finally a core who
|> doesn't deals with xC nor m(). You'll get crazy looking for the bug.

Not if you set_unxpected() to a function that rethrows the original
exception, and you make sure to catch and deal with *all* exceptions
somewhere in the call stack - in main() if nowhere else.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Tue, 8 Mar 1994 16:54:45 GMT
Raw View
tmb@arolla.idiap.ch (Thomas M. Breuel) writes:

>Lots of people seem to be arguing that strictly enforced exception
>signatures are important and do not interfere with development and
>maintenance.  I'm curious: who has actually done a non-trivial project
>in a language that enforces exception signatures?

I think the emphasis here is on "seem to be arguing", at least in my
case.  I've been arguing that static checking of exception signatures
is better than runtime checking of exception signatures.  But
that doesn't imply that it's better than no checking at all.

>What I do know is that I and others have built large software systems
>in languages that do not enforce exception signatures.  Exceptions in
>those languages have been very useful.  The lack of static checking has
>been found to work well from the point of view of development and
>maintenance and doesn't seem to have caused difficulties for building
>reliable software.

Do these same languages include runtime checking, the default
behaviour of which is to abort the program if an unexpected exception
is thrown?  (Do they include exception signatures at all?)

The point is that although the lack of static checking may not
cause any problems, the presence of runtime checking probably will.

I would be much happier with C++ exception handling if the default
behaviour of `unexpected()' was to simply pass the exception through.
If `unexpected()' is going to call `abort()', then I want my compiler
to at the very least warn me whenever a function might call `unexpected()'.
Hence the desire for static checking.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: cbarber@bbn.com (Christopher Barber)
Date: 08 Mar 1994 18:49:33 GMT
Raw View
>>>>> "TMB" == Thomas M Breuel <tmb@arolla.idiap.ch> writes:

    TMB> If you can't write safety critical applictions in C++ without
    TMB> static exception checking, you shouldn't be writing safety
    TMB> critical applications in any language.

This is a content-free argument.  You can probably say this about
any modern general-purpose programming language and any type of
programming:

 "If you can't write X applications in the Foobar language, you shouldn't
  be writing them in any language."

I think few are arguing that it would be impossible to develop safety
critical applications in a C++ with dynamic checking.  The argument is
that adding static checking would greatly improve the confidence of
developers of such applications that their code is safe.

This would be only one of many things that could be improved in the
language to make it easier to write "safe" code.  There are other languages
that provide much better safety guarantees than C++ does.  For instance,
Modula-3 guarantees that there will be no unchecked run time errors unless
"UNSAFE" features of the language are used.  It would be really nice if
there could be some way to do the same for C++ (if for no other reason, to
shut up the C++ bashers on comp.lang.ada ;-).

    TMB> Static exception checking might, at best, reduce the cost of
    TMB> designing, verifying, and testing safety critical code.

Not just safety critical code.  Perhaps you are forgetting how expensive
it can be to fix bugs discovered after release time.  Since unchecked
exception bugs can't be found until runtime, there is a decent chance
that pre-release testing might not discover it before the code is released
to the customer, and even if it is found then, it is still very expensive
to fix compared to the cost of fixing it when the code is originally
compiled.

    TMB> reduction in cost needs to be weighed carefully against the
    TMB> increase in language complexity, compiler complexity, and
    TMB> maintenance complexity, and the associated costs and risks (!).

Yup, but that is true of any proposed language change.  Note that adding
functionality to the language does not necessarily make it more complex!

In the case of static checking there would be no visible complexity added
to the language, and in many ways would be simpler: if you see a function
without an exception signature it won't throw any exceptions (except
perhaps fatal ones), if a function pointer has no exception signature
then the function to which it refers will not throw any exception (this
assumes that along with static checking, we would want to make exceptions
part of the function type), etc.

There definitely would be complexity added at the compiler/linker level,
but I think that most of the cost would be in adding exceptions to the
function type (as if name mangling wasn't bad enough without it ;-).  If
that is done, static checking won't be much more onerous than checking
function signatures against each other.

There might be some extra chores for some people with existing code
following the existing semantics, but I and others have argued that this
would not really be that bad.  Obviously, this is a matter of opinion.

I am not sure what you mean by "associated risks".

    TMB> Evidently, many people (myself included) still believe that the
    TMB> costs associated with adding static exception checking are
    TMB> significantly larger than any potential benefit.

Clearly, or there would be no debate :-)
But there are also those of us who feel quite the opposite.

- Chris


--
Christopher Barber
(cbarber@bbn.com)




Author: daniels@biles.com (Brad Daniels)
Date: Tue, 8 Mar 1994 21:41:53 GMT
Raw View
In article <9406802.27876@mulga.cs.mu.oz.au>,
Fergus Henderson <fjh@munta.cs.mu.OZ.AU> wrote:
>pascual@gonzo.tid.es (Pascual Juan) writes:
>
>>    3.- No exception signature means "throw xmsg" instead "throws unexpected"
>> (xmsg is the base class of standard exceptions propossed).
>
>I disagree with this for the same reason that Bill Leonard has stated
>and Andrew Koenig has hinted at: users should NOT be forced to use
>exception signatures if they don't want to.  I'm certainly not convinced
>that I will want to use them.

It doesn't sound like you're responding to what he's trying to say, or maybe
I misunderstand what he's trying to say...  Basically, "xmsg" would implicitly
be part of the exception signatures of all functions.  Any exception derived
from that class could then be passed without any checking.  Exceptions which
should not normally be checked should all inherit from xmsg.  Those wanting
to send type-checked exceptions would simply throw exceptions not derived
from xmsg.  This seems like a pretty low-cost approach all around.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: djones@megatest.com (Dave Jones)
Date: Tue, 8 Mar 1994 22:42:22 GMT
Raw View


Author: matt@physics2.berkeley.edu (Matt Austern)
Date: 08 Mar 1994 23:11:43 GMT
Raw View
In article <CBARBER.94Mar7103150@apricot.bbn.com> cbarber@bbn.com (Christopher Barber) writes:

>     MA> (2) If you're using a template, then you don't know at compile time
>     MA>         what types you're dealing with, so you don't know what
>     MA>         functions are being called.  The only possible exception
>     MA>         signature for Vector<T>::Vector(int Length), for example,
>     MA>         is throw anything, because you don't know what exceptions
>     MA>         might be thrown by T's constructor.
>
> This is not really that much of a problem.  Many things about templates
> can't be checked until instantiation time.  For instance, I have several
> templates that assume that T has a particular method that gets called by
> one of the template methods; this can't be checked until the template is
> instantiated.  Same goes with exception signatures, but so what?  The
> checking still gets done at compile time...

The checking isn't what I'm concerned about.  It certainly would be
possible to write a compiler that would refuse to instantiate a
template if the exception signatures weren't compatible; my concern is
that I can't think of any sensible way to declare the exceptions that
would actually do something useful with static checking.  The problem
of exception specifications and templates is, in my opinion, rather
more serious than most people have recognized.

I'm going to be pedantic and go through an example of this in some
detail.  I might add, before I begin, that it was when I was trying to
do exactly this that I decided there was no good solution, and decided
not to use exception signatures in my current project.

So: let's suppose I have a class List<T>, and let's suppose it has a
member function List<T>::Add(const T&).  This function takes a
reference to a T, makes a copy of it, and adds it to the list.  The
problem is: what should we use for the exception specification of
List<T>::Add(const T&)?  Well, pretty clearly Add() can throw xalloc,
and also it can throw any exceptions thrown by T::T(const T&).  How
should we express this in Add()'s exception specification?

If we're going to use exception specifications, and if we demand that
List<T>::Add(const T&) not throw anything not in its specification, I
can see only three options:

 (1) Declare that the exception signature is, say, (xalloc,foo),
     and refuse to instantiate the template if T::T(const T&)
     throws anything other than xalloc or foo, or, of course,
     something derived from them.  [And, of course, we could
     imagine that "foo" stands for a list of several acceptable
     exceptions, or for an empty list.]
 (2) Declare that the exception signature is (xalloc,foo), and
     put a catch(...) wrapper around the call to T::T(const T&).
     Any exceptions thrown in T's copy constructor will be
     rethrown as foo.
 (3) Declare that the exception signature is throw anything.
     Of course, it can't really throw anything---it can really
     only throw xalloc or something thrown by T::T(const T&)---
     but we can't express this any way other than by the overbroad
     "throw any".

All three options, in my opinion, are unsatisfactory.  Let's take them in
order.

Option (1) is unsatisfactory because it means that List<T> is no
longer a generic container class: you can't use it for lists of
arbitrary objects.  You c you can't use it for lists of
arbitrary objects.  You can't for example, use it to hold objects that
you get from someone else's class library---not, at any rate, without
defining a new wrapper class.  We're cutting off T's head to fit
List<T>'s bed.

Option (2) is also unsatisfactory.  If T::T(const T&) is throwing an
exception, it's signalling that something specific is going wrong.
Putting in some general-purpose wrapper means that this information is
lost; instead of knowing about (and maybe dealing with), say, a
printer that's offline, or a file that can't be opened, or a
connection that can't be established, all you know is that an
insertion into a list failed.  This goes against the whole idea of
throwing a specific class as an exception.

Finally option (3)---the least unsatisfactory, in my opinion, because
at least you're still allowing the insertion of general objects into
your list, and you can still deal with exceptions thrown by
T::T(const T&).  Still, this makes it very hard to use a List<T> if
you insist on the ability to do static checking of exception
specifications.  Every time you have List<T>::Add(const T&) in a
function whose exception specification is more restrictive than "throw
any", you'll have to surround it with a catch(...) wrapper.

The problem is that exception specifications, as currently defined,
don't allow you to express the real truth about what exceptions
List<T>::Add(const T&) can throw: it can throw xalloc, plus anything
thrown by T::T(const T&).  Whatever we do declare as the exception
signature is wrong: either more restrictive than what we really want,
or less so.  I don't know which of those is worse; my solution, as I
said before, was to give up on the whole idea of using exception
specifications for a generic container class.

We might imagine defining a new syntax for exception specification, so
that we really could define the exception signature of
List<T>::Add(const T&) to be "anything thrown by T::T(const T&), plus
xalloc".  Personally, I think that this new syntax would be annoyingly
complicated, and of doubtful benefit.
--
Matthew Austern                       Never express yourself more clearly
matt@physics.berkeley.edu             than you think.    ---N. Bohr




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 2 Mar 1994 19:19:34 GMT
Raw View
In article <1994Feb28.175754.19509@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
> I guess you believe the C/C++ committes designed the "const" mechanism to
> annoy you, not to allow checking which prevents modification to read-only
> or ROM'ed data.  Not using const in C++ is either unsafe or inefficent,
> since when passing an (large) object into a function you will either
> waste time passing the whole object or pass it by reference which opens
> up the possibility that the callee could modify your object without your
> knowledge.

I don't believe that at all.  I just know from experience that it cannot be
used effectively if it is not used absolutely, totally, completely
consistently.  And I know that I can't use it consistently because the
libraries I use didn't do so.  So I'm stuck.  I am certain the same will
happen with static checking of exceptions.

> : Warnings are a distraction, or else they just get totally ignored.  If you
> : intend to pay attention to some warnings, then you have to examine all of
> : them to discern which ones are interesting.  If throw signatures, or lack
> : thereof, generates lots of warnings, then there is an increased chance I
> : will miss the ones I am really interested in.  In other words, if the
> : default means "throws nothing", I don't care whether violation is an error
> : or warning, because both will be a major irritant to me.
>
> This assume you plan to undermine the exception mechanism in much the same way
> as you did with const.

Again, no.  It just means I would opt, at least for now, to not use static
checking.  I intend to program as though *any* exception could be raised,
regardless of what a function may tell me.  I don't see that static or
run-time checking buys me anything, which is why I would opt for not
putting throw signatures on anything.

> : I agree with the choice of "default means throws nothing", for much the same
> : reason as I avoid "const" if I can.  I don't have the time to go back and
> : change all my code to consistently use "const", nor would there likely ever
> : be time to go through and change all the function signatures in a large
> : project all at once.
>
> I hope you mean you agree with "default means throws everything", since new
> will throw xalloc for all your existing code.  In any case I'll take
> "default means throws nothing except system specific emergencies".

Yes, sorry, that was a mistype.

> So your argument is that you don't like "const" and so you mis-use it,

No, my argument is that const cannot be used if you use even one library
that wasn't consistent with it.  I don't mis-use it, I just don't use it at
all if I can help it.  I don't see how that is misuse.

> and so therefore exceptions should be implemented in a way as to
> let you mis-use it more easily.  I think your argument better supports the
> argument for static checking (like const) by precedent.  Your use of const
> argues strongly for strict static checking to prevent mis-use of potentially
> lethal language mechanisms like dynamic checking of exceptions.

As I said, I don't think any checking of exceptions buys me anything.  And
just for the record, I've said nothing about the exception mechanism being
implemented so it can be misused.  I've said two things: I want the option
of not using exception signatures at all, thus getting neither static nor
dynamic checking; to be able to make that choice, the default signature
needs to be "throws anything".

> I hope and pray that the software you are writing does not hold the balance
> of human life.  If the modification of what should be const objects does not
> kill someone, the time spent in unexpected() certainly will.

Oh, come on!  Software just isn't that reliable that the presence or lack
of static checking of exceptions will make the slightest detectable
difference in software failure rates.  Why don't you argue for a compiler
that will detect logic errors?  That would have a far more profound effect
on software quality!

Besides, it just so happens that the software I am writing does NOT hold
the balance of human life.  That also happens to be true of a large
majority of programmers.

> I dislike approaches which undermine the integrity of the applications written
> in a language just so someone doesn't have to type in some prototypes.
> As for the "existing code" argument, if you were not planning on having to
> change your code, why did you choose a language which is still evolving? or
> at least why would you expect to be able to upgrade your code to a later
> version of the language definition without altering your existing code?

I never said I wouldn't change my code.  My argument is based on the fact
that I will be using libraries I don't control, so I *can't* change the
prototypes.

> As for robustness see David Chase's (I believe) response which basically
> said (paraphrase) ...
> How robust can your code be if it can throw exceptions which you might not
> know about.

Number one, using exceptions at all is likely to make my code more robust
than it is now, even if there is a chance of raising an unexpected
exception.  Number two, I would code with the expectation that *any*
exception can be raised, so there's no such thing as an unexpected
exception.  Number three, the robustness of my code depends on more than
whether an unexpected exception can occur, and those other factors cause
lots more bugs.  I'll take the possibility of an unexpected exception, if
it means I can eliminate some really nasty bugs that I know I have now.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: daniels@biles.com (Brad Daniels)
Date: Wed, 2 Mar 1994 20:20:33 GMT
Raw View
In article <2l2nk3$qbv@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
>In article <CBARBER.94Mar1181633@apricot.bbn.com>, cbarber@bbn.com (Christopher Barber) writes:
>> I am not really sure what this "extra" work is beside having to add
>> explicit exception signatures to functions, and that should only be
>> mildly painful when you first convert over your code.
>
>As I said before, the problem is that "default means throws nothing" will
>be a problem when using libraries owned by someone else.  I don't have the
>option of changing the header files once -- if I change them at all, it
>will have to be a copy of the header files.  That work will have to be done
>over and over, everytime those header files change.

Yes, but if the default for you is "no spec means throw nothing", the default
is the same for them, as well.  That means they *can't* throw any exceptions,
and the default is correct, assuming they're using the same version of the
compiler as you are.  If they're using a different version of the compiler
than you are, there's a good chance that some other aspect of the objects
will be broken relative to your current compiler release, at least if past
history is any indication.

>Don't forget that one set of header files are the system header files,
>which come with the OS.  So every new release of the OS would result in
>another iteration of header-file changes.

If those functions come to throw exceptions, they will be well-defined
and fairly static.  Besides, this is an argument for the "default means
throw nothing" camp, not the "throw anything" camp.  If the default is to
throw nothing, nobody needs to do anything.  If the default is to throw
anything, you need to add "throw ()" to all the system routines to make
them play nice.  Of course, since these routines all have "C" linkage in
most cases, you could just say that "C" linkage functions can never
throw exceptions...

>> My point was that
>> there are a lot more engineers out there who are concerned with reliability
>> than you make it seem.  If you ignore their needs, they may abandon the
>> language and make for a smaller market in which to sell your tools.
>
>As I said in a previous post, I think this reliability issue is a red
>herring.  Whether or not we have static checking of exceptions, software is
>going to fail.  Static checking of exceptions isn't addressing even one of
>the most common causes of software failures today.  When you've addressed
>those causes, then maybe we can discuss reliability vis-a-vis exceptions.

Of *course* wild exceptions aren't a major cause of failure today.  They
are hardly ever *used* anywhere today!  I prefer to introduce them in
a clean, controlled manner as I add them to my programs or write new programs.

>>    They may be a small portion now, but there are lots of people who hope
>>    that C++ will help change that situation.  One of the goals of
>>    object-oriented development is software reuse, which if it is really
>>    successful will have more of us writing and using libraries and writing
>>    less application-specific code.
>>
>> While C++ should promote code reuse and will broaden the market for
>> tools and libraries,  there will aways be many more application developers
>> than library vendors, because otherwise the tool vendors will not be able
>> to make a living!
>
>I didn't say there would be more library vendors than application vendors.
>I said that the proportion of library vendors would grow.  I said this in
>response to a comment about the number of library vendors being such a
>small part of the market.  I think the concerns about libraries is real,
>and will only get worse as software reuse increases.  I was objecting to the
>dismissal of those concerns on the basis of "they're just a small part of
>the user community".

Hmmm...  Wouldn't new library vendors be writing new code?  How could
changes to existing code possibly affect them?  If they're writing new
code, they can follow new rules.

>Besides, it won't be the library vendors that suffer the most anyway; it
>will be the users of those libraries who have the headaches because the
>exception signatures have to match across libraries from different vendors!
>And that is likely to be nearly *all* of the user community of the future.

If one library vendor wants to use another library vendor's code, they will
have to deal with exception issues before shipping.  If they simply coexist,
all coordination happens in user code.

Anyway, I'd like to mention again the possible solution of having an
"UncheckedException" class which is implicitly part of every function's
exception signature.  Anyone wanting their exceptions to escape static
checking could simply throw an exception of a class derived from
UncheckedException.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: daniels@biles.com (Brad Daniels)
Date: Wed, 2 Mar 1994 20:49:01 GMT
Raw View
In article <2l2m2d$qbv@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
>In article <CM0CHG.Jn0@biles.com>, daniels@biles.com (Brad Daniels) writes:
>> In article <2kvvdh$46q@travis.csd.harris.com>,
>> Bill Leonard <bill@ssd.csd.harris.com> wrote:
>> >In a previous post, I suggested that the static-checker advocates should
>> >just put "throw()" on all *their* function signatures and then pay
>> >attention to any compiler warnings that result.  Why isn't that just as
>> >viable an option?
>>
>> Because saying throw() may be false, while saying throw(...) is always
>> true.  If my compiler enforces no signature meaning "throws nothing"
>> uniformly, I can depend on a function with no signature throwing no
>> exceptions.  If the compiler doesn't enforce that rule, I have to assume
>> that any function with no signature might throw any exception, and that
>> adding throw() to it may lead to false assumptions.
>
>I don't see how it can lead to false assumptions if the compiler statically
>checks all the signatures.  In that world, adding throw() to a function that
>actually throws an exception (or calls something that does) will generate an
>error.  Isn't that exactly the purpose of static checking?

I thought we were talking about diddling headers in the case where you don't
have access to the code.  If you have access to the code, either solution
is viable.

>So, I don't buy your argument.  You want to force all of us to do work
>(namely, to put throw signatures on every function *AND* maintain them),
>and you reap all the benefit.  Meanwhile, I've made what I think is a
>suggestion for a reasonable alternative, which you reject because it's too
>much work for you.  Too bad.

I'm talking about features which make it more natural to write safer code.
To respond to your later objections, yes, I know no language feature can
make code completely safe.  Language features can, however, eliminate the
opportunity for some kinds of errors.  I view static exception signature
checking as analagous to static parameter type checking.  Exceptions
should be a part of the language, not a wart on it.  If exception signatures
are to exist at all, they should be fully integrated.

I'll mention one more time, just in case anybody missed it, that I see a
possible solution in the form of having an unchecked exception class be
implicitly added to every exception signature.  Anyone wanting to throw
unchecked exceptions could then throw an exception which inherits from
the unchecked exception class.  This would allow those who don't care to
keep a tight rein on exceptions to throw unchecked exceptions, while those
who want to be able to tell at a glance which functions throw what exceptions
will be able to use exceptions which are statically checked.  The main problem
I see with this approach is that it requires the addition of a built-in
class which can be used as a base class.  I don't think there's currently
any language feature with a similar requirement.

Anyway, just because I'm overly tenacious, I'll respond to the rest of
your post:

...
>First, you've missed a vital point in my argument.  Right now my code
>doesn't use exceptions at all.  I think it would be more robust if it did.
>But if I have to maintain exception signatures on my code right from the
>very start, I'll never get the opportunity to add exceptions to it, so it
>will remain in its current state.  How did that improve my software quality?

I didn't miss that point at all.  I thought I'd refuted it earlier, but you
disagree.  Not much to be done there.

>So far the main counter to this point I've heard is that I should put
>"throw(...)" all *every* function.  Well, I can't.  I don't own the include
>files for many functions I use, so I can't modify them.  Therefore, I'd be
>stuck with static checking even though I don't want it.

It's just that adding "throw (...)" in the presence of static checking with
a default of "throws nothing" can be fully automated, while adding "throw ()"
in the presence of a default of "throws anything" can't be fully autometed.
You pointed the same thing out yourself earlier in your message.  You remember,
right before you said you didn't buy my argument?

>Second, this whole safety argument is pretty spurious, in my opinion.  Static
>checking of exceptions is not going to make anybody's software fail-safe.
>No feature will.  Period.  Whether it makes the software less prone to failure
>is an open question, since there is no empirical evidence one way or another.

If it eliminates a possible class of errors, it makes code safer.  That's
about the best you can say about any language feature.

>I don't believe the world can be made risk-free.  The fact of the matter
>is, computers fail a lot less often than humans, even with the current
>state of the art in software development.  I don't fear a software failure
>when I go into a hospital or fly in an airplane -- if I fear anything, it
>is that some human idiot will make a mistake and fly the airplane into a
>mountain or confuse me with another patient and remove the wrong body
>part. :-)

People write software.  Computers compile software.  Since computers fail a
lot less often than people, doesn't it make sense for the computer to detect
the errors people make while writing software?

>On top of that, if the software does fail, I'll bet it will be caused by
>some programmer who got an if-test backwards or a similar logic mistake
>that no amount of compiler checking will ever find.  In other words, I'm
>certain that it will be a long time before unexpected exceptions is the
>cause of a statistically large proportion of software bugs.

I bet we could make that "long time" into "never" with proper static
checking of exceptions.  If the compiler yells at you when you try
to throw the wrong kind of exception, you'll never get the chance to put
that kind of bug in your program.

>Third, I don't need to be educated as to the costs and ramifications of
>software quality.  But in the real world, customers seldom make purchases
>based on quality alone.  It is often the case that a significant delay in
>releasing a software product will lose sales to the competition, even
>though the competition's product may be lower in quality.  One reason for
>this is that we have no way to objectively measure quality, and customers
>seldom realize they didn't get it until it is way too late.  Furthermore,
>customers demand features; I don't win sales by having high quality if I
>don't have the features when the competition does.

I agree with most of your points on this one.  I just think they're
not as relevant to the whole exception signature issue as you seem
to think.

>> The basic problem I have is that the "throws anything" default means that
>> any program which requires knowledge of the types of exceptions it receives
>> will have to either place signatures on all functions or include lots of
>> extraneous try blocks.
>
>So it's okay for you to tell me I have to add signatures to all my
>functions, but you're not willing to do it yourself?  How come it's okay
>for me but not for you?

Because I want to improve the safety of everybody's code, while you want
to avoid some work now, even though avoiding the work now may end up being
at least as expensive in the long run.  Static checking makes deciding what
you can throw a no-brainer in most cases, and therefore makes such errors
very tough to introduce.  Anything else makes unexpected exception errors
much more likely.

>> When adding new features to a language, I favor
>> making it viable over the long term rather than compromising for short-term
>> considerations.  In general, I think people should have to work to make
>> their programs less safe rather than working to make their programs more
>> safe.
>
>And I think features should only be added to languages if they benefit,
>and will be used by, at least a large minority of the users, and then only
>if the costs to those who don't use it are not unreasonable.  IMHO, static
>checking of exceptions IN THE PRESENCE of "no signature means throws nothing"
>does not meet that test.

If you bundle exceptions and exception signatures as a single feature set,
it does meet that test.  If you don't use exceptions, it doesn't matter
what the defaults are.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: pascual@gonzo.tid.es (Pascual Juan)
Date: Wed, 2 Mar 1994 20:54:46 GMT
Raw View
I want to collect all the proposals made about compile-time checking of
exception signatures made by:

 Brad Daniels (daniels@biles.com)
 Christopher Barber (cbarber@bbn.com)
 Jerry Schwarz (?)
 Tucker Taft (stt@spock.camb.inmet.com)

and me, who joins them with some addings.

 Pascual Juan (pascual@gonzo.tid.es)   <- Not an ANSI member.

In Short:
=========

    1.- There is no unexpected exceptions.

    2.- If a function wants to throw (directly or indirectly) an exception
 that doesn't match with the ones declared in its exception signature
 (the same or derived) then a compilation error is generated.

    3.- No exception signature means "throw xmsg" instead "throws unexpected"
 (xmsg is the base class of standard exceptions propossed).

    4.- Pointers to function in general (member, non-member, static) will
 add new features in order to declare it with the exceptions thrown.

    5.- There is no function overloading with different exception signature,
 just like different return value in oveloaded funtion declarations.

    6.- Type-Safe-Linkage adds the exceptions throwed by a function in the
 name of the symbols generated. There will be linking incompatibilities
 between symbols with and without exceptions, because functions
 that can't throw exceptions are not part of the language.

Explanation:
============

    1.- No unexpected
    -----------------
 Unexpected weakens C++ language. It could make ANSI C++ a good
 aproximation to Smalltalk and its "method not found". As we
 can see, this is incompatible with safe software.

    2.- Compile-time checking
    -------------------------
 A function can throw an exception directly using the reserved word
 "throw", and indirectly if it calls other functions that throws
 exceptions directly or indirectly. Compile-time checking is
 obvious with direct exceptions, and indirect exceptions can be
 checked if their respective signatures has been checked.

 Direct and indirect exceptions has to be declared at scope of the
 function we are dealing, in order to know their inheritance. The
 following code:

  class xBase { ... }; // Declaration of
                       // exception base.

  class xDerived : public xBase { ... }; // Declaration of a
                                         // derived exception.

  void f(int i) throw xBase
  {
    ...
    throw xDerived(data);
    ...
  }

 can't be checked if compiler haven't both class declarations, same as
 return value of functions:

  class Base { ... }; // Declaration of
                      // return value base.

  class Derived : public Base { ... }; // Declaration of a
                                        // derived return value.

  Base & f(int i)
  {
    ...
    return some_derived; // decladed as:
  }                      //   Derived some_derived(data);

 It is easy to build an indirect exception example like previous one.

    3.- No signature means "throw xmsg"
    -----------------------------------
 This could make existing code compatible just by recompiling, because
 when you get a failure in the default "new" operator or a division
 by zero, REALLY EXCEPTIONALS exceptions will be thrown. This ones
 can be even catched by safe functions or its caller, and warned with
 a compiler option.

 Look out! Declaring a function with an exception signature breaks
 this rule, and strict compile-time checking works on:

  void f_1() // No signature means "throw xmsg"
  {
    ...
    char * p = new char[81]; // Can throw "xalloc", a derived
    ...                      // class of xmsg
                } // Compiles fine.

  void f_2() // No signature means "throw xmsg"
  {
    ...
    function_with_signature(); // Can throw "Z", that is not a
    ...                        // derived class of xmsg.
                  ...                        // No "catch (Z & z)" is done
                } // Compiler error!!!

  void f_3() throw Y // Signature means "throw Y",
  {                  // not "throw Y, xmsg".
    ...
    char * p = new char[81]; // Can throw "xalloc", a derived
    ...                      // class of xmsg
                } // Compiler error!!!

 In this way, new code will be safer, and old code can still work.

 I doubt if no signature has to mean "throw xmsg" or "throw xruntime".
 Acording to standard library proposals, there will be a standard
 exception hierarchy:

   class xmsg { ... };
   class xruntime : public xmsg { ... };
   class xalloc : public xruntime { ... };

 As I am not a committee member, and I have no more information than
 their names, I don't know if xmsg will be enought to handle its
 derived through virtual functions. However, xruntime has a beautifull
 name to be the default exception.

    4.- New features in pointers to function
    ----------------------------------------
 Pointers to function will have to be declared with exception
 signature:

  void (*fp)() throw Z = function_with_signature; // See before.

 And pointers declared without signature will mean "throw xmsg":

  void (*fp)() = f_1; // See before.

 In this way, new code will be safer, and old code can still work.

    5.- No signature overloading
    ----------------------------
 Compilers will have a behaviour similar to overloading return values:

  int    f();
  char * f();

  main() { f(); } // Compiler Error: Which f()?

 You can't distinguish them, same as exceptions:

  void f() throw Y;
  void f() throw Z;

  main() { f(); } // Compiler Error: Which f()?

    6.- Type-Safe-Linkage
    ---------------------
 Caller functions won't link with a function who throws exceptions
 who doesn't match with the ones declared in the signature.
 Thus, next functions generates new symbols:

  void f(int) throw Z;         // Becomes f__Fi_1Z
  void * operator new(size_t); // Becomes __nw6size_t_4xmsg

 This will make a "brand new" symbol for operator new, and existing
 libraries won't link with this "brand new" new. Providers can
 supply the ANSI C++ libC.a and the old libOldC.a without exceptions
 during a transition lap. When you link with a non-ANSI lib you
 know what you risk at, and you will get exactly the old behaviour.

 Functions who doesn't throw exceptions (C libraries) has to be
 declared out of the language with the directive: extern "C".
 If you have a "pre-ANSI C++" library, you have to use a new
 directive: extern "C++". Its functions will have Type_Safe_Linkage
 but they won't assume 'no signature means "throw xmsg"' but
 'no signature means "throw nothing"'. It makes a bridge to
 existing libraries with only 3 lines of code added:

  extern "C++"                  // One
  {                             // Two
  #include "my_pre_ansi_lib.h"
  }                             // Three

END of Proposal
===============

I just wanted to be constructive.
--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-314-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: matt@physics2.berkeley.edu (Matt Austern)
Date: 02 Mar 1994 21:34:17 GMT
Raw View
In article <1994Mar2.205446.16481@tid.es> pascual@gonzo.tid.es (Pascual Juan) writes:

>     4.- Pointers to function in general (member, non-member, static) will
>  add new features in order to declare it with the exceptions thrown.

Function pointers aren't the only time when code is being called
indirectly.  Two other cases:
 (1) If you're using a virtual function, then you don't know,
     at compile time, which function is being called.
 (2) If you're using a template, then you don't know at compile
     time what types you're dealing with, so you don't know what
     functions are being called.  The only possible exception
     signature for Vector<T>::Vector(int Length), for example,
     is throw anything, because you don't know what exceptions
     might be thrown by T's constructor.

Compile-time exception specification checking would require very
severe restrictions on the use of templates, function pointers, and
virtual functions.  I don't think it's worth it.

Andrew Koenig, one of the authors of the original exception proposal,
has said that he didn't want exception specifications to be part of
the language at all.  I think we ought to take his opinion seriously:
he has a good deal more experience with exceptions than most of us.
And, as I use exceptions more and more, I have a good deal more
sympathy for his point of view.  Rather than wishing for stricter
enforcement of exception specifications, I think I'm just going to
use them quite sparingly.
--
Matthew Austern                       Never express yourself more clearly
matt@physics.berkeley.edu             than you think.    ---N. Bohr




Author: fenster@age.cs.columbia.edu (Sam Fenster)
Date: 02 Mar 1994 23:19:43 GMT
Raw View
Matt said:
> Compile-time exception specification checking would require very
> severe restrictions on the use of templates, function pointers, and
> virtual functions.  I don't think it's worth it.

I don't see the problem.

 - Exception signatures should clearly be part of a function's type.

 - The compiler would require the exception specification of a
redefined virtual function to be compatible with the one in its base class.
The same as for its (covariant) return type.

 - Template classes would be checked for exception legality at
instantiation time.  The same as member access, etc.  Templates that are to be
usable for arbitrary parameters would (obviously) either have members
declaring throw(...) or that wrap T.f() in catch(...).

"Severe restrictions?" I think not.  Worth it?  Definitely.  It makes
exception signatures type-safe and orthogonal to the rest of C++.




Author: whipp@roborough.gpsemi.com (David Whipp)
Date: Thu, 3 Mar 1994 08:16:44 GMT
Raw View
What I dislike about rigid exception signature checking is the need to write
the exception signature in many functions (all the way up the calling tree)
when most functions don't want to know - Its almost as bad as having to pass
a status flag all the way up.  Extra typing (not much - I know) for no great
benifit seems undesirable to me.  If I consider a C-style structure chart for
a program then I just want to draw exception arrows going up the tree to the
handler, missing out the functions in between.  C++ is, of course, object
oriented but the concept of structure still holds.

I am favourable to the idea of static checking in only the case of a function
with an exception signature calling a function with its own exception
signature. It may seem like a bit of a kludge, but to me it appears a lot
safer than having compiler options which change the semanics of a program
(which changing the default for no signature would do).  It would then be a
natural extension to have a compiler option to provide full exception
signature coverage (as warnings).  I personally treat warnings things to be
removed, and never to be ignored except during development stages where
writing a proper test harness just might not be worth while.


Let me add an idea of my own.  It is likely that members of a given object
may throw similar exceptions.  Why not give objects exception signatures.
These would be implicitly added to all members of that object.  Similarly,
an object should have exception catch methods which will attempt to catch
exceptions before they are transmuted to 'unexpected.

This would be especially useful in the case of a system with multiple active
objects.  It is quite concievable (at least in my system models) for an
object to create an exception without being called by the main application
(for example, consider an interrupt service routine which throws an
exception). giving objects catch methods would aid in the cleaning up of such
situations.

[I haven't read the proposals - I apologise if these ideas are already there]


--
---  +--  | | |   David Whipp.                    <whipp@roborough.gpsemi.com>
|  | |  | | | |   ------------------------------------------------------------
|  | +--  | | |   Due to transcription  and  transmission  errors,  the  views
---  |     | |    expressed  here  may  not  reflect  even  my  own  opinions!




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: Thu, 3 Mar 1994 17:34:39 GMT
Raw View
In article <CLzsJ9.MvI@inmet.camb.inmet.com> stt@spock.camb.inmet.com (Tucker Taft) writes:

> Actually, the best and most clever proposal I have heard here
> is the proposal by Jerry Schwarz, where a "throw" signature indicates
> that the specified exceptions should be considered alternate
> returns relative to this function.  Any caller of this function
> that also has a "throw" signature must appropriately deal with
> these "alternate return" exceptions, either by handling them or
> mentioning them in its own "throw" signature.  This would be
> a compile-time check.

That's hardly original -- indeed that is exactly what CLU does.

That approach has what I think are several severe disadvantages in
practice, the most severe being that adding an exception to the
list of those thrown becomes an incompatible change, even if the exception
is not actually thrown.

In other words, it rules out the following technique:

 1. Release n throws some set of exceptions.

 2. Release n+1 says it might throw some additional exceptions,
    but doesn't.

 3. Release n+2 does actually throw those exceptions.

Deferring checking until execution time gives users a one-release
transition period that they don't have otherwise.


Now of course, C++ implementors, as a service to their users, might
wish to have their compilers generate warnings when a function says it
might throw an exception that isn't caught.  That seems like a good
idea to me.
--
    --Andrew Koenig
      ark@research.att.com




Author: cbarber@bbn.com (Christopher Barber)
Date: 3 Mar 94 17:06:52
Raw View
In article <CLzpC1.Bwy@tempel.research.att.com>
ark@tempel.research.att.com (Andrew Koenig) writes:

   In article <CBARBER.94Feb28122640@apricot.bbn.com>
   cbarber@bbn.com (Christopher Barber) writes:
   > If you allow overloading on exception signature...

   Overloading on exception signature?  You mean that you want it to be
   possible to change the behavior of a function arbitrarily depending
   on whether or not a particular exception is caught?  Yecch!

   If nothing else, making that work would require attaching catch
   clauses not only to every function call but also to every block,
   as far as I can see.

You are right, this was a silly suggestion.  I don't know what got
into me :-)

   > Even if overloading were not allowed, I don't see this as any different
   > from the problem created when a library vendor wants to change the
   > signature of a function in some other way.  In fact, your same line of
   > reasoning could just as well be used to argue against function prototypes
   > in C!

   No, it's more like the following situation:  suppose the `open' function
   were changed so that if you give it an extra magic cookie as part of
   its file name argument (that is, as part of the string itself), it
   will open a file on another machine.  If that happens, it opens up the
   possibility of whole new classes of error, because the machine could
   go down while the file is being read.

   I am arguing that this new behavior should not impose any obligations
   on programs that do not take advantage of it.

I see your point.  But the problem is that a program might not be aware
that it is using a new feature that it should be.  In your example of a new
version of open(), one might have written a program that takes a filename
argument as input, opens the file, and does something.  Now if the user
provides one of these new filenames, and a failure occurs the program will
not be equipped to deal with a new errno value, and the writer of the
program will not even know that he needed to do anything differently.  Many
consider this a fair compromise, others do not.

   > Another approach would be to derive Networkerror from IOerror.  Provided
   > that the exception objects have been well-designed by the library vendor,
   > this should be a perfectly acceptable mechanism.

   Agreed.  But in the real world, Networkerror may well be defined by the
   provider of the networking library and IOerror by the provider of the
   I/O library.

Blech.  If I were the designer of the I/O library, I would want to hide my
dependence upon other libraries and would probably turn the external
Networkerror exception into IOerror or an IOerror subclass specific to my
own library.  Otherwise, you force your users to read two sets of
documentation instead of one in order to figure out how to use your
library.  The only time I would want to simply pass on exceptions from
a library not under my control would be if it were a standardized library
of some sort (either part of the C++ standard or some other such standard
C++ interface to something, such as CORBA).

   More generally, development techniques that work just fine when one
   party controls the whole program may fall apart when the program is
   pieced together from several sources -- and it is the latter usage that
   is truly the important one to support.

Agreed, but I am not sure that you have convincingly shown that
static checking is a hinderance to this sort of development.

   > I think that software developers will be *extremely* unhappy if a
   > supposedly backward compatible library change ends up causing
   > unanticipated runtime errors in their applications, especially when
   > they are discovered first by clients!  I would much rather wait to
   > receive a totally compatible set of libraries, down to the exception
   > signature, and *know* what exceptions can occur at runtime than risk
   > having an upgrade ruin my reputation with my clients.

   As I said before, I am sympathetic to that view.

   What I have no sympathy for is the viewpoint that that is the *only*
   legitimate opinion and that therefore it is acceptable to force all users
   to go along with it, whether they agree with it or not.

I hope that no one assumes that because I argue for a particular point of
view, that I don't respect the opinions of others.  The fact is that
adopting any kind of standard requires "forcing" some users to go along
with the standard.  Without compromise there could be no standard at all.
To be honest, I don't actually care all that much if static checking is
left out of the standard.  I just think that it would be nice if it were
in there.

   >    It was scenes like this that argued convincingly that checking
   >    exception specifications must not be done at compile time.  In
   >    effect, it would mean that the exceptions thrown by a function must
   >    never change once that function has been defined.

   > Just like the arguments of a function may never change once the
   > function has been defined?  IMHO, the ability to subclass exception
   > objects provides sufficient flexibility to overcome this restriction.

   And in my opinion it doesn't.  Perhaps you might try writing some sample
   code in a static-checking world and see what it feels like.  Then you
   might change your mind.

Perhaps I would, but it would take an awful lot of coding to come to a
conclusion one way or another based on experience.  Besides which,
compilers which support any kind of exceptions are just now becoming
available, and it will probably be a while longer before static checkers are
available, so it will be a while before anyone gets real experience with
this in C++, although there might be sufficient experience in other
languages with exceptions with static checking (are there any?).

In the end, as long as there are some vendors that provide static-checking
tools, everyone will probably be relatively happy....

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: djones@megatest.com (Dave Jones)
Date: Fri, 4 Mar 1994 01:19:49 GMT
Raw View


Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 4 Mar 1994 09:25:54 GMT
Raw View
Lots of people seem to be arguing that strictly enforced exception
signatures are important and do not interfere with development and
maintenance.  I'm curious: who has actually done a non-trivial project
in a language that enforces exception signatures?

What I do know is that I and others have built large software systems
in languages that do not enforce exception signatures.  Exceptions in
those languages have been very useful.  The lack of static checking has
been found to work well from the point of view of development and
maintenance and doesn't seem to have caused difficulties for building
reliable software.  In standardizing C++, why not try to stick with
technology of which we know that it works and is useful?

    Thomas.




Author: pascual@gonzo.tid.es (Pascual Juan)
Date: Fri, 4 Mar 1994 10:15:58 GMT
Raw View
In article <CM3nHs.n7@tempel.research.att.com>, ark@tempel.research.att.com (Andrew Koenig) writes:
|> In article <CLzsJ9.MvI@inmet.camb.inmet.com> stt@spock.camb.inmet.com (Tucker Taft) writes:
|> > ... specified exceptions should be considered alternate
|> > returns relative to this function.  Any caller of this function
|> > that also has a "throw" signature must appropriately deal with
|> > these "alternate return" exceptions, either by handling them or
|> > mentioning them in its own "throw" signature.  This would be
|> > a compile-time check.

Jerry Schwarz's proposal is the main part in my collection of proposals.

|> That's hardly original -- indeed that is exactly what CLU does.

We do't want it to be original, but safe.

|> That approach has what I think are several severe disadvantages in
|> practice, the most severe being that adding an exception to the
|> list of those thrown becomes an incompatible change, even if the exception
|> is not actually thrown.
|> ...

Excuse me, but you was told (not by me) that it's the same incompatibility
you get when member functions changes. It can be avoided using OOP:

-------------------------------------------------------------------------------
 // Verion 1.0

 class ReturnValue { ... };

 ReturnValue& f()
 {
   ...
   return some_return_value; // Declared as:
 }                           //   ReturnValue some_return_value;
-------------------------------------------------------------------------------
 // Version 2.0 INCOMPATIBLE!!

 class AnotherReturnValue { ... };

 AnotherReturnValue& f()
 {
   ...
   return some_return_value; // Declared as:
 }                           //   AnotherReturnValue some_return_value;
-------------------------------------------------------------------------------
        // Version 2.0 Upgrading capabilities in a compatible way.

 class ReturnValue { ... };
        class AnotherReturnValue: public ReturnValue { ... }; // Derived.

        ReturnValue& f()
        {
          ...
          return some_return_value; // Declared as:
        }                           //   AnotherReturnValue some_return_value;
-------------------------------------------------------------------------------

You can upgrade exceptions in the same way. Just change:
 void f() throw(ReturnValue)
 ...
   throw AnotherReturnValue;
in spite of:
 ReturnValue& f()
 ...
   return some_return_value;

It's easy, it's safe, it's orthogonal with existing language.

|> Now of course, C++ implementors, as a service to their users, might
|> wish to have their compilers generate warnings when a function says it
|> might throw an exception that isn't caught.  That seems like a good
|> idea to me.
|> --
|>     --Andrew Koenig
|>       ark@research.att.com

It coul be useless if the language has holes like linking symbols.
Please, read our proposals. I'm sure you'll like it

--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: Fri, 4 Mar 1994 14:25:39 GMT
Raw View
In article <1994Mar2.173048.20330@datascope.com> sfc@datascope.com (Steve F. Cipolli (P/M)) writes:

> I think this concept of "alternate return" vs. "unanticipated situations" can
> be easily muddled.  Maybe you could provide a clearer definition.

Fair enough.

This example is unrealistic in one tiny respect: a desire for backward
compatibility makes it unlikely that << will throw exceptions any time soon.

However, suppose it did.  That is, suppose that ostream::operator<<(const char*)
were capable of throwing any of a variety of exceptions because of various
error situations it might encounter.

Then what can we say about the following program?

 main()
 {
  cout << "Hello world" << endl;
 }

When writing this, I do not expect it to throw an exception.  Indeed,
it will do so only in exceptional circumstances.  If it does, I am
content to have my program terminate with an appropriate diagnostic
message.

However, if << has an exception specification
  and the language requires static checking
  and the default is that functions throw no exceptions,

then this program will fail to compile.  Whether you believe that's a good
thing or not, it certainly is not an acceptable change from current practice.

That means we have to change one of the last two things above, since
we are assuming for the sake of argument that << has an exception
specification.

Well, if you're in favor of static checking, your remaining choice is
to say that functions throw all exceptions by default.  But *that* means
that every time you wish to add an exception specification to a function,
you must also deal with every function that it calls, either by adding
an exception specification to it or by wrapping it in a try-catch block.
In effect, the use of exceptions becomes mandatory in every part of
your program.  I don't think that's an acceptable change from current
practice either.
--
    --Andrew Koenig
      ark@research.att.com




Author: daniels@biles.com (Brad Daniels)
Date: Fri, 4 Mar 1994 14:28:39 GMT
Raw View
In article <CM3nHs.n7@tempel.research.att.com>,
Andrew Koenig <ark@tempel.research.att.com> wrote:
...
>Deferring checking until execution time gives users a one-release
>transition period that they don't have otherwise.

This assertion assumes that the new exception will be thrown sometime
during testing on that one release.  If the exception is never thrown,
the programmer will never be notified that it needs to be caught, resulting
in a potential time bomb shipping to customers.

Did you ever review the possibility of having an unchecked exception class
which is implicitly part of every exception signature in order to allow
programmers to create exceptions which explicitly opt out of a static
checking mechanism?  The more I think about such an approach, the more
viable it seems to me.  If you rejected such an approach, why did you do
so?

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: bs@alice.att.com (Bjarne Stroustrup)
Date: 4 Mar 94 14:34:59 GMT
Raw View

bobkf@news.delphi.com (BOBKF@DELPHI.COM @ Delphi Internet Services Corporation) writes

 > ark@tempel.research.att.com (Andrew Koenig) writes:
 >
 > >I'm afraid not.  The first version of the exception proposal that
 > >Bjarne and I wrote did not have exception specifications.  We added
 > >them after several committee members indicated they would argue strongly
 > >against the proposal without them and would extend their own implementations
 > >to include them.
 >
 > Yes, the political process. It is obvious from this thread, though, that
 > the specification advocates would have driven much further had you and
 > Bjarne given further ground. Turning exception specification into a type
 > specification with static checking seems to be the true goal of this camp.

Actually, we were listening to experienced programmers. My recollection is
that exception specifications came in without any consideration of voting in
the standards committee.

Just to add gasoline to the fire: I am a strong supporter of static type checking,
yet I would prefer no exception specifications to statically checked exception
specifications. I still consider dynamically checked exception specifications
preferable to either extreme. Time will tell.

 - Bjarne




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sat, 5 Mar 1994 15:17:10 GMT
Raw View
ark@tempel.research.att.com (Andrew Koenig) writes:

>Suppose you're using a library from vendor X that in turn uses a library from
>vendor Y.  The Y library has I/O routines, which may potentially throw
>I/O error exceptions.  Suppose, for example, that the Y library contains
>
> extern void write (Device, Buffer) throw (IOerror);
>
>Now suppose that vendor Y adds networking capabilities to the Y library.
>That means that if you apply `write' to a network device, it might now
>raise a network error

So what's wrong with throwing Network_IOerror, which is derived from
IOerror?

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: stt@spock.camb.inmet.com (Tucker Taft)
Date: Mon, 28 Feb 1994 20:34:08 GMT
Raw View
In article <2kt2g1$eh2@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
> . . .
>One of the big advantages I see with C++ exceptions over Ada-like
>exceptions is that they can pass information along.

BTW, Ada 9X allows information to be passed along with exceptions,
in the form of an "exception occurrence" object.

> . . .So the low-level
>routine can generate an exception and pass along enough information to
>generate an intelligible error message at a high level (where the knowledge
>of how to present an error message resides).  ...

Exactly.

>Bill Leonard
>Harris Computer Systems Division
>Fort Lauderdale, FL  33309
>bill@ssd.csd.harris.com

S. Tucker Taft   stt@inmet.com
Intermetrics, Inc.
Cambridge, MA  02138




Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 28 Feb 1994 22:32:19 GMT
Raw View
In article <CBARBER.94Feb28122640@apricot.bbn.com> cbarber@bbn.com (Christopher Barber) writes:
|Truly unanticipated situations are not really capable of being handled
|through anything except some sort of 'abort' exception.  If you can
|anticipate it, then you should be able to include the exception in the
|signature as part of the function's type.

Truly unanticipated situations can be, and should be, handled
routinely in software that is supposed to be robust and fault
tolerant:

 try {
  result = complicated_evolving_function();
 }
 catch(...) {
  result = pedestrian_backup_solution();
 }

     Thomas.




Author: cbarber@bbn.com (Christopher Barber)
Date: 28 Feb 94 15:20:12
Raw View
In article <2kt444$eh2@travis.csd.harris.com>
bill@amber.csd.harris.com (Bill Leonard) writes:

   In article <CLsyqF.JI5@biles.com>, daniels@biles.com (Brad Daniels) writes:
   > People keep harping on what a pain it might be to add exceptions if
   > the kind of static checking I've discussed were to be added.  The point
   > of this whole discussion, though, is that any other kind of checking is
   > *dangerous*.

   I think that is scare-mongering rather than a thoughtful argument.  How can
   using exceptions without checking be more dangerous than not using
   exceptions at all?  Besides, dangerous is a relative term and far too
   strong to be applied to most software.  If my software fails, no one is
   going to be injured or killed, so dangerous hardly seems applicable.

Adding exceptions provides a way to circumvent the normal function
calling sequence.  Without exceptions, those interested in multi-level
error handling will return status codes and use function parameters
to provide error context.  Given that model, you know that either a
function will return or the program will crash with some catastrophic
error (abort(), seg. faults, etc.).  When you add exceptions, without
static checking it is possible to call a function and have it throw an
exception that you didn't know you were supposed to catch, thus turning
a possibly managable condition into a probably fatal 'unexpected'
exception; not a good thing if you want to be sure of the reliability
of your software.

As to the consequences of software failures, you are probably correct that
most software failures do not result in life-threatening situations.
Nevertheless, are you suggesting that anyone who wants to write software
for navigation, medical, weapons control, command and control, air traffic
control, and the like must abandon hope of being able to use C++?
Furthermore, unnecessary software failures can and do result in the loss of
millions of dollars, can lose you the respect of your clients.  You might
not consider this "dangerous", but I certainly do!

   Remember, we're talking about a standard that *ALL* software will have to
   live with.

No.  Just all C++ software!  Those that don't get what they want with C++
will have to use other languages.  IMHO, when you get down to it, doing static
checking is only going to hurt library and compiler vendors who represent
a very small portion of the C++ programming population....

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Mon, 28 Feb 94 17:15:24 PST
Raw View
In article <JSS.94Feb21180431@summit.lucid.com>, jss@summit.lucid.com (Jerry Schwarz) writes:
|> In article <2k370fINNt6d@early-bird.think.com> chase@Think.COM (David Chase) writes:
|>
|> chase:
|>    The abort-on-out-of-memory is a combination of two things -- I believe that
|>    there was some sort of ideological opposition to throwing an exception, ...
|>
|> The "ideological opposition" is exactly what I'm talking about.  If
|> exceptions are to be used as alternate returns, then running out of
|> memory isn't an appropriate use of the mechansim.

This, then, is a problem with using exceptions for alternate returns.
If doing so is going to make exceptions 'inappropriate' for the thing they
were designed to accomplish, then it is better not to use them so.

|>    Non-fatal emergencies (file not found, disk is full, disk is offline,
|>    permission denied) should be declared in the signature, as well as
|>    non-local transfers of control.
|>
|> Whether I/O problems are fatal or not depends on what I was going
|> to do with the file.

In general that decision may be better left to higher levels of logic.
That is why you throw an exception for such things.  The basic rule should
be that whenever a low-level routine is unable to fulfill its contract
it throws an exception, and lets its caller decide what to do about it.

|> E.g.  Suppose I have a function
|>
|>         Code compile(Command c) throw(illformed) ;
|>
|> This compiles a "Command" and throws illformed if c isn't well formed.
|> Maybe this uses I/O (to hold an intermediate file) and maybe it doesn't.
|> In my opinion, whether it does or not shouldn't be part of its interface.
|>
|> So when it fails because the disk is full there is no way to report that
|> fact.

So, add 'internal_error' to the throw list, and convert disk full and
out of memory and the like into internal_error.

Or make out_of_memory and disk_full subclasses of internal_error, so
you do not have to throw away information and can still keep the interface
clean.

By the way, I consider an illformed input to be an 'emergency' condition
just as much as a full disk or insufficient memory.  It is certainly not
what I would consider a mere alternate return.




Author: simon@sco.COM (Simon Tooke)
Date: Tue, 01 Mar 1994 01:16:27 GMT
Raw View
In <2kt351INNi35@early-bird.think.com> chase@Think.COM (David Chase) writes:
...
>Still, I think all this chit-chat about backward compatibility is a
>little bit silly.  Last hunk of C++ I worked on (in the 100-200kloc
>range) didn't use virtual base classes, didn't use multiple
>inheritance, didn't use templates, didn't use exceptions, was
>conservative in its use of overloading and destructors, and I was the
>only person using virtual member functions, but the damn thing still
>broke with every new release (2.0 -> 2.1, 2.1 -> 3.0, cfront ->
>"other") of the language or different compiler.

Gee, sounds like the source to cfront.

(Ducks.... Just kidding!)

-simon




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Mon, 28 Feb 94 17:19:14 PST
Raw View
In article <CLsyqF.JI5@biles.com>, daniels@biles.com (Brad Daniels) writes:
|> In article <9402251715.AA22080@tdat.elsegundoca.ncr.com>,
|> Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
|> >What he is saying is that the types of errors he is trying to catch are
|> >those that are generated in low-level routines - like disk full - but
|> >that it is the high-level routines that know how to handle the problem.
|> >Without exceptions you have to use error returns that are propagated
|> >up through many levels.
|>
|> This seems again to be an argument not to use exception signatures at all.
|> Not using exception signatures is certainly an understandable approach,
|> and certainly the easiest and quickest one for retrofitting exceptions
|> into a large program.

Indeed, and in the case of the project I mentioned above, that is
ndeed how we are likely to go once we get a compiler that implements
exceptions.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Tue, 1 Mar 1994 02:08:39 GMT
Raw View
daniels@biles.com (Brad Daniels) writes:

>Andrew Koenig <ark@tempel.research.att.com> wrote:
>>[static checking of exception signatures is]
>>Easy in theory; insurmountably complicated in practice.
>>
>>There are three problems.
>>
>>First, the default for functions is that they potentially throw anything,
>>which means that any function with an exception
>>specification must wrap *all* other functions in try blocks.

No, only calls to functions with no exception specification would
need to be wrapped.  I don't see this as a problem.

>>Second, exception specifications are not part of a function's type,
>>which means that any call through a function pointer must also be wrapped
>>in a try block.
>
>This is definitely a problem.  I see this as more of a correctable deficiency
>in the type system in light of exceptions than as a problem with static
>checking.

Yes, this is easily correctable.  The ARM specifies that exception
signatures aren't part of a function's type because the ARM does not
adopt static checking.  The two are linked.  It would be foolish to
implement static checking without recording exception signatures as
part of a function's type.

>>Finally, things like overflow and division by zero result in undefined
>>behavior, which means that the implementation is permitted to throw
>>exceptions in such cases.  Since those exceptions are not formally part
>>of the language definition, it is inappropriate to have to cater to them.

So clearly static checking should not require that functions handle
these exceptions.  What's the problem?  Are you complaining that
this means that functions might throw exceptions that aren't in their
signature?  If so, then so what - they might also delete all your
files and blow up your monitor.  That's what undefined behaviour means:
all guarantees are void.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: mat@mole-end.matawan.nj.us
Date: Tue, 1 Mar 1994 10:00:08 GMT
Raw View
In article <1994Feb25.192710.18677@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:

> It seems to me that this whole discussion is less about static vs. dynamic
> checking than it is about that age old issue of compatibility vs. usability.
> Static type checking for exceptions is obviously the right choice.  ...

No, it's not _obviously_ so.  It may be so, but it's far from obvious to me,
since the basic model of an exception presumes an error propagating from
something that identifies it _past_ things that know not of it until it can
be caught by something prepared to deal with it.

 ...
> If the dynamic checking responders are still not satisfied with the arguments
> set forth by the static checkers, I would like to leave you with some food
> for thought from my domain.  The company I work for is a manufacturer of
> medical equipment.  When our system goes to unexpected(), not just the
> application dies!  ...

Yours is a very real dilemma.  Static checking is appropriate in your case,
and a good implementation might--perhaps ought to--warn about exceptions
than can occur (overflow?  divide check?) in a function whose exception
signature is simply  throw() .

> I wish the "compatibles" would stop dismissing the "usables" as simply
> purists, and stop introducing backward compatibility loop-holes which can
> undermine the integrity of the applications built with the C++ language.

I don't think that's the issue.  Look again at the functionality built into
the C++ exception model; I'm pretty sure it's there for a reason.
--
 (This man's opinions are his own.)
 From mole-end    Mark Terribile
 mat@mole-end.matawan.nj.us, Somewhere in Matawan, NJ
 (Training and consulting in C, C++, UNIX, etc.)




Author: girod@dshp01.trs.ntc.nokia.com (Marc Girod)
Date: 01 Mar 1994 12:51:33 GMT
Raw View
Are there people who disagree with these conclusions, which I draw
from the discussion I followed on this thread (I have one pointer that
dates back to September 92!)?

- run-time checking of exception type is worse than no checking at
  all;
- static checking does not make sense without exception specification
  being part of the signatures;
- this is unfortunately too big a change to be done in C++.

Out of which we can only say: nice try :-) too bad, let's remove the
exception specifications (which is better than discourage their use),
and hope that someone will use this bright idea again as the
cornerstone for a future language.

--
+-----------------------------------------------------------------------------+
| Marc Girod - Nokia Telecommunications       Phone: +358-0-511 7703          |
| TL4E - P.O. Box 12                            Fax: +358-0-511 7432          |
| SF-02611 Espoo 61 - Finland              Internet: marc.girod@ntc.nokia.com |
|    X.400: C=FI, A=Elisa, P=Nokia Telecom, UNIT=TRS, SUR=Girod, GIV=Marc     |
+-----------------------------------------------------------------------------+




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: Tue, 1 Mar 1994 14:24:01 GMT
Raw View
In article <CBARBER.94Feb28122640@apricot.bbn.com> cbarber@bbn.com (Christopher Barber) writes:
> In article <CLx3s3.HFL@tempel.research.att.com>
> ark@tempel.research.att.com (Andrew Koenig) writes:
>
>    The key to this issue, I think, is whether you wish to treat an exception
>    as an `alternate return,' in which case the exceptions possibly thrown
>    are quite reasonably part of a function type, or as a way to handle possibly
>    unanticipated situations, in which case they just don't fit into the
>    static type checking model.

> Truly unanticipated situations are not really capable of being handled
> through anything except some sort of 'abort' exception.  If you can
> anticipate it, then you should be able to include the exception in the
> signature as part of the function's type.

Of course.  But that doesn't gainsay my coment.

There are plenty of cases where one might expect an exception
might occur but not give a hoot about what kind it is.

The most obvious of these is the main loop of an editor- or
interpreter-like program that you would really not have terminate
abruptly in the face of exceptions generated anywhere inside it.
The loop is likely therefore to look something like this:

 while (!exiting) {
  try {
   docommand();
  } catch (...) {
  }
 }

Here we assume that the program has the convention that any exception
generated in its inner workings also generates an appropriate diagnostic
message, so the main loop doesn't have to.

These exceptions are in no sense unexpected, even though it is not
easy to know what all of them might be.

> If you allow overloading on exception signature then the library vendor
> can support both signatures, have the backwards compatible version of
> the function call the new one and intercept the new exception and either
> turn it into something appropriate (perhaps turn it into an IOerror or
> an abort, for example).  This is certainly no worse than having
> Networkerror turn into unexpected, is it?

Overloading on exception signature?  You mean that you want it to be
possible to change the behavior of a function arbitrarily depending
on whether or not a particular exception is caught?  Yecch!

If nothing else, making that work would require attaching catch
clauses not only to every function call but also to every block,
as far as I can see.

> Even if overloading were not allowed, I don't see this as any different
> from the problem created when a library vendor wants to change the
> signature of a function in some other way.  In fact, your same line of
> reasoning could just as well be used to argue against function prototypes
> in C!

No, it's more like the following situation:  suppose the `open' function
were changed so that if you give it an extra magic cookie as part of
its file name argument (that is, as part of the string itself), it
will open a file on another machine.  If that happens, it opens up the
possibility of whole new classes of error, because the machine could
go down while the file is being read.

I am arguing that this new behavior should not impose any obligations
on programs that do not take advantage of it.

> Another approach would be to derive Networkerror from IOerror.  Provided
> that the exception objects have been well-designed by the library vendor,
> this should be a perfectly acceptable mechanism.

Agreed.  But in the real world, Networkerror may well be defined by the
provider of the networking library and IOerror by the provider of the
I/O library.

More generally, development techniques that work just fine when one
party controls the whole program may fall apart when the program is
pieced together from several sources -- and it is the latter usage that
is truly the important one to support.

> Gee, looks kind of like the sequence you get when an OS vendor makes
> incompatible changes in the OS interface.  I think that this example
> is overly contrived for economic reasons:  most people are going to
> be extremely wary of using tools which rely overly on the tools of
> yet another party in any case, regardless of what happens with exceptions.

Yes, it sure does look like what happens when an OS vendor makes incompatible
changes in the interface.  It is precisely for that reason I am arguing
that the particular exceptions thrown should not be formally considered
part of the interface -- at least not in the same way as the argument types.

> I think that software developers will be *extremely* unhappy if a
> supposedly backward compatible library change ends up causing unanticipated
> runtime errors in their applications, especially when they are discovered
> first by clients!  I would much rather wait to receive a totally compatible
> set of libraries, down to the exception signature, and *know* what
> exceptions can occur at runtime than risk having an upgrade ruin my
> reputation with my clients.

As I said before, I am sympathetic to that view.

What I have no sympathy for is the viewpoint that that is the *only*
legitimate opinion and that therefore it is acceptable to force all users
to go along with it, whether they agree with it or not.

>    It was scenes like this that argued convincingly that checking exception
>    specifications must not be done at compile time.  In effect, it would
>    mean that the exceptions thrown by a function must never change once
>    that function has been defined.

> Just like the arguments of a function may never change once the function
> has been defined?  IMHO, the ability to subclass exception objects provides
> sufficient flexibility to overcome this restriction.

And in my opinion it doesn't.  Perhaps you might try writing some sample
code in a static-checking world and see what it feels like.  Then you
might change your mind.

> It seems to me, that your main argument with static checking of exception
> signatures is that it would create too much work for compiler vendors
> (because of the need to include exception signatures in a function's type)
> and would inconvenience library vendors (because of the restrictions placed
> on changing the exception signatures of functions in the API).

No, I'm not making those arguments at all.

My argument is that it is too inconvenient for the *user* to have to
know, whenever writing any expression, all the exceptions that expression
might ever throw.

I understand that some people might like to do things that way,
but I don't see a strong argument for requiring all users to do
so, all the time.
--
    --Andrew Koenig
      ark@research.att.com




Author: stt@spock.camb.inmet.com (Tucker Taft)
Date: Tue, 1 Mar 1994 15:33:08 GMT
Raw View
In article <CLvz86.CG8@tempel.research.att.com>,
Andrew Koenig <ark@tempel.research.att.com> wrote:

>In article <2kn3k0$gvp@news.delphi.com> bobkf@news.delphi.com (BOBKF@DELPHI.COM) writes:
>
>> >If it were up to me, I would abolish exception specifications altogether.
>
>> At last, a workable proposal!
>
>I'm afraid not.  The first version of the exception proposal that
>Bjarne and I wrote did not have exception specifications.  We added
>them after several committee members indicated they would argue strongly
>against the proposal without them and would extend their own implementations
>to include them.

Actually, the best and most clever proposal I have heard here
is the proposal by Jerry Schwarz, where a "throw" signature indicates
that the specified exceptions should be considered alternate
returns relative to this function.  Any caller of this function
that also has a "throw" signature must appropriately deal with
these "alternate return" exceptions, either by handling them or
mentioning them in its own "throw" signature.  This would be
a compile-time check.

This approach would allow full compile-time enforcement of throw signatures
when the programmer indicates their interest in such things by including
a throw signature in their prototypes.  In a function without a throw
signature, there would be no checking, either at compile-time or run-time.
It would allow additional exceptions to be brought into the checking
process incrementally, by simply adding them to the appropriate throw
signatures.

Exceptions not mentioned in a throw signature would represent
truly exceptional/emergency/internal-error situations.
These by their very nature cannot be easily anticipated, and to try
to do so would defeat an important "software fault tolearance" purpose
of exceptions, namely they signal when a component fails unexpectedly,
and hence its results cannot be trusted.

The run-time checking approach seems like a loser, as its primary
accomplishment is to "throw" away information, by converting a potentially
meaningful exception into a relatively less meaningful "unexpected" one,
while adding overhead.

>    --Andrew Koenig
>      ark@research.att.com

S. Tucker Taft
Intermetrics, Inc.
Cambridge, MA  02138




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 1 Mar 1994 17:41:31 GMT
Raw View
In article <1994Feb25.192710.18677@datascope.com>, sfc@datascope.com (Steve F. Cipolli (P/M)) writes:
> It seems to me that this whole discussion is less about static vs. dynamic
> checking than it is about that age old issue of compatibility vs. usability.
> Static type checking for exceptions is obviously the right choice.  Anyone
> who denies that denies C++.

Like most attempts to make things black-or-white, this one is false.
Things just aren't that well-defined.  I deny that static type checking is
obviously correct, but I believe C++ to be a good language.  Where does
that leave me?

> If the dynamic checking responders are still not satisfied with the
> arguments set forth by the static checkers, I would like to leave you
> with some food for thought from my domain.  The company I work for is a
> manufacturer of medical equipment.  When our system goes to unexpected(),
> not just the application dies!  Those who have their fingers on the
> keyboard ready to tell me how to change unexpected() to something which
> "recovers", should think about what recovery is possible when an unknown
> event occurs during a surgical procedure.

Well I don't understand what all the fuss is about.  Nothing is stopping
you from putting "throw()" on all your function signatures, and nothing is
stopping your compiler from giving warnings if you call a function whose
signature doesn't match the caller's.  In fact, the compiler can even offer
an option that makes those warnings into errors -- it just can't claim
standard conformance in the presence of that option.  (That's not unusual
in the compiler world.)

If your compiler doesn't produce the warnings, get another compiler.
Surely vendors who want to sell to the "life-critical" market will be
willing to add features designed to support it.

So, you get your "static checking" that you want, as long as you pay
attention to those warnings.  Meanwhile, those of us who aren't involved in
developing life-critical applications don't pay for it if we don't want it.

I think the burden of providing for "armor-plated" applications should be
at least partially on the developers of those applications.  Now the
static-checkers have said that I can get "unchecked" throw specifications
by adding "throw(...)"  everywhere.  I'll explain in another post why that
isn't always feasible.  But more importantly, why should I consider that a
solution to the problem, when asking them to add "throw()" everywhere is an
equally viable solution, and affects far fewer people?

> I wish the "compatibles" would stop dismissing the "usables" as simply purists,
> and stop introducing backward compatibility loop-holes which can undermine the
> integrity of the applications built with the C++ language.

In my opinion and experience, a language that tries to insure against all
possible failure cannot achieve mainstream support.  It is simply too
costly.  That's okay, but it unfortunately means that developers of
critical applications either have to use a different language or at least a
different dialect of that language.  One might lament that situation, but
lamentations are unlikely to change the outcome.

On the other hand, if those developers are willing to put forth some
effort, we can have a language that can *help* insure against failure,
provided you follow certain guidelines (like never ignore a compiler
warning, period).  I think that's a reasonable position to take.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 1 Mar 1994 17:52:49 GMT
Raw View
In article <CLy8tr.2nG@biles.com>, daniels@biles.com (Brad Daniels) writes:
> In article <2kt444$eh2@travis.csd.harris.com>,
> Bill Leonard <bill@ssd.csd.harris.com> wrote:
> >Hurrah!  You finally got it!
>
> Finally?  When did you jump into this discussion?  All I've ever said is
> that *if* we have exception signatures, they should not be enforced at
> run-time.  We're then left with either eliminating the things altogether,
> or enforcing them properly (i.e. statically.)  I've said exactly the same
> thing more times than I can count, but people seem to keep misinterpreting
> me.  I personally like the idea of having statically enforced signatures,
> but in many ways, I'd be just as happy with no signatures.  I just don't
> want to see unexpected() errors at run time because somebody put in an
> exception signature which became inaccurate at some later point.

Where we disagree, I think, is over what the default means.  I want the
default to mean "throws anything", whereas I think you want to mean "throws
nothing".

> >I don't get it.  If the default signature is changed to "nothing means
> >throws nothing", how can I ever use exceptions and not use signatures?
>
> Use "throw(...)" everywhere?  :-)

That looks like a throw signature to me. :-) And this is not often a viable
option.  I use include files that I don't "own", so I can't change them.
(Oh, sure, I can make a copy of them and edit the copy, but then I'd have
to do that everytime I get a new version.  Why should I go to so much
work?)

In a previous post, I suggested that the static-checker advocates should
just put "throw()" on all *their* function signatures and then pay
attention to any compiler warnings that result.  Why isn't that just as
viable an option?

> Seriously, though, what I'm hoping
> is that rigidly enforced exception signatures will become a widely available
> compile time option on most implementations of C++.

Nothing is preventing that, but I think you'll have more luck if you are
willing to take some of the burden on yourself, by putting empty throw
signatures on all your functions.  Since the ones who really *need* static
checking (i.e., the "life-critical" application developers) are a minority
of programmers, I think it reasonable that they should bear at least part
of the burden.

> THAT'S NOT WHAT I'VE BEEN SAYING.  What's dangerous is putting in
> exception signatures which will be checked at run-time.  Having no checking
> at all is not terribly dangerous, though it's not as safe as rigid static
> checking.  Have I made my stand clear yet?  Sheesh.

You made it before.  What *you* are missing is that the debate is mostly
over what the default should be.  If you were willing to drop that part of
your argument, I'd agree with you, because the "default means throws
nothing" is the part I object to.

> Oh, I see.  Those of us whose software may be used to control, say, an
> explosives factory, a nuclear power plant, or maybe some kind of life-saving
> medical equipment should live with something that makes their code unsafe
> so that those of you who write programs where a crash is just a minor
> inconvenience can avoid a little extra work to make your code work right.

No, I think you should have to do some work.  So far, all I've seen is that
*I* have to do all the work so *you* can have what you want.  That kind of
argument is not destined to get my agreement.

> If you think it's right for your program to blow up unexpectedly at run-time,
> well...  Just remind me not to stand near any equipment controlled by any
> software you wrote.

That'll be easy -- I don't write any such software. :-)

> The fact is, at this point, there is little chance of getting static checking
> into the first draft of the standard.  What I'm hoping is that the underlying
> support mechanisms needed for static checking will go into place so that we
> can get optional static checking from several compiler vendors.  I will
> never use the current run-time checking because it is so dangerous.  I would
> love to use static checking were it available.

Why isn't it available?  You can put "throw()" on all your functions.  Your
compiler can (and probably will) give warnings if you violate those
signatures.  You can pay attention to those warnings.  Where's the problem?

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 1 Mar 1994 17:58:49 GMT
Raw View
In article <CBARBER.94Feb28152012@apricot.bbn.com>, cbarber@bbn.com (Christopher Barber) writes:
> As to the consequences of software failures, you are probably correct that
> most software failures do not result in life-threatening situations.
> Nevertheless, are you suggesting that anyone who wants to write software
> for navigation, medical, weapons control, command and control, air traffic
> control, and the like must abandon hope of being able to use C++?

Well, that might depend on lots more than just whether exception signatures
are statically checked. :-) All I'm arguing is that those people should
have to do some work, and not put all the burden on those of us who *don't*
need that feature.

> Furthermore, unnecessary software failures can and do result in the loss of
> millions of dollars, can lose you the respect of your clients.  You might
> not consider this "dangerous", but I certainly do!

I don't consider it dangerous, but it is certainly a consideration.  But so
is being late with a software release that contains functionality needed by
my customer.

> No.  Just all C++ software!  Those that don't get what they want with C++
> will have to use other languages.  IMHO, when you get down to it, doing static
> checking is only going to hurt library and compiler vendors who represent
> a very small portion of the C++ programming population....

They may be a small portion now, but there are lots of people who hope that
C++ will help change that situation.  One of the goals of object-oriented
development is software reuse, which if it is really successful will have
more of us writing and using libraries and writing less
application-specific code.  I don't know if that success will come, but it
seems like a worthy goal.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 1 Mar 1994 18:10:54 GMT
Raw View
In article <CLyBqF.3vH@biles.com>, daniels@biles.com (Brad Daniels) writes:
> I didn't say that.  What I said is that problems which can be fixed up
> front using compiler-directed techniques should be fixed up front.  If the
> compiler can detect a bug, it should tell you.  If it tells you about a
> bug, you should fix it.  There's nothing "high-minded" or pure about that.
> I'm not expecting someone to seek out every bug before releasing code, but
> if the compiler can tell you about a bug you're introducing, it's plain
> stupidity to ignore it.

This statement, IMHO, marks the difference between a programmer and a software
engineer.  I've found lots of bugs in my code that I decided to *temporarily*
ignore.  Why?  Because the fix was complicated, the effect of the bug was
minor (some little-used feature wouldn't work in a rare situation, or the wrong
error message would get issued, etc.), and I needed to release the software
soon.  I think that is the right engineering decision in many cases.  All I
am arguing for is the option of making that decision based on my knowledge of
the software.

However, I don't have the option of fixing compile-time errors.  I *have*
to fix them.  And that's where I have a problem with statically-checked
exception signatures, especially when the default means "throws nothing".
As I said before, it's this default thing that I object to.

> So write a preprocessor to put "throw (...)" after every function declaration
> or definition.  You can then change over as needed.  Actually, I'd prefer
> to see this feature initially provided as a compiler option, but I've
> discussed the details of how I feel about that to death in other recent
> notes.  Actually, given GNU emacs and a few days to hack compiler output,
> I think it should be feasible to fully automate putting the correct exception
> signature on your functions, assuming the compiler gives sufficient info.

As I said in a previous post, this isn't viable with include files I get
from someone else.  Nor is it viable in an environment where several
applications-development groups share common include files.

> >Besides the issue of time, though, there is the issue of effect.  What bugs
> >were introduced by making all those changes?  When we are close to
> >releasing our product, we try not to make *ANY* change that is not
> >absolutely necessary.  If we make any kind of a major change, that
> >necessitates starting over completely with the testing cycle, which is
> >*very expensive*.
>
> You mean a change like adding a run-time checked exception signature in
> the middle of your code?  If it's statically checked, the odds of introducing
> a bug are substantially reduced.

But I have said time and again that I don't plan to use exception
signatures at all!  So I don't have the problem of run-time-checked
signatures.

> If the intervening exception signatures are correct, you can be
> completely certain at compile time that your catch-all handler correctly
> deals with all possible exceptions.  This is a very useful feature.
> Also, any discip- line which can be enforced by the compiler can be of
> substantial use in a large system where multiple programmers have the
> opportunity to introduce new and exciting bugs because of interaction
> among multiple subsystems.  If you're going to use exceptions in a large
> program, there's a whole lot to be said for static checking from the
> safety and maintainability standpoints.

And I might opt for that if I were starting a new application, from
scratch, with support for exceptions from the beginning.  But I may not
live long enough to have that opportunity. :-)

> In general, they needn't be involved apart from having the right exception
> signature.

Just like I don't need to be involved in my own funeral, apart from the
dying.  :-) Having the right exception signature is what we've been arguing
about!

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: daniels@biles.com (Brad Daniels)
Date: Tue, 1 Mar 1994 22:44:03 GMT
Raw View
In article <2kvvdh$46q@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
>In article <CLy8tr.2nG@biles.com>, daniels@biles.com (Brad Daniels) writes:
>> In article <2kt444$eh2@travis.csd.harris.com>,
>> Bill Leonard <bill@ssd.csd.harris.com> wrote:
>> >Hurrah!  You finally got it!
>>
>> Finally?  When did you jump into this discussion?  All I've ever said is
>> that *if* we have exception signatures, they should not be enforced at
>> run-time.  We're then left with either eliminating the things altogether,
>> or enforcing them properly (i.e. statically.)  I've said exactly the same
>> thing more times than I can count, but people seem to keep misinterpreting
>> me.  I personally like the idea of having statically enforced signatures,
>> but in many ways, I'd be just as happy with no signatures.  I just don't
>> want to see unexpected() errors at run time because somebody put in an
>> exception signature which became inaccurate at some later point.
>
>Where we disagree, I think, is over what the default means.  I want the
>default to mean "throws anything", whereas I think you want to mean "throws
>nothing".

If you're doing static checking, "throws nothing" makes the most sense.
If you're not, it doesn't.  Throwing nothing is the better solution for
new code, but it would inconvenience those who want to retrofit exceptions.

>> >I don't get it.  If the default signature is changed to "nothing means
>> >throws nothing", how can I ever use exceptions and not use signatures?
>>
>> Use "throw(...)" everywhere?  :-)
>
>That looks like a throw signature to me. :-) And this is not often a viable
>option.  I use include files that I don't "own", so I can't change them.
>(Oh, sure, I can make a copy of them and edit the copy, but then I'd have
>to do that everytime I get a new version.  Why should I go to so much
>work?)
>
>In a previous post, I suggested that the static-checker advocates should
>just put "throw()" on all *their* function signatures and then pay
>attention to any compiler warnings that result.  Why isn't that just as
>viable an option?

Because saying throw() may be false, while saying throw(...) is always
true.  If my compiler enforces no signature meaning "throws nothing"
uniformly, I can depend on a function with no signature throwing no
exceptions.  If the compiler doesn't enforce that rule, I have to assume
that any function with no signature might throw any exception, and that
adding throw() to it may lead to false assumptions.

>> Seriously, though, what I'm hoping
>> is that rigidly enforced exception signatures will become a widely available
>> compile time option on most implementations of C++.
>
>Nothing is preventing that, but I think you'll have more luck if you are
>willing to take some of the burden on yourself, by putting empty throw
>signatures on all your functions.  Since the ones who really *need* static
>checking (i.e., the "life-critical" application developers) are a minority
>of programmers, I think it reasonable that they should bear at least part
>of the burden.

Safer code is always better.  I write process control software.  If it
crashes, the results can be anything from simply annoying a machine operator
to blowing a large crater in the floor of an explosives factory.  Anyone
who writes code which may be used to control machinery will have similar
concerns to mine.  We all have real life-or-death concerns about software
quality.  There is also a much larger body of programmers who produce
applications for resale.  If your livelihood depends on customer perceptions
of your product, these code safety concerns should be no less real to you
than to those of us whose mistakes might result in direct physical harm.
If the word processor you write blows up on users, losing them valuable
time and data, they will not be likely to come to you next time they need
a word processor.  If your game program crashes just before your customer
reaches the final scene, they will badmouth your game publicly, losing you
sales.  The only people who can get away with having programs fail avoidably
are people who write free software or who have a captive customer base.

As far as getting the optional support, the problem is that adding the
support requires extending the type system, and vendors are understandbly
nervous about extending the type system.  Besides, I wouldn't use a
vendor's extended syntax in my program, though I might use extended
checking semantics where available.

...
>> Oh, I see.  Those of us whose software may be used to control, say, an
>> explosives factory, a nuclear power plant, or maybe some kind of life-saving
>> medical equipment should live with something that makes their code unsafe
>> so that those of you who write programs where a crash is just a minor
>> inconvenience can avoid a little extra work to make your code work right.
>
>No, I think you should have to do some work.  So far, all I've seen is that
>*I* have to do all the work so *you* can have what you want.  That kind of
>argument is not destined to get my agreement.

The basic problem I have is that the "throws anything" default means that
any program which requires knowledge of the types of exceptions it receives
will have to either place signatures on all functions or include lots of
extraneous try blocks.  When adding new features to a language, I favor
making it viable over the long term rather than compromising for short-term
considerations.  In general, I think people should have to work to make
their programs less safe rather than working to make their programs more
safe.

About the only good compromise I can come up with is to have a standard
class of exceptions which any function can throw.  I.e., having no exception
signature would be equivalent to "throw (UncheckedException)", and any
exception signature would implicitly include UncheckedException.  If you
wanted to add exceptions without adding exception signatures, all you'd have
to do is have them inherit from UncheckedException.  Would something like
that be reasonable from your standpoint?

>> If you think it's right for your program to blow up unexpectedly at run-time,
>> well...  Just remind me not to stand near any equipment controlled by any
>> software you wrote.
>
>That'll be easy -- I don't write any such software. :-)

What kind of software do you write, anyway?

>...

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: cbarber@bbn.com (Christopher Barber)
Date: 1 Mar 94 18:16:33
Raw View
In article <2kvvop$46q@travis.csd.harris.com>
bill@amber.csd.harris.com (Bill Leonard) writes:

   In article <CBARBER.94Feb28152012@apricot.bbn.com>,
        cbarber@bbn.com (Christopher Barber) writes:

   > As to the consequences of software failures, you are probably correct
   > that most software failures do not result in life-threatening
   > situations.  Nevertheless, are you suggesting that anyone who wants to
   > write software for navigation, medical, weapons control, command and
   > control, air traffic control, and the like must abandon hope of being
   > able to use C++?

   All I'm arguing is that those people should have to do some work, and
   not put all the burden on those of us who *don't* need that feature.

I am not really sure what this "extra" work is beside having to add
explicit exception signatures to functions, and that should only be
mildly painful when you first convert over your code.  My point was that
there are a lot more engineers out there who are concerned with reliability
than you make it seem.  If you ignore their needs, they may abandon the
language and make for a smaller market in which to sell your tools.

   > Furthermore, unnecessary software failures can and do result in the
   > loss of millions of dollars, can lose you the respect of your clients.
   > You might not consider this "dangerous", but I certainly do!

   I don't consider it dangerous, but it is certainly a consideration.  But so
   is being late with a software release that contains functionality needed by
   my customer.

Once you have taken the initial plunge and added the signatures, what is
going to cause further delays?  The fact is, you are probably already going
to have significant delays just from having to upgrade your compiler.

   > No.  Just all C++ software!  Those that don't get what they want with
   > C++ will have to use other languages.  IMHO, when you get down to it,
   > doing static checking is only going to hurt library and compiler
   > vendors who represent a very small portion of the C++ programming
   > population....

   They may be a small portion now, but there are lots of people who hope
   that C++ will help change that situation.  One of the goals of
   object-oriented development is software reuse, which if it is really
   successful will have more of us writing and using libraries and writing
   less application-specific code.

While C++ should promote code reuse and will broaden the market for
tools and libraries,  there will aways be many more application developers
than library vendors, because otherwise the tool vendors will not be able
to make a living!

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 2 Mar 1994 06:19:50 GMT
Raw View
mat@mole-end.matawan.nj.us writes:

>IMO, exception specifications are best reserved for `firewalls' around
>major functional or semantic subsystems.

Exception specifications are an all-or-nothing thing, IMHO.
If you want to use them in only a few `firewalls', you would
be just as well (if not better) served by just putting some
appropriate `catch' statements in the appropriate places,
and perhaps even explicitly calling `unexpected()' yourself.

If you're only going to use exception specifications in a few places,
what do they buy you?

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: chase@Think.COM (David Chase)
Date: 2 Mar 1994 16:05:28 GMT
Raw View
bill@amber.csd.harris.com (Bill Leonard) wrote:

> Well I don't understand what all the fuss is about.  Nothing is stopping
> you from putting "throw()" on all your function signatures, and nothing is
> stopping your compiler from giving warnings if you call a function whose
> signature doesn't match the caller's.

> So, you get your "static checking" that you want, as long as you pay
> attention to those warnings.  Meanwhile, those of us who aren't involved in
> developing life-critical applications don't pay for it if we don't want it.

This approach (and it is a plausible one, not unlike the treatment of
prototypes in Ansi-C) still requires that an exception signature be made
part of a pointer-to-function type.  It need not be part of the standard
that assignments to function pointers be checked, but obviously the compilers
that generate warnings for OOS exceptions will also generate warnings on
dubious assignments.  People writing life-critical stuff are assumed to take
compiler warnings very seriously.

This starts to not work so well when you consider the behavior of libraries
(though, if I were writing a life-critical system, I'd keep a handy collection of
ten-foot poles for use with any code that I purchased from anyone else, including
the compiler vendor).  There, qsort (in standard mode) might be friendly
enough to mention that it likes its comparison function to throw the exceptions
X, Y, or Z (this would just be advisory, in standard mode, right?) but qsort
itself could not also declare that it only threw X, Y, or Z (because in standard
mode, the comparison function exception list was only advisory, and thus could
not be relied on by qsort itself).

The problem of templates is more or less the clincher for me.  Probably the
best approach is to consider some of these "friendly" extensions (helpful
to some people, don't change the behavior of any existing programs) and quit
right there.  I think that templates are badly wedged in C++ (and they aren't
done much better in Modula-3, for reasons similar to the problems that they
cause here).  This will probably have to wait for another day and another
language (and no, I have don't think that any of the current acronyms with
O-for-Object in them will make any future transition any easier).

David Chase, speaking for myself
Thinking Machines Corp.




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 2 Mar 1994 17:30:48 GMT
Raw View
Andrew Koenig (ark@tempel.research.att.com) wrote:
: The key to this issue, I think, is whether you wish to treat an exception
: as an `alternate return,' in which case the exceptions possibly thrown
: are quite reasonably part of a function type, or as a way to handle possibly
: unanticipated situations, in which case they just don't fit into the
: static type checking model.

I think this concept of "alternate return" vs. "unanticipated situations" can
be easily muddled.  Maybe you could provide a clearer definition.

If in your example below the new write() throws the new exception, Networkerror,
the application (assuming it was not altered to recognize this exception)
will go to unexpected().  In my field that means something bad is about to
happen.  Also, the likelihood of this scenario happening is quite realistic,
since their is no way for the application writer to know that the new
network exception exists (The application vendor buys vendor X libraries,
which includes vendor Y's).  What will eventually happen is unexpected will
be called for most exceptions.  And since unexpected normally terminates, the
net effect will render exceptions useless.

: Theoretical arguments aren't very convincing, at least to me, so here
: is a pragmatic one.

: Suppose you're using a library from vendor X that in turn uses a library from
: vendor Y.  The Y library has I/O routines, which may potentially throw
: I/O error exceptions.  Suppose, for example, that the Y library contains

:  extern void write (Device, Buffer) throw (IOerror);

: Now suppose that vendor Y adds networking capabilities to the Y library.
: That means that if you apply `write' to a network device, it might now
: raise a network error:

:  extern void write (Device, Buffer) throw (Networkerror, IOerror);

: Now, this happens only if you're using a network device.  In that sense,
: it is strictly an upward compatible change.  Yet if exceptions are statically
: checked, there is no way to make this an upward compatible change to the
: Y library: adding Networkerror to the throw list will break the library
: from vendor X.

: That in turn means that you can't accept changes from vendors X and Y
: independently: you must first take changes from X (to accept the extra
: exception) and then from Y.  Indeed, Y cannot begin shipping a new version
: of the Y library until X has sent out new versions to all X's customers.

: In other words, static checking imposes the following required sequence:

:  Y decides to add a new exception.
:  Y tells all vendors whose libraries depend on Y that the
:   new exception is coming.
:  All the vendors change their libraries.
:  All the cusomters install the new versions.
:  Finally, Y can ship the new version.

: Obviously, if there are more dependencies, this sequence becomes even
: longer.  As it is, any customer of Y can block any new version of the
: Y library that throws any new exceptions.  Ick.

I understand this argument.  I truely appreciate the logistical problems
involved.  However I must point out that if vendor Y had changed the function
to
 extern void write(Device, Buffer, Length) throw(IOerror);

vendor X's library would be equally broken.

: It was scenes like this that argued convincingly that checking exception
: specifications must not be done at compile time.  In effect, it would mean
: that the exceptions thrown by a function must never change once that function
: has been defined.

In much the same way as the functions interface must not change.

Dynamic exception checking has the same pluses and minuses as it's dynamic type
checking cousin.  Its easy to use within a changing universe, at the cost of
doing the wrong thing without a warning.  I appreciate the benefit, but I
can not ease my conscience of what might happen in critical applications when
a truly unexpected exception occurs.  I believe the C++ committee must address
that concern with at least a mandate to insure static checking is included in
all compliant compilers (issuing warnings by default with options to force
them to errors).  This is one implementation issue the C++ committee IMO must
govern.  Without such a mandate, it could leave the implementors, the committee,
ANSI, and ISO vulnerable to litigation.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.





Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 2 Mar 1994 17:48:32 GMT
Raw View
Bill Leonard (bill@amber.csd.harris.com) wrote:
: In article <CLsyqF.JI5@biles.com>, daniels@biles.com (Brad Daniels) writes:

: > People keep harping on what a pain it might be to add exceptions if
: > the kind of static checking I've discussed were to be added.  The point
: > of this whole discussion, though, is that any other kind of checking is
: > *dangerous*.

: I think that is scare-mongering rather than a thoughtful argument.  How can
: using exceptions without checking be more dangerous than not using
: exceptions at all?  Besides, dangerous is a relative term and far too
: strong to be applied to most software.  If my software fails, no one is
: going to be injured or killed, so dangerous hardly seems applicable.

And I for one thank God for that!

So, once again let me sum up your argument. Your stuff (I use that term
loosely) is not dangerous, C++ is a general standard, therefore the language
standard should be written with you in mind, but not the safety of human
beings whose lives my depend on a critical application.

: Remember, we're talking about a standard that *ALL* software will have to
: live with.

This pretty much sums it up "ALL software", not just the non-critical ones.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.




Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 2 Mar 1994 12:52:03 -0500
Raw View
ark@tempel.research.att.com (Andrew Koenig) writes:

>In article <2kn3k0$gvp@news.delphi.com> bobkf@news.delphi.com (BOBKF@DELPHI.COM) writes:

>> >If it were up to me, I would abolish exception specifications altogether.

>> At last, a workable proposal!

>I'm afraid not.  The first version of the exception proposal that
>Bjarne and I wrote did not have exception specifications.  We added
>them after several committee members indicated they would argue strongly
>against the proposal without them and would extend their own implementations
>to include them.

Yes, the political process. It is obvious from this thread, though, that
the specification advocates would have driven much further had you and
Bjarne given further ground. Turning exception specification into a type
specification with static checking seems to be the true goal of this camp.
However politically correct this may be to acolytes of strong typing, it
would also be an experiment in language design with very little basis in
industrial experience. I would have preferred (like you) to leave the
specifications out until there was a consensus what they would be used
for, but the compromise you got is workable. Exception specifications as
types is a good topic for the next round of standardization.

Bob Foster
Object Factory




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 2 Mar 1994 18:31:41 GMT
Raw View
In article <CM0CHG.Jn0@biles.com>, daniels@biles.com (Brad Daniels) writes:
> In article <2kvvdh$46q@travis.csd.harris.com>,
> Bill Leonard <bill@ssd.csd.harris.com> wrote:
> >In a previous post, I suggested that the static-checker advocates should
> >just put "throw()" on all *their* function signatures and then pay
> >attention to any compiler warnings that result.  Why isn't that just as
> >viable an option?
>
> Because saying throw() may be false, while saying throw(...) is always
> true.  If my compiler enforces no signature meaning "throws nothing"
> uniformly, I can depend on a function with no signature throwing no
> exceptions.  If the compiler doesn't enforce that rule, I have to assume
> that any function with no signature might throw any exception, and that
> adding throw() to it may lead to false assumptions.

I don't see how it can lead to false assumptions if the compiler statically
checks all the signatures.  In that world, adding throw() to a function that
actually throws an exception (or calls something that does) will generate an
error.  Isn't that exactly the purpose of static checking?

So, I don't buy your argument.  You want to force all of us to do work
(namely, to put throw signatures on every function *AND* maintain them),
and you reap all the benefit.  Meanwhile, I've made what I think is a
suggestion for a reasonable alternative, which you reject because it's too
much work for you.  Too bad.

> Safer code is always better.  I write process control software.  If it
> crashes, the results can be anything from simply annoying a machine operator
> to blowing a large crater in the floor of an explosives factory.  Anyone
> who writes code which may be used to control machinery will have similar
> concerns to mine.  We all have real life-or-death concerns about software
> quality.  There is also a much larger body of programmers who produce
> applications for resale.  If your livelihood depends on customer perceptions
> of your product, these code safety concerns should be no less real to you
> than to those of us whose mistakes might result in direct physical harm.
> If the word processor you write blows up on users, losing them valuable
> time and data, they will not be likely to come to you next time they need
> a word processor.  If your game program crashes just before your customer
> reaches the final scene, they will badmouth your game publicly, losing you
> sales.  The only people who can get away with having programs fail avoidably
> are people who write free software or who have a captive customer base.

First, you've missed a vital point in my argument.  Right now my code
doesn't use exceptions at all.  I think it would be more robust if it did.
But if I have to maintain exception signatures on my code right from the
very start, I'll never get the opportunity to add exceptions to it, so it
will remain in its current state.  How did that improve my software quality?

So far the main counter to this point I've heard is that I should put
"throw(...)" all *every* function.  Well, I can't.  I don't own the include
files for many functions I use, so I can't modify them.  Therefore, I'd be
stuck with static checking even though I don't want it.

Second, this whole safety argument is pretty spurious, in my opinion.  Static
checking of exceptions is not going to make anybody's software fail-safe.
No feature will.  Period.  Whether it makes the software less prone to failure
is an open question, since there is no empirical evidence one way or another.

I don't believe the world can be made risk-free.  The fact of the matter
is, computers fail a lot less often than humans, even with the current
state of the art in software development.  I don't fear a software failure
when I go into a hospital or fly in an airplane -- if I fear anything, it
is that some human idiot will make a mistake and fly the airplane into a
mountain or confuse me with another patient and remove the wrong body
part. :-)

On top of that, if the software does fail, I'll bet it will be caused by
some programmer who got an if-test backwards or a similar logic mistake
that no amount of compiler checking will ever find.  In other words, I'm
certain that it will be a long time before unexpected exceptions is the
cause of a statistically large proportion of software bugs.

Third, I don't need to be educated as to the costs and ramifications of
software quality.  But in the real world, customers seldom make purchases
based on quality alone.  It is often the case that a significant delay in
releasing a software product will lose sales to the competition, even
though the competition's product may be lower in quality.  One reason for
this is that we have no way to objectively measure quality, and customers
seldom realize they didn't get it until it is way too late.  Furthermore,
customers demand features; I don't win sales by having high quality if I
don't have the features when the competition does.

> The basic problem I have is that the "throws anything" default means that
> any program which requires knowledge of the types of exceptions it receives
> will have to either place signatures on all functions or include lots of
> extraneous try blocks.

So it's okay for you to tell me I have to add signatures to all my
functions, but you're not willing to do it yourself?  How come it's okay
for me but not for you?

> When adding new features to a language, I favor
> making it viable over the long term rather than compromising for short-term
> considerations.  In general, I think people should have to work to make
> their programs less safe rather than working to make their programs more
> safe.

And I think features should only be added to languages if they benefit,
and will be used by, at least a large minority of the users, and then only
if the costs to those who don't use it are not unreasonable.  IMHO, static
checking of exceptions IN THE PRESENCE of "no signature means throws nothing"
does not meet that test.

Let me emphasize again that it is only this default that I object to.  If
you were only willing to take the burden yourself of putting exception
signatures on all your functions, we could agree.  I think I'm taking a
reasonable position; everybody gets what they really want, and those who
want the feature are the ones who pay for it.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: john_werner@taligent.com (John Werner)
Date: Wed, 2 Mar 1994 18:53:04 GMT
Raw View
In article <CM0CHG.Jn0@biles.com>, daniels@biles.com (Brad Daniels) wrote:

> Throwing nothing is the better solution for new code.

Not necessarily.  For those of us who think exception specifications are
basically worthless, a default of throwing anything is better, even for new
code.  Since we don't want to use this feature of the language, we don't
have to bother with the specifications that it requires.

> Safer code is always better.

I think everyone here agrees with this.  A lot of the argument is really
about whether exception specifications actually help make code safer.  I
don't think they do in most cases.  This is especially true with the
current design, where broken specifications lead to immediate run-time
aborts instead of the possibility of someone further up the call chain
handling the error.

There seem to be two major camps here:

- People who think exception specifications are useful and would like to
use them, but think that run-time checking negates most of the usefulness.
These people tend to favor a default of "throws nothing".

- People who just don't want to use the specifications at all, or only in
very limited circumstances.  These people tend to favor a default of
"throws anything".


> The basic problem I have is that the "throws anything" default means that
> any program which requires knowledge of the types of exceptions it receives
> will have to either place signatures on all functions or include lots of
> extraneous try blocks.

Both sides can make this argument:

"The basic problem I have is that the "throws nothing" default would mean
that any program which doesn't require knowledge of the types of exceptions
it receives will have to place signatures on all functions."


Since the proponents of exception specifications don't seem to like
run-time checking, and those who don't use specifications shouldn't care
when the checking gets done, maybe a compromise would be in order:

- Change the specification checking to run time instead of compile time.
This would probably require tighter wording in the standard about what
'new' and other such operations should do when they fail.  There would also
need to be some way to convert unexpected/illegal exceptions into known
exception classes, perhaps via the "unexpected" function.  This would also
require a lot more work from compiler vendors.  I don't know too much about
all the issues here; Is it even possible?

- Leave the default at "throws anything", to make the people who don't like
specifications happy.  If people are going to go to the effort of adding
specifications to all of their functions, adding "throw ()" shouldn't be
too much of an extra burden.

--
John Werner                           john_werner@taligent.com
Taligent, Inc.




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 2 Mar 1994 18:58:11 GMT
Raw View
In article <CBARBER.94Mar1181633@apricot.bbn.com>, cbarber@bbn.com (Christopher Barber) writes:
> I am not really sure what this "extra" work is beside having to add
> explicit exception signatures to functions, and that should only be
> mildly painful when you first convert over your code.

As I said before, the problem is that "default means throws nothing" will
be a problem when using libraries owned by someone else.  I don't have the
option of changing the header files once -- if I change them at all, it
will have to be a copy of the header files.  That work will have to be done
over and over, everytime those header files change.

Don't forget that one set of header files are the system header files,
which come with the OS.  So every new release of the OS would result in
another iteration of header-file changes.

> My point was that
> there are a lot more engineers out there who are concerned with reliability
> than you make it seem.  If you ignore their needs, they may abandon the
> language and make for a smaller market in which to sell your tools.

As I said in a previous post, I think this reliability issue is a red
herring.  Whether or not we have static checking of exceptions, software is
going to fail.  Static checking of exceptions isn't addressing even one of
the most common causes of software failures today.  When you've addressed
those causes, then maybe we can discuss reliability vis-a-vis exceptions.

> Once you have taken the initial plunge and added the signatures, what is
> going to cause further delays?

See above.

> The fact is, you are probably already going
> to have significant delays just from having to upgrade your compiler.

Yes, but I only have to do that once.

>    They may be a small portion now, but there are lots of people who hope
>    that C++ will help change that situation.  One of the goals of
>    object-oriented development is software reuse, which if it is really
>    successful will have more of us writing and using libraries and writing
>    less application-specific code.
>
> While C++ should promote code reuse and will broaden the market for
> tools and libraries,  there will aways be many more application developers
> than library vendors, because otherwise the tool vendors will not be able
> to make a living!

I didn't say there would be more library vendors than application vendors.
I said that the proportion of library vendors would grow.  I said this in
response to a comment about the number of library vendors being such a
small part of the market.  I think the concerns about libraries is real,
and will only get worse as software reuse increases.  I was objecting to the
dismissal of those concerns on the basis of "they're just a small part of
the user community".

Besides, it won't be the library vendors that suffer the most anyway; it
will be the users of those libraries who have the headaches because the
exception signatures have to match across libraries from different vendors!
And that is likely to be nearly *all* of the user community of the future.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: Wed, 2 Mar 1994 19:06:35 GMT
Raw View
In article <9406013.11557@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:

> No, only calls to functions with no exception specification would
> need to be wrapped.  I don't see this as a problem.

No, calls to every function without an explicit empty exception
specification would need to be wrapped.

Or do you think it's OK to propagate exceptions through { } that don't
happen to be function bodies?  What justification is there for treating
function bodies specially?

> Yes, this is easily correctable.  The ARM specifies that exception
> signatures aren't part of a function's type because the ARM does not
> adopt static checking.  The two are linked.  It would be foolish to
> implement static checking without recording exception signatures as
> part of a function's type.

On the other hand, the cost in complexity is considerable from the
user's viewpoint.  As I think I mentioned before, ML tried that route
and found that it didn't work in practice --- and ML has much tighter
type checking than C++ in all other respects.

> So clearly static checking should not require that functions handle
> these exceptions.  What's the problem?  Are you complaining that
> this means that functions might throw exceptions that aren't in their
> signature?  If so, then so what - they might also delete all your
> files and blow up your monitor.  That's what undefined behaviour means:
> all guarantees are void.

I am pointing out that if static checking would make it impossible for
an implementation to offer, as an extension, the ability for + to throw
an exception on overflot, because that ability would impose a requirement
to check for that exception.


Meta-remark:  Not everyone on the world programs in the same style.
One should be very careful when advocating language requirements
that rule out particular styles of programming.
--
    --Andrew Koenig
      ark@research.att.com




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 2 Mar 1994 19:09:32 GMT
Raw View
Dag Bruck (dag@control.lth.se) wrote:
: >>>>> On Fri, 25 Feb 1994 19:27:10 GMT, sfc@datascope.com (Steve F. Cipolli (P/M)) said:

: > This, like the overflow possibilities inherent in the ++ operator
: > for the new bool type leave serious questions in my mind as to the long term
: > viability of C++ in critical applications.

: You have mis-interpreted the ++ operator for bool.

: Operator ++ sets a bool variable to true.  It does not increment it.
: There is no restriction on the number of times you can use ++ on a
: bool.  It is equivalent to

:  b = true;

: This is indeed an example where the committee has gone the "purer" way
: instead of just pushing C compatibility to the bitter end.

Thank you... I apologize for my ignorance and thank the committee for stopping
the insanity.  This apology is also extended to Andrew Koenig, since we
debated this issue at length in the context of the bool posting a month or
so ago.

Stephen Cipolli
Datascope Corp.





Author: jss@summit.lucid.com (Jerry Schwarz)
Date: 22 Feb 1994 02:04:31 GMT
Raw View
In article <2k370fINNt6d@early-bird.think.com> chase@Think.COM (David Chase) writes:


chase:
   The abort-on-out-of-memory is a combination of two things -- I believe that
   there was some sort of ideological opposition to throwing an exception,
   plus the implementors (me among them) never quite got around to it, partly
   because we were still dickering about how some of these things ought to be
   dealt with.

The "ideological opposition" is exactly what I'm talking about.  If
exceptions are to be used as alternate returns, then running out of
memory isn't an appropriate use of the mechansim.

jss:
   |> No matter how "safely" you code, you always have the problem of
   |> having to deal with emergencies.  If I write

   |>         void f() throw(X) ;

   |> then the implied "contract" for f is that it will abort in case of
   |> emergency.

chase:
   [did you mean "really abort" or "throw X"?]

I meant "really abort". X is an alternate return.  In a complete
specification of f, I have to say when it will occur and what it
means.  If some unanticipated situation occurs (like out of memory, or
disk full) which the contract does not cover, then I have to really
abort.  Throwing X in an emergency would violate the contract.


   Non-fatal emergencies (file not found, disk is full, disk is offline,
   permission denied) should be declared in the signature, as well as
   non-local transfers of control.

Whether I/O problems are fatal or not depends on what I was going
to do with the file.  E.g.  Suppose I have a function

        Code compile(Command c) throw(illformed) ;

This compiles a "Command" and throws illformed if c isn't well formed.
Maybe this uses I/O (to hold an intermediate file) and maybe it doesn't.
In my opinion, whether it does or not shouldn't be part of its interface.

So when it fails because the disk is full there is no way to report that
fact.

jss:
   If I write

           void g(X&) ;

   then the contract is that it will throw an exception in case of
   emergency and report the "X" result in the argument.

   The difference is that for "f" I have to worry that I won't get
   back control.  While "g" promises always to give back control to
   the caller.  If I'm trying to write robust code it seems to me
   that "g" has the more sensible contract.  If I'm just trying to
   program without worrying about emergencies, then "f" may be
   easier to use.

chase:
   I think I hope that you have f and g backwards here -- from the point
   of view of style, g throwing an exception and reporting the result
   back in the argument is pretty ugly, and it is also a bit of a pain
   for compiler writers (basically, in a world where exceptions may be
   thrown at many places, one must be exceptionally careful about how
   accesses to global variables and reference parameters are scheduled
   and optimized).

I said about f and g exactly what I intended.  g is allowed to throw
an exception in case of an emergency, but f is not.  Using the above
example "Compile" cannot throw "illformed" in an emergency, and
(because of the throw clause) it can't throw anything else so in an
emergency it has to abort.

chase:
   Grumble.  It's one thing to leave features out because experience is lacking,
   and an entirely different matter to completely ignore existing experience and
   experiments.  Missing=nothing simplifies production of reliable code, and
   provides more opportunities for an optimizer, and makes it easier for
   maintainers to tell what is going on.  What could be wrong with that?


Grumble: I've been spending time responding precisely because I think
that your experience is relevant.  I like the M-3 exception system for
M-3.  It seems to make sense within the context of M-3.  I'm not
arguing that it should be changed to the C++ system.  But that doesn't
mean that I think the C++ system should be a copy of the M-3 system.

I think there are significant differences (both technical and
historical) between the languages that have lead to differences in the
exception mechanism.

I've been trying to explain why I think the C++ mechanism is
appropriate for C++.  Maybe I'm wrong, or maybe I'm not being clear,
but I resent the suggestion that I'm ignoring your experience when the
fact that I'm responding in what I believe to be a calm rational
fashion shows that I am paying attention.

  -- Jerry Schwarz(jss@lucid.com)













Author: ark@tempel.research.att.com (Andrew Koenig)
Date: Tue, 22 Feb 1994 15:17:55 GMT
Raw View
In article <CLI16L.CvD@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>  No. If a function declares it can throw
> A, B or C, then it can only call functions that throw
> A, B, C, classes derived from them, or, the calls
> must be wrapped in  try blocks that only throw A, B, or C,
> or classes derived from them.

Easy in theory; insurmountably complicated in practice.

There are three problems.

First, the default for functions is that they potentially throw anything,
which means that any function with an exception
specification must wrap *all* other functions in try blocks.

Second, exception specifications are not part of a function's type,
which means that any call through a function pointer must also be wrapped
in a try block.

Finally, things like overflow and division by zero result in undefined
behavior, which means that the implementation is permitted to throw
exceptions in such cases.  Since those exceptions are not formally part
of the language definition, it is inappropriate to have to cater to them.
--
    --Andrew Koenig
      ark@research.att.com




Author: chase@Think.COM (David Chase)
Date: 22 Feb 1994 16:14:50 GMT
Raw View
dag@control.lth.se (Dag Bruck) writes:
|> >>>>> On 18 Feb 1994 20:04:31 GMT, chase@Think.COM (David Chase) said:
|> > I don't think I've ever seen a decent justification for "missing signature"
|> > equals "throws anything".

|> The best argument, in my view, is that it makes the transition to
|> "exceptional" code easier because you don't have to re-write
|> everything (every function signature) at once.

I've heard this argument, but experience says it isn't that bad.
"Experience" is the Jordan/Glassman experiment with Modula-2+ and the Acorn
OS, the Modula-3 change-of-semantics (motivated in part by the result of
that experiment), porting the Acorn C-And-Modula(2+)-Execution-Library from
ARM to SunOS/Sun-3, observing the carnage in the include files (ifdef'd for
K&R/Ansi/Posix/C++) shipped by Sun, and writing a portion of a compiler
in C++ 2.0, 2.1, 3.0, etc. It can be done, and it isn't that bad.

Note that there are several knobs to turn:

1. are the standard libraries prototyped in a way that includes
   exceptions in all function declarations?

2. does the compiler assume missing=none or missing=any for exception
   specifications during semantic (warning/error message) analysis?

3. in the case of an out-of-signature exception thrown, is it a warning
   or an error message at compile time?

4. does the compiler assume missing=none or missing=any for exception
   specifications when it is generating calls to "unexpected" or building
   exception descriptors?

You can mix and match these knobs somewhat to ease the transition.
For instance, I think that the exceptions thrown by library routines
should be specified ASAP, and the include files should be fixed, NOW.
This is especially true for procedures with callbacks (such as qsort)
so that people can decide what is a legal type of function to pass in
(I don't think that "throw (...)" is necessary in this instance, because
inheritance, including multiple inheritance, can be used in the exception
type.)  I also think that the compilers should only warn when they
see out-of-signature exceptions, and not reject programs.

If these things happen, at some time in the future, code can transition from
default=any to default=none, with respect to run-time calls to unexpected, and
there should be relatively little pain.  Compilers can even encode, NOW, in
their exception tables, what it is that is going on, and this can be noted in a
debugger or profiler, and you can even experiment with the behavior of
code if you toggle a run-time switch from "any" to "none".

(I'm assuming a PC-range implementation of exception handling -- the
 ranges generated can distinguish between the cases of explicit-any,
 explicit-none, and no-throw-clause, at very little run-time cost, and
 the decision to call unexpected can be postponed until run-time.)

|> I agree completely, but I think your view is unrealistic.  I get the
|> impression that you speak from experience of a project where you had
|> almost full control of the source code, including libraries.

This is correct, to the extent that you can speak of "full control" for a
piece of code written in a mutating language.  I may also be assuming a
certain minimal level of competence on the part of the language/library
implementors -- that is, nobody said that this all had to be as easy
as falling off of a log.  If you look at the time spent making some of
these crappy languages go fast, I think you'd agree with me that the effort
required to transition the libraries is relatively low, and that the payoff
in terms of future code reliability/maintainability is relatively favorable.

David Chase, speaking for myself
Thinking Machines Corp.




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Tue, 22 Feb 94 11:04:59 PST
Raw View
In article <CLI16L.CvD@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller) writes:
|> In article <9402041709.AA10627@tdat.ElSegundoCA.NCR.COM> swf@tdat.ElSegundoCA.NCR.COM writes:
|>
|> >I do not think it is, in practice, *possible* to do it at compile
|> >time.
|>
|>  Its simple to do at compile time and involves no
|> great complexity. ...
|>
|>  No. If a function declares it can throw
|> A, B or C, then it can only call functions that throw
|> A, B, C, classes derived from them, or, the calls
|> must be wrapped in  try blocks that only throw A, B, or C,
|> or classes derived from them.

This brings up my second objection.

It is now too expensive to retrofit exceptions into existing code,
since that binary-only library that is used throughout which 'we` do
not have the budget to upgrade doesn't have exceptions signatures,
and cannot be called from any function that has an exception signature
other than "throw(...)" without adding several lines of code to each
to handle all uexpected exceptions.

[And that library *may* effectively throw exceptions if you set 'new'
to do so, or if it uses callback functions].

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: daniels@biles.com (Brad Daniels)
Date: Tue, 22 Feb 1994 22:02:32 GMT
Raw View
In article <CLMt5w.E59@tempel.research.att.com>,
Andrew Koenig <ark@tempel.research.att.com> wrote:
>In article <CLI16L.CvD@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>  No. If a function declares it can throw
>> A, B or C, then it can only call functions that throw
>> A, B, C, classes derived from them, or, the calls
>> must be wrapped in  try blocks that only throw A, B, or C,
>> or classes derived from them.
>
>Easy in theory; insurmountably complicated in practice.
>
>There are three problems.
>
>First, the default for functions is that they potentially throw anything,
>which means that any function with an exception
>specification must wrap *all* other functions in try blocks.

The current default is the wrong choice for static checking.  The default
for static checking should be that no signature means no exceptions are
thrown.  There has already been a bit of discussion in this thread of
why that is a better approach.

>Second, exception specifications are not part of a function's type,
>which means that any call through a function pointer must also be wrapped
>in a try block.

This is definitely a problem.  I see this as more of a correctable deficiency
in the type system in light of exceptions than as a problem with static
checking.

>Finally, things like overflow and division by zero result in undefined
>behavior, which means that the implementation is permitted to throw
>exceptions in such cases.  Since those exceptions are not formally part
>of the language definition, it is inappropriate to have to cater to them.

I'm not sure how much of a problem this is.  The "best" solution from the
standpoint of functionality would be to have such events throw known
exceptions.  Unfortunately, this behavior may not be implementable on
all architectures, and is therefore likely to be considered unsuitable
from a standards perspective.  Failing that, however, implementation
defined behavior (I assume that's what you meant, rather than undefined),
is, as the name implies, implementation defined.  An implementation is
free to throw exceptions outside a function's type signature if it
so desires in such cases.

Obviously, it's too late to get the more useful behavior into the first
draft or subsequent release of the standard.  I hope, though, that some
vendors will provide static checking as an option.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: cbarber@bbn.com (Christopher Barber)
Date: 22 Feb 94 14:05:37
Raw View
In article <2k0sf7INNdnr@early-bird.think.com>
barmar@think.com (Barry Margolin) writes:

   In article <CLC2Iy.M6F@biles.com> daniels@biles.com (Brad Daniels) writes:
   >That is a spurious argument.  Adding new kinds of exceptions to a function
   >constitutes an incompatible change in a library.  If your library calls a
   >function which suddenly and unexpectedly starts throwing exceptions, the
   >library is broken because something it uses has changed incompatibly.

   It seems that the dilemma X3J16 faced was that they wanted to make such an
   incompatible change to the standard C++ libraries.  If they also required
   throw signatures to be enforced at compile time, this would mean that just
   about everything would need to be recompiled when a vendor incorporated the
   exceptions into its library.

So what?  What is so horrible about recompilation?  Chances are, everything
will need to be recompiled in any case when the user gets a new copy of the
compiler.  I would much rather spend the extra time recompiling than have a
client call up because his program crashed due to an uncaught exception.

IMHO, exceptions without compile time checking are pretty wimpy.  However,
I figure that the better vendors out there will figure that out and will
offer compiler switches that provide it, and eventually it will make it
into the standard.  But it would be much nicer if it was part of the
standard now rather than in the next millenium :-)

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: cbarber@bbn.com (Christopher Barber)
Date: 22 Feb 94 14:34:52
Raw View
In article <DAG.94Feb19083805@bellman.control.lth.se>
dag@control.lth.se (Dag Bruck) writes:

   >>>>> On 18 Feb 1994 20:04:31 GMT, chase@Think.COM (David Chase) said:

   > I don't think I've ever seen a decent justification for "missing
   > signature" equals "throws anything".

   The best argument, in my view, is that it makes the transition to
   "exceptional" code easier because you don't have to re-write
   everything (every function signature) at once.

I am not sure that laziness on the part of library vendors is a good
excuse for omitting a language feature.  If you are already taking the
time to add exceptions to a function or method implementation, how hard
is it really to take the extra time to add the exceptions to the signature?

   > Because existing libraries don't throw exceptions, it is perfectly
   > reasonable to say that they don't throw anything, ....

   I don't think so.  Two examples:

           1.  Library functions may use call-backs to the application,
               "qsort" is a trivial example, container classes (e.g.,
               constructor) another.

But if the exception list were part of the function signature, then
one would not be able to pass a pointer to a sort function which raises
an exception.  Same goes with container classes, etc.

           2.  Library routines may run out of memory, which in C++
               is typically handled with an exception.

But doesn't really need to be.  The old convention of new() returning a 0
on failure worked fine.  In fact, you could provide two versions of new(),
one which has an out-of-memory exception in its signature and another one
which does not (yes, that would be overloading on exception signature!).
In any case, you could always take the compromise position that functions
may always throw a few built-in, and generally fatal, exceptions such as
out-of-memory, even if they are not in their signature.

   > .... but it is also relatively easy to update any code that calls
   > these libraries, guided by warning messages from a compiler, if it is
   > decided that those libraries should have signatures.

   I think you assume that (1) you have access to the source code of all
   libraries, and (2) that you have the man-power to convert the
   libraries and to maintain the conversion when the next release comes
   out.

Huh? Perhaps I misunderstood David's post, but I thought that he was
addressing the situation where one were to get a new copy of a library
which adds exceptions to its function signatures.  In such a case, you
would only need to change your own code to handle the exceptions and would
not need access to the library source code.  Where you would have a problem
would be if you had a library dependent upon another library which changes.
While this is a possible situation, I would not get caught dead using a
non-self-contained library to which I did not have the source code.  IMHO,
If a library vendor cannot guarantee that its libraries will work when
installed, they have no business being in the marketplace.

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: chase@Think.COM (David Chase)
Date: 23 Feb 1994 17:46:59 GMT
Raw View
jss@summit.lucid.com (Jerry Schwarz) writes:

|> Grumble: I've been spending time responding precisely because I think
|> that your experience is relevant.  I like the M-3 exception system for
|> M-3.  It seems to make sense within the context of M-3.  I'm not
|> arguing that it should be changed to the C++ system.  But that doesn't
|> mean that I think the C++ system should be a copy of the M-3 system.
...
|> I've been trying to explain why I think the C++ mechanism is
|> appropriate for C++.  Maybe I'm wrong, or maybe I'm not being clear,
|> but I resent the suggestion that I'm ignoring your experience when the
|> fact that I'm responding in what I believe to be a calm rational
|> fashion shows that I am paying attention.

Okey-dokey, I'll try again.  I think that it would be a serious mistake
for C++ to use the rule "default == throws anything", and I don't think
that the reasons are Modula-centric, and if anything, I think that this
rule is easier to tolerate in C++ than it is in Modula-3.  (A longwinded
justification follows, with some editorial remarks at the end.)

First, if you read the M-3 report, there's no real prescription as to when
to use exceptions or not, but they do describe exceptions in a way that
helps give a feel for when they are appropriate.  Since there is no
other way defined to report emergencies, exceptions are used to report
emergencies.  Since there is no other way to get non-local goto (no
longjmp, and a not-very-flexible loop exit statement) exceptions are
sometimes used for this purpose as well.  One does not exclude the other.

HOWEVER, it is suggested (section 3, Statements) that an implementor should
be willing to spend 10000 instructions in the exceptional case to save one
instruction in the non-exceptional case.  This is hardly an encouragement to
use exception-handling for alternate returns.  It happens that there is
also a fairly trivial optimization when the "throw" and "catch" appear
in the same body of code that can turn this into a goto, so this penalty
does not apply to exceptions used for multi-level loop exit, and so some
people might take this as an encouragement to use exceptions for non-local
goto (but the report does not discuss this optimization).  It also happens
that RETURN and EXIT are formally defined in terms of "special exceptions"
for purposes of giving them the correct interaction with TRY-FINALLY, but
this also happens to give them a really Martian interaction with TRY-
EXCEPT-ELSE -- this is probably a bit confusing if you are trying to figure
out how the committee intended you to use exceptions, but my conclusion
is that TRY-EXCEPT-ELSE is a really dangerous thing to do, since it
not only catches all "exceptions" (including the failure/abort exception
that is intended to terminate a program), it also changes the expected
behavior of EXIT and RETURN (hysterical note -- the Olivetti M-3 compiler
code these wrong because I didn't realize that this was the case while
writing the code generator).

Don't make the mistake of lumping the "failure exception" in with all the
other emergencies.  "Failure exception" is a formal abort, so that a program
can interact properly even with that.  Note that, because Modula-3 is a safe
language, "failure" usually means some sort of programmer error, and not a
damaged abstract machine.  In C and C++ I think the distinction is less
clear.

Running out of memory in Modula-3 (one example of an emergency) is a
real grey area, because the abstract machine is more or less "blocked", even
though it is in a consistent state.  The only other "emergency" that is
similar to this one is one in which the time to finish computing is
suddenly bounded, either because of CPU limit, or power failure.  Again,
the abstract machine is in a consistent state, but resources are sharply
limited.

Any lesser emergency (for instance, subscript error, failed NARROW,
dereference of NIL) allows you to continue computing from the exception
handler, and (if the M-3 committee had the sense to think up standard names
for these "checked runtime errors") can be caught and dealt with ("dealt
with" is usually "clean up and exit", but the gangrenous limb may also be
amputated).  These exceptions are alluded to in the M-3 report.

So, repeating myself somewhat, there are four basic categories of
"emergency" (as opposed to alternate return, or minor error):

1.  damaged abstract machine (uncorrectable memory error, machine on fire)
2.  stuck abstract machine (no more memory, no more time)
3.  programmer error (subscript range, failed NARROW, out-of-sig exception)
4.  recoverable external error (disk full, disk offline, keyboard detached)

#3 & #4 make sense to map into exceptions.  #2 might make sense to map into
an exception, if you could figure out how to recover gracefully, #1 does
not, because there is guarantee that anything works anymore (different rules
apply in various situations -- an OS must attempt to deal even with #1)
Where C++ and Modula-3 differ is in the chances of encountering problems of
classes #1 and #2 -- M-3 is somewhat more able to provoke #2 errors for
mysterious reasons (why didn't GC reclaim some memory?), whereas C++ is
somewhat more able to provoke #1 errors for mysterious reasons (scribbling
on the stack, or run-time data structures).

It is also possible to make an exception part of an interface for alternate
returns, where these are expected to be either rare or cheap relative to the
accompanying computation, and where it "fits".  For example, I coded up a
simple Newton solver that would throw an exception if it did not converge in
the specified number of iterations.

I think it is beyond dispute that exceptions are used, and are intended
to be used, to report "emergencies" in Modula-3.  There are some unusual
cases that don't follow this rule, but they correspond to "can't continue
at all".

>         Code compile(Command c) throw(illformed) ;

> This compiles a "Command" and throws illformed if c isn't well formed.
> Maybe this uses I/O (to hold an intermediate file) and maybe it doesn't.
> In my opinion, whether it does or not shouldn't be part of its interface.

> So when it fails because the disk is full there is no way to report that
> fact.

One Modula-centric (is this really Modula-centric?  I think that would
be sad if it were true) answer to this is:

if "use of disk I/O" is not part of the interface, then that exception
should be caught internally and converted to an exception that is more
appropriate to the interface.  A function with an empty exception signature
(with the exception of the implicit "failure/abort" exception) is a function
that *cannot fail*.  The exception signature (in code that is intended
to be robust) should enumerate all possible non-normal outcomes.  (Ask
yourself -- how else would you write robust code?  If you've figured
out all the outcomes, what's so hard about writing them down?  If you
have not figured out all the outcomes, what definition of "robust" are
you using? Note that, if all interfaces are written in this way already,
it is much easier to write a new interface this way, than if you are
starting from scratch.)

Another "Modula-centric" rule is that "errors should be reported someplace
that they can be corrected".  Naive application of this rule would have
all sorts of exceptions in every signature for potential forwarding to
the user (disk offline, disk full, wrong floppy in drive, etc) but in
practice these should probably all be lumped into a single "user
intervention required" exception.

*RIGHT HERE* is where C++ beats Modula-3 in a big way -- C++ can have
inheritance among exception types, but Modula-3 does not.  C++ could have a
base class "user intervention required" from which various actual exceptions
were derived, whereas Modula-3 could only make that distinction in the data
that is attached to an exception.  This lets you avoid the use of a kitchen
sink exception all over your code, and lets you extend interfaces in
unforeseen ways (is user intervention really required, all the time? Maybe
not -- perhaps another disk can be used when one is full, perhaps some
temporary files can be deleted).  I haven't seen anything forbidding the use
of multiple inheritance for exception types, either.  A corollary of this is
that where the interface/exception rules should compel you (in M-3) to catch
an exception and re-raise it as a new exception appropriate to the
interface, in C++ this could all happen automatically by careful choice of
subtyping.

Now, as to why it is better to have the default be "raises none", I think
there are two questions:

1. is it better, starting from scratch?

   Here I think the answer is a definite "yes" -- it automates part of the
   job of producing reliable code, and the exception signature portion of
   function declarations and types is helpful in maintaining code over the
   long haul.

2. is it better, starting from where we are now?

   I think the answer here is also "yes" becayse I think there is a
   relatively painless migration.

   a. there is always an "abort" exception in every signature (this
      rule inherited from Modula-3).  This might be used over-much
      in the beginning, less over time.  Toy programs and examples
      make liberal (and implicit) use of this exception, so it is
      still easy to write "Hello World", though it may compile with
      a warning (what if the disk to which stdout is directed is full?)

   b. static analysis might warn about out-of-signature exceptions
      instead of rejecting programs outright (especially in transition).

   c. relatively little existing code throws exceptions, so (in the case
      of callbacks) this code trivially satisfies whatever signatures
      might be imposed on function parameters (that is, you can always
      pass a throws-S into a throws-T context if S is a subset of T,
      though the rule ought to be more sophisticated than that in the
      presence of subtyping -- every member of S ought to either appear
      in T or have a supertype in T).

The obvious transition step of adding warnings to the compiler and
signatures to libraries would result in a big pile of warnings, that
could be fixed fairly rapidly, ignored, or suppressed with a compiler
option.

Looking back at your post again, the only place where I see that our
assumptions diverge is the allowances made for "unanticipated" situations.
I think the M-3 style (for robust code) is that some attempt should be made
to deal with every possible outcome.  The major flaw that I see in doing
this is that this can be very expensive and time-consuming -- on the other
hand, I've found that I end up handling all the oddball cases sooner or
later, so I might as well do it sooner.  And, if you take that attitude, the
Modula-3 rule for exception signatures is a big help.  The major flaw in not
dealing with every possible outcome is that the resulting code is a little
flaky (mail programs that lose mail when a disk is full, window systems that
crash when you wait too long to "place" a window, OSes that freeze,
debuggers that say "line 32769" and show you "line 1", mail reading packages
that don't sync your outgoing mail file to disk, you get the picture).

On the other hand, why am I arguing with success?  (All the examples
above, are, of course, taken from recent "real life".)

David Chase, speaking for myself
Thinking Machines




Author: chase@Think.COM (David Chase)
Date: 23 Feb 1994 18:17:13 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:
|> >  No. If a function declares it can throw
|> > A, B or C, then it can only call functions that throw
|> > A, B, C, classes derived from them, or, the calls
|> > must be wrapped in  try blocks that only throw A, B, or C,
|> > or classes derived from them.

(I think JMS was working from the assumption that the default
 ought to be throws none.)

ark@tempel.research.att.com (Andrew Koenig) writes:

|> Easy in theory; insurmountably complicated in practice.

I think "insurmountably" stretches things a little bit.

|> There are three problems.

|> First, the default for functions is that they potentially throw anything,
|> which means that any function with an exception
|> specification must wrap *all* other functions in try blocks.

Change the default.  (In one fell swoop, all the existing code that
does not throw exceptions is correctly typed.)

|> Second, exception specifications are not part of a function's type,
|> which means that any call through a function pointer must also be wrapped
|> in a try block.

Add exception specifications to a function's type.

|> Finally, things like overflow and division by zero result in undefined
|> behavior, which means that the implementation is permitted to throw
|> exceptions in such cases.  Since those exceptions are not formally part
|> of the language definition, it is inappropriate to have to cater to them.

Define exceptions that are raised on overflow and division by zero, if
an implementation raises exceptions for those events.  Make the choice
to raise the exception or not implementation-defined.  Define pragmas that
will locally disable this checking.

David Chase, speaking for myself
Thinking Machines Corporation






Author: jss@summit.lucid.com (Jerry Schwarz)
Date: 24 Feb 1994 00:25:38 GMT
Raw View

chase:
   if "use of disk I/O" is not part of the interface, then that exception
   should be caught internally and converted to an exception that is more
   appropriate to the interface.  A function with an empty exception signature
   (with the exception of the implicit "failure/abort" exception) is a function
   that *cannot fail*.  The exception signature (in code that is intended
   to be robust) should enumerate all possible non-normal outcomes.  (Ask
   yourself -- how else would you write robust code?  If you've figured
   out all the outcomes, what's so hard about writing them down?  If you
   have not figured out all the outcomes, what definition of "robust" are
   you using? Note that, if all interfaces are written in this way already,
   it is much easier to write a new interface this way, than if you are
   starting from scratch.)

I think we're not so far apart.  We agree that there are certain
failure/abort exceptions that should be throwable from any function
regardless of its declaration.

When you say the default is "throw nothing" you mean "only throw
emergency exceptions," and when I say the default should be "throw
anything" I mean "only throw emergency exceptions".  Where we differ
is in what constitutes an emergency exception. You want to designate a
single one.  I'd like to allow more flexibility and would like the
exception handling mechanism to support the distinction.  From my
point of view, the weakness of the C++ mechanism is that it doesn't
give any way to distinguish between an "non-normal outcome" and an
"emergency".

chase:
   Now, as to why it is better to have the default be "raises none", I think
   there are two questions:
   ...

   2. is it better, starting from where we are now?

      I think the answer here is also "yes" becayse I think there is a
      relatively painless migration.

      a. there is always an "abort" exception in every signature (this
         rule inherited from Modula-3).  This might be used over-much
         in the beginning, less over time.  Toy programs and examples
         make liberal (and implicit) use of this exception, so it is
         still easy to write "Hello World", though it may compile with
         a warning (what if the disk to which stdout is directed is full?)

See what I mean.  "raises none" means "raises only abort".  I think a throw
clauses lists the "non-normal outcomes".  Anything else is an emergency
exception. I think it is unreasonable to attempt to list in advance the
emergency exceptions.

chase:
      b. static analysis might warn about out-of-signature exceptions
         instead of rejecting programs outright (especially in transition).

I think what should be warned about is any "non-normal outcome" that
can propogate into the "emergency exceptions".  That is, if a function
that doesn't list "X" in its throw clause calls a function that does,
and doesn't catch such an exception, then there should be a warning.

I think this leads to the same warnings as your proposal.  I would
be willing to call this an error (although the current working paper
doesn't).

chase:
      c. relatively little existing code throws exceptions, so (in the case
         of callbacks) this code trivially satisfies whatever signatures
         might be imposed on function parameters (that is, you can always
         pass a throws-S into a throws-T context if S is a subset of T,
         though the rule ought to be more sophisticated than that in the
         presence of subtyping -- every member of S ought to either appear
         in T or have a supertype in T).

Ditto. Except that code that does "new" is pervasive and (according
to the working paper) throws an exception.  You would presumably
want to make this the "abort" exception.  The library working group
is deriving all the exceptions thrown by the standard library, including
"out of memory" from a single base, so that would be ok under your
proposal.

The difference is that under my approach functions can start throwing
arbitrary exceptions for emergencies without changing any callers.
Under your approach I can only throw an "abort" without changing
callers.

chase:
   Looking back at your post again, the only place where I see that our
   assumptions diverge is the allowances made for "unanticipated" situations.

Exactly.  We may enough in agreement that there isn't much more
to say.

  -- Jerry Schwarz(jss@lucid.com)





Author: barmar@think.com (Barry Margolin)
Date: 24 Feb 1994 07:45:38 GMT
Raw View
In article <CLnBw8.55x@biles.com> daniels@biles.com (Brad Daniels) writes:
>In article <CLMt5w.E59@tempel.research.att.com>,
>Andrew Koenig <ark@tempel.research.att.com> wrote:
>>Finally, things like overflow and division by zero result in undefined
>>behavior, which means that the implementation is permitted to throw
>>exceptions in such cases.

>I'm not sure how much of a problem this is.  The "best" solution from the
>standpoint of functionality would be to have such events throw known
>exceptions.

In Common Lisp, our answer for this was to specify that *if* the
implementation detects certain errors in various built-in operators (such
as the arithmetic operators), then it will signal well-defined conditions.
If it doesn't detect the error, behavior is undefined; while this formally
means that it could signal any error, in practice it means that the program
is likely to misbehave more drastically (e.g. core dump, scribble on
memory).  There are also declarations (analogous to pragmas) that force
checking for certain errors.

--
Barry Margolin
System Manager, Thinking Machines Corp.

barmar@think.com          {uunet,harvard}!think!barmar




Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: Thu, 24 Feb 1994 11:25:15 GMT
Raw View
In article <JSS.94Feb23162538@summit.lucid.com> jss@summit.lucid.com (Jerry Schwarz) writes:
>chase:
>      c. relatively little existing code throws exceptions, so (in the case
>         of callbacks) this code trivially satisfies whatever signatures
>         might be imposed on function parameters (that is, you can always
>         pass a throws-S into a throws-T context if S is a subset of T,
>         though the rule ought to be more sophisticated than that in the
>         presence of subtyping -- every member of S ought to either appear
>         in T or have a supertype in T).
>
>Ditto. Except that code that does "new" is pervasive and (according
>to the working paper) throws an exception.  You would presumably
>want to make this the "abort" exception.  The library working group
>is deriving all the exceptions thrown by the standard library, including
>"out of memory" from a single base, so that would be ok under your
>proposal.
>
>The difference is that under my approach functions can start throwing
>arbitrary exceptions for emergencies without changing any callers.
>Under your approach I can only throw an "abort" without changing
>callers.
>

Am I misinterpreting something? Doesn't multiple inheritance buy you this?
This approach gives you a choice, if you want to throw a emergency exception,
then derive it from xabort (and possibly some other exception class, if this
is needed), otherwise, don't derive from Abort. In this way, xalloc could
be both derivec from xmsg (I forget the exact "Standard C++ derivation")
and from xabort.

As Mr Chase said, this change shoudn't break much code since not much code
is written using exceptions.

The only thing to think about, though, is that this approach means that each
function without a throw signature is implicitly wrapped with a "catch(xabort)"
clause, and I believe this adds overhead compared to the current "raises
anything" approach. I'm not a compiler-writer so I don't know if it's
significant in terms of space or optimization opportunities (remember,
inline functions without signatures would also implicitly throw only xabort).

>
>  -- Jerry Schwarz(jss@lucid.com)
>

Take care
    Robert

--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse               |"If we have to re-invent the wheel,       |
| EMAIL: g2devi@cdf.utoronto.ca    |  can we at least make it round this time"|
+----------------------------------+------------------------------------------/




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 24 Feb 1994 17:19:38 GMT
Raw View
I've been following this discussion for some time, and while most of the
arguments I would make have already been made, there is one point that
seems to be getting lost.

This issue seems, to me, to be somewhat related to the issue of putting
"const" on things.  In the project I am currently working on, we tend to
avoid using const because it generates too many warnings.  Why?  Because in
order to use const effectively and without warnings, it has to be used
consistently _everywhere_.  No exceptions.  Once one interface fails to
specify const on an argument that should have it, then you will inevitably
get warnings about it because somewhere or other you will eventually pass
an object marked "const" to that routine.

Warnings are a distraction, or else they just get totally ignored.  If you
intend to pay attention to some warnings, then you have to examine all of
them to discern which ones are interesting.  If throw signatures, or lack
thereof, generates lots of warnings, then there is an increased chance I
will miss the ones I am really interested in.  In other words, if the
default means "throws nothing", I don't care whether violation is an error
or warning, because both will be a major irritant to me.

I agree with the choice of "default means throws nothing", for much the same
reason as I avoid "const" if I can.  I don't have the time to go back and
change all my code to consistently use "const", nor would there likely ever
be time to go through and change all the function signatures in a large
project all at once.

I would just like to be able to use exceptions, but I don't have a compiler
that supports it.  When we get one, we'll have to incorporate usage of
exceptions gradually.  One way to do that is change only a few low-level
routines to throw exceptions in certain well-defined circumstances, and
change just a few high-level routines to catch those exceptions and deal
with them.  Meanwhile, the intermediate routines don't have to change.
With the "default means throws nothing", nearly every routine in the
project would have to be modified, and there would never be time enough to
do that.

In article <1994Feb24.112515.2574@cdf.toronto.edu>, g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
> In article <JSS.94Feb23162538@summit.lucid.com> jss@summit.lucid.com (Jerry Schwarz) writes:
> >The difference is that under my approach functions can start throwing
> >arbitrary exceptions for emergencies without changing any callers.
> >Under your approach I can only throw an "abort" without changing
> >callers.

I haven't really been able to understand this "emergency" versus
"non-emergency" distinction very well.  Out of memory doesn't necessarily
mean I can't continue -- it might mean I need to switch to using another
memory pool, or it might mean the memory pool reserved for a particular
subsystem is full, but the rest of the application can continue.

Furthermore, what seems like an "emergency" doesn't preclude an attempt
to shut down gracefully, especially if you protect the shutdown with
exception handlers.  For instance, a segmentation fault may mean things
are really bad, or it might just mean that one little data structure is
trashed but everything else is okay.  In the latter case, attempts to shut
down are very likely to succeed (at least partially).

I dislike "all or nothing" approaches, especially when I'm dealing with
existing code.  I want to make my application more robust with exceptions,
but it will take time.  I don't regard robust as a yes-or-no proposition,
so I don't expect my application to become 100% robust overnight.  Besides,
using exceptions gets you more than robustness -- it also buys time and
space over such techniques as status codes.  I'd like to get those benefits
without having to delay my next product release by 6 months of mostly
wasted effort.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Thu, 24 Feb 94 09:44:42 PST
Raw View
In article <JSS.94Feb23162538@summit.lucid.com>, jss@summit.lucid.com (Jerry Schwarz) writes:
|>
|> chase:
|>
|> Ditto. Except that code that does "new" is pervasive and (according
|> to the working paper) throws an exception.  You would presumably
|> want to make this the "abort" exception.

That depends largely on what an "abort" exception is and does.

If it unwinds the stack all the way through main() before aborting,
that is fine, but that is not what unexpected() does.

Why do I insist on this for out-of-memory?

Because, even if it cannot be recovered from, it is still desirable
for the program to clean up after itself in this case.  If it has
created temporary or lock files it needs to remove them, if it has
started a 'transaction', it needs to back it out, and so on.

This is a far cry from the situation where the abstract machine is
hopelessly corrupted, in which case immediate exit is the only real
possibility.  But we hardly need exceptions for that anyway.

For everything else, real exceptions are needed to allow proper cleanup
after the blockage.

[Actually, I do not agree that out-of-memory is unfixable, in fact simply
rewinding the stack releases memory, and selecting a memory conserving
algorithm after that may well solve the problem].

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: 24 Feb 94 18:13:07 GMT
Raw View
In article <2kg6j9INNec6@early-bird.think.com> chase@Think.COM (David Chase) writes:

> ark@tempel.research.att.com (Andrew Koenig) writes:

> |> Easy in theory; insurmountably complicated in practice.

> I think "insurmountably" stretches things a little bit.

Not if you include the pragmatic difficulties of convincing the
standards committee to accept a change of the magnitude you describe.

> |> There are three problems.
>
> |> First, the default for functions is that they potentially throw anything,
> |> which means that any function with an exception
> |> specification must wrap *all* other functions in try blocks.

> Change the default.  (In one fell swoop, all the existing code that
> does not throw exceptions is correctly typed.)

Indeed.  It also means that any time someone does a throw, all functions
between the throw point and the catch point must be changed to accommodate
the possibility of throwing that exception.  That is no great hardship
if your programming style is to assume that all exceptions will be
explicitly handled, but I think that in practice many people are going
to treat exceptions as run-time errors -- things they believe will never
happen.  If they do happen, they will *then* change their programs to
deal with them.

You may like this style or not, but I don't think there's a strong enough
case to be worth ruling it out.

> |> Second, exception specifications are not part of a function's type,
> |> which means that any call through a function pointer must also be wrapped
> |> in a try block.
>
> Add exception specifications to a function's type.

That's quite a major change.  For example, it means formulating conversion
rules for function pointers with one exception set to function pointers
with some other exception set.  It seems unlikely that it would be possible
to convince the committee to accept a change of that magnitude.

> |> Finally, things like overflow and division by zero result in undefined
> |> behavior, which means that the implementation is permitted to throw
> |> exceptions in such cases.  Since those exceptions are not formally part
> |> of the language definition, it is inappropriate to have to cater to them.
>
> Define exceptions that are raised on overflow and division by zero, if
> an implementation raises exceptions for those events.  Make the choice
> to raise the exception or not implementation-defined.  Define pragmas that
> will locally disable this checking.

Defining pragmas is out of the question.  Pragmas are exclusively reserved
for things that are *not* part of the standard.  And implementations that
signal overflow by raising signals rather than throwing exceptions are
unlikely to be happy about being forced to change incompatibly.


If it were up to me, I would abolish exception specifications altogether.
--
    --Andrew Koenig
      ark@research.att.com




Author: daniels@biles.com (Brad Daniels)
Date: Thu, 24 Feb 1994 21:13:25 GMT
Raw View
In article <2kinja$9ug@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
>I've been following this discussion for some time, and while most of the
>arguments I would make have already been made, there is one point that
>seems to be getting lost.
>
>This issue seems, to me, to be somewhat related to the issue of putting
>"const" on things.  In the project I am currently working on, we tend to
>avoid using const because it generates too many warnings.  Why?  Because in
>order to use const effectively and without warnings, it has to be used
>consistently _everywhere_.  No exceptions.  Once one interface fails to
>specify const on an argument that should have it, then you will inevitably
>get warnings about it because somewhere or other you will eventually pass
>an object marked "const" to that routine.

I prefer to either fix the problem when I encounter it or cast away const-
ness where I can't fix the interface.  Similarly, with exception signatures,
you can fix the code, or you can throw in an extra try block.  Ignoring
warnings is almost never a good thing to do.

>Warnings are a distraction, or else they just get totally ignored.  If you
>intend to pay attention to some warnings, then you have to examine all of
>them to discern which ones are interesting.  If throw signatures, or lack
>thereof, generates lots of warnings, then there is an increased chance I
>will miss the ones I am really interested in.  In other words, if the
>default means "throws nothing", I don't care whether violation is an error
>or warning, because both will be a major irritant to me.

... And your application dying on an unexpected exception won't be a major
irritant to your customers?  Which would you prefer exceptions to irritate?

>I agree with the choice of "default means throws nothing", for much the same
>reason as I avoid "const" if I can.  I don't have the time to go back and
>change all my code to consistently use "const", nor would there likely ever
>be time to go through and change all the function signatures in a large
>project all at once.

I would arrgue that the time required to add exception signatures (or
consistent use of "const") given a compiler which can point out exactly
where the errors occur is insignificant compared to the time and
potential lost business involved in fixing a bug introduced because you
didn't use one of these features.  Propagating fixes of this sort back
through the code can actually take surprisingly little time given the
number of places you need to modify.  I've removed const qualifiers from
functions resulting in the need to propagate changes back through 40
or more other modules in the space of an hour or so with the help of
emacs' next-error feature.  Adding const qualifiers usually results in
very few code changes, actually, not that that point is strictly relevant
to the main topic.

>I would just like to be able to use exceptions, but I don't have a compiler
>that supports it.  When we get one, we'll have to incorporate usage of
>exceptions gradually.  One way to do that is change only a few low-level
>routines to throw exceptions in certain well-defined circumstances, and
>change just a few high-level routines to catch those exceptions and deal
>with them.  Meanwhile, the intermediate routines don't have to change.
>With the "default means throws nothing", nearly every routine in the
>project would have to be modified, and there would never be time enough to
>do that.

The functions wouldn't change.  Their signatures would.  The scope of
such changes is more manageable than one might expect.  To add exceptions
gradually, though, it's probably better to start at the high level functions
and work down than to do it from the bottom up.  When you add an exception
to a low-level function, you are really adding it to every function which
uses that function as well, whether or not the exception signatures are
enforced.

...
>I dislike "all or nothing" approaches, especially when I'm dealing with
>existing code.  I want to make my application more robust with exceptions,
>but it will take time.  I don't regard robust as a yes-or-no proposition,
>so I don't expect my application to become 100% robust overnight.  Besides,
>using exceptions gets you more than robustness -- it also buys time and
>space over such techniques as status codes.  I'd like to get those benefits
>without having to delay my next product release by 6 months of mostly
>wasted effort.

It's not an all or nothing deal.  Adding exceptions at a high level is
really cheap.  Doing it at a lower level is a bit more work, but with
compile-time enforced exception signatures, it's at least fully automated
work.

Another thing about your arguments is that it sounds like you really don't
want to use exception signatures at all.  If you use them and they are
enforced only at run time, you can get into real trouble.  The problem
with making "no signature" mean "throws nothing" is that you can't just
start throwing exceptions wherever you want.  Basically, as I've been
saying all along, exception signatures with run-time enforcement are more
dangerous than not having exception signatures at all.  If you're going
to have exception signatures, you need static checking.  If you want to
do static checking, you need to have a missing exception signature indicate
that the function throws no exceptions.  Doing that, though, makes things
difficult for the people like you who just want to throw a few exceptions
here and there without really integrating them into their whole design.

The whole point of this discussion for me is that whatever the right
solution may be, enforcing exception signatures at run time results in
the worst possible behavior.  You need either full, static checking, or
no checking at all.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: jss@summit.lucid.com (Jerry Schwarz)
Date: 24 Feb 1994 21:56:53 GMT
Raw View

>   This issue seems, to me, to be somewhat related to the issue of putting
>   "const" on things.  In the project I am currently working on, we tend to
>   avoid using const because it generates too many warnings.  Why?  Because in
>   order to use const effectively and without warnings, it has to be used
>   consistently _everywhere_.  No exceptions.  Once one interface fails to
>   specify const on an argument that should have it, then you will inevitably
>   get warnings about it because somewhere or other you will eventually pass
>   an object marked "const" to that routine.

I've seen this referred to a "const pollution" and I agree, the
situation with exceptions is similar.  And I agree. We want to allow
introduction of exceptions without causing "exception pollution".

>   I haven't really been able to understand this "emergency" versus
>   "non-emergency" distinction very well.  Out of memory doesn't necessarily
>   mean I can't continue -- it might mean I need to switch to using another
>   memory pool, or it might mean the memory pool reserved for a particular
>   subsystem is full, but the rest of the application can continue.


As I've been using the word (and I introduced it into the discussion)
an emergency is a situation in which a function cannot perform
as specified.  Whether a particular situation (out of memory, out of
disk space, ...) is an emergency or not depends on where it occurs.






Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Fri, 25 Feb 1994 19:27:10 GMT
Raw View
Brad Daniels (daniels@biles.com) wrote:
: In article <CLMt5w.E59@tempel.research.att.com>,
: Andrew Koenig <ark@tempel.research.att.com> wrote:
: >
: >There are three problems.
: >
: >First, the default for functions is that they potentially throw anything,
: >which means that any function with an exception
: >specification must wrap *all* other functions in try blocks.

: The current default is the wrong choice for static checking.  The default
: for static checking should be that no signature means no exceptions are
: thrown.  There has already been a bit of discussion in this thread of
: why that is a better approach.

: >Second, exception specifications are not part of a function's type,
: >which means that any call through a function pointer must also be wrapped
: >in a try block.

: This is definitely a problem.  I see this as more of a correctable deficiency
: in the type system in light of exceptions than as a problem with static
: checking.

: >Finally, things like overflow and division by zero result in undefined
: >behavior, which means that the implementation is permitted to throw
: >exceptions in such cases.  Since those exceptions are not formally part
: >of the language definition, it is inappropriate to have to cater to them.

: I'm not sure how much of a problem this is.  The "best" solution from the
: standpoint of functionality would be to have such events throw known
: exceptions.  Unfortunately, this behavior may not be implementable on
: all architectures, and is therefore likely to be considered unsuitable
: from a standards perspective.  Failing that, however, implementation
: defined behavior (I assume that's what you meant, rather than undefined),
: is, as the name implies, implementation defined.  An implementation is
: free to throw exceptions outside a function's type signature if it
: so desires in such cases.

I concur.

It seems to me that this whole discussion is less about static vs. dynamic
checking than it is about that age old issue of compatibility vs. usability.
Static type checking for exceptions is obviously the right choice.  Anyone
who denies that denies C++.  For it is the same theory which has popularized
C++ over languages with loose type checking.  Languages which lack static
type checking, gamble at run-time.  I see no reason for exceptions to be
any less important.  As for the age old issue of compatibility vs. usability,
I have watched this battle rage over practically every major extension to
C++ (the last being bool).  It seems there are more "compatibles" than
"usables" on the C++ committee.  I think the static checking responders have
adequetely addressed the concerns of the dynamic checking responders.  The
only question which remains is whether the C++ committee wants to leave room
to "cheat" in implementing and using libraries.  It appears in this case that
the language lawyers or more precisely language legislators are very much
like their congressional counter-parts; leaving loop-holes to serve themselves.
If the dynamic checking responders are still not satisfied with the arguments
set forth by the static checkers, I would like to leave you with some food
for thought from my domain.  The company I work for is a manufacturer of
medical equipment.  When our system goes to unexpected(), not just the
application dies!  Those who have their fingers on the keyboard ready to tell
me how to change unexpected() to something which "recovers", should think
about what recovery is possible when an unknown event occurs during a surgical
procedure.  This, like the overflow possibilities inherent in the ++ operator
for the new bool type leave serious questions in my mind as to the long term
viability of C++ in critical applications.

I wish the "compatibles" would stop dismissing the "usables" as simply purists,
and stop introducing backward compatibility loop-holes which can undermine the
integrity of the applications built with the C++ language.

Stephen Cipolli
Datascope Corp.




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Fri, 25 Feb 94 09:15:11 PST
Raw View
In article <CLqyyE.75y@biles.com>, daniels@biles.com (Brad Daniels) writes:
|> Bill Leonard writes:
|>
|> I would arrgue that the time required to add exception signatures (or
|> consistent use of "const") given a compiler which can point out exactly
|> where the errors occur is insignificant compared to the time and
|> potential lost business involved in fixing a bug introduced because you
|> didn't use one of these features.

Not when you have several hundred to several thousand *functions*
that have all been well tested.  At least not unless there is some way
to *automate* the changes.

Now, luckily we are just starting to use C++, so we will be enforcing
consistant use of const from the start.  But, like Bill Leonard, our
compiler does not currently support exceptions, so we will have to
retrofit those.

|> Propagating fixes of this sort back
|> through the code can actually take surprisingly little time given the
|> number of places you need to modify.  I've removed const qualifiers from
|> functions resulting in the need to propagate changes back through 40
|> or more other modules in the space of an hour or so with the help of
|> emacs' next-error feature.

Oh, a mere 40 modules!  No wonder you think it possible, you are only
dealing with a small to medium sized project!

Seriously, one of our *smaller* projects here has 746 source files
(not counting header files).  At 40 modules and hour that's about 18.5
hours - 2 and a half work days.  [And note, many of those source files
contain many functions - often over 10, so we are talking about over
5000 functions].

|> >I would just like to be able to use exceptions, but I don't have a compiler
|> >that supports it.  When we get one, we'll have to incorporate usage of
|> >exceptions gradually.  One way to do that is change only a few low-level
|> >routines to throw exceptions in certain well-defined circumstances, and
|> >change just a few high-level routines to catch those exceptions and deal
|> >with them.  Meanwhile, the intermediate routines don't have to change.
|> >With the "default means throws nothing", nearly every routine in the
|> >project would have to be modified, and there would never be time enough to
|> >do that.
|>
|> The functions wouldn't change.  Their signatures would.

To change the signatures you *still* have to edit both the header file
and the implementation file - the text of the function header changes.

Doing that in two places for 5000+ functions is going to take a *long* time!

|>  The scope of
|> such changes is more manageable than one might expect.  To add exceptions
|> gradually, though, it's probably better to start at the high level functions
|> and work down than to do it from the bottom up.  When you add an exception
|> to a low-level function, you are really adding it to every function which
|> uses that function as well, whether or not the exception signatures are
|> enforced.

What he is saying is that the types of errors he is trying to catch are
those that are generated in low-level routines - like disk full - but
that it is the high-level routines that know how to handle the problem.
Without exceptions you have to use error returns that are propagated
up through many levels.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: daniels@biles.com (Brad Daniels)
Date: Fri, 25 Feb 1994 23:03:50 GMT
Raw View
In article <9402251715.AA22080@tdat.elsegundoca.ncr.com>,
Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
...
>|> Propagating fixes of this sort back
>|> through the code can actually take surprisingly little time given the
>|> number of places you need to modify.  I've removed const qualifiers from
>|> functions resulting in the need to propagate changes back through 40
>|> or more other modules in the space of an hour or so with the help of
>|> emacs' next-error feature.
>
>Oh, a mere 40 modules!  No wonder you think it possible, you are only
>dealing with a small to medium sized project!
>
>Seriously, one of our *smaller* projects here has 746 source files
>(not counting header files).  At 40 modules and hour that's about 18.5
>hours - 2 and a half work days.  [And note, many of those source files
>contain many functions - often over 10, so we are talking about over
>5000 functions].

My current project consists of about 300 modules, each implementing an
average of 20-30 functions.  A change to one of the low-level functions
requiring back-propagation of modifications usually effects on the order
of 30-40 modules in my code, which can be done in about an hour.  Scaling
proportionately to your project, I'd estimate such a change would take 2-3
hours.  There are a few classes which would effect more code (e.g. our
string class), but in general, any given class doesn't seem to be used by
more than about 10-15% of the rest of the code.  That proportion may vary
from application to application, but it seems likely that the proportion
of code using any given other piece of code should decrease with larger
projects, since most pieces of the code implement discrete functionality
in large systems.  The really low-level, used-all-over-the-place classes
are the only ones which present potentially substantial problems.

The times above are per low-level function changed.  The time to make
exceptions ubiquitous is indeed substantial, but adding any given exception
is quite manageable.  Making the changes as needed is a viable approach.

...
>|>  The scope of
>|> such changes is more manageable than one might expect.  To add exceptions
>|> gradually, though, it's probably better to start at the high level functions
>|> and work down than to do it from the bottom up.  When you add an exception
>|> to a low-level function, you are really adding it to every function which
>|> uses that function as well, whether or not the exception signatures are
>|> enforced.
>
>What he is saying is that the types of errors he is trying to catch are
>those that are generated in low-level routines - like disk full - but
>that it is the high-level routines that know how to handle the problem.
>Without exceptions you have to use error returns that are propagated
>up through many levels.

This seems again to be an argument not to use exception signatures at all.
Not using exception signatures is certainly an understandable approach,
and certainly the easiest and quickest one for retrofitting exceptions
into a large program.  If you plan to use exception signatures at all,
however, you will be in for a shock the first time one of your functions
that thinks it's only throwing its own locally generated exceptions receives
instead a disk full exception.  At that point, with run-time exception
signature checking, your program will go into unexpected() and probably
die quite messily, since unexpected() is tough to recover from.

People keep harping on what a pain it might be to add exceptions if
the kind of static checking I've discussed were to be added.  The point
of this whole discussion, though, is that any other kind of checking is
*dangerous*.  To do it right, you need to either have no checking at all,
or do full-blown static checking with all that implies.  I'd like to be
able to do the former to support retrofitting exceptions to old code more
easily, and the latter for new development.  However, if the latter were
the default, I don't think the pain would be as bad as some seem to fear.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 26 Feb 1994 04:09:20 -0500
Raw View
ark@tempel.research.att.com (Andrew Koenig) writes:

>If it were up to me, I would abolish exception specifications altogether.

At last, a workable proposal!

Bob Foster
Object Factory




Author: john_werner@taligent.com (John Werner)
Date: Sat, 26 Feb 1994 19:11:11 GMT
Raw View
In article <CLqqLw.M6C@tempel.research.att.com>,
ark@tempel.research.att.com (Andrew Koenig) wrote:

> If it were up to me, I would abolish exception specifications altogether.

Sounds good to me.  In my own projects I've never been able to see a good
reason for using them.  The vast majority of my exception handlers don't
care what kind of exceptions they catch; they just clean something up and
then throw again.  Occasionally there's a handler that catches a certain
kind of exception and tries the operation again using a different method.
But even in those cases other exceptions are just passed through or caught
with a 'catch (...)' for cleanup before throwing again.

Only one or two handlers near the top of the call tree try to decipher all
the different kinds of exceptions and report them to the user.  An unknown
exception 'catch (...)' is reported as an "unknown error" or "programming
error".  Adding exception specifications to this kind of code would add a
big maintenance burden without much real benefit.

--
John Werner                          john_werner@taligent.com
Taligent, Inc.




Author: mat@mole-end.matawan.nj.us
Date: Sun, 27 Feb 1994 02:35:30 GMT
Raw View
In article <9402251715.AA22080@tdat.ElSegundoCA.NCR.COM>, swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen) writes:
> In article <CLqyyE.75y@biles.com>, daniels@biles.com (Brad Daniels) writes:
> |> Bill Leonard writes:

> |> I would arrgue that the time required to add exception signatures (or
> |> consistent use of "const") ...  is insignificant compared to the time and
> |> potential lost business involved in fixing a bug introduced because you
> |> didn't use one of these features.

Except that they protect against different sorts of things.  Const
correctness ensures that information flows in the direction that you
expect, and that data have the values and the meaning that you expect.

 ...
> Now, luckily we are just starting to use C++, so we will be enforcing
> consistant use of const from the start.  But, like Bill Leonard, our
> compiler does not currently support exceptions, so we will have to
> retrofit those.

Exception specifications ensure that exceptions of some unnamed kind do
not propagate back over a function.  But the whole point of exceptions
is that a condition that is detected in one place _CAN_ propagate from
the place in which it is detected to the place in which it can be
interpreted.  The static analysis of  const  correctness is not adequate
for checking this, since it requires determining whether an exception can
be interpreted in the abstraction on which this function is based.

IMO, exception specifications are best reserved for `firewalls' around
major functional or semantic subsystems.
--
 (This man's opinions are his own.)
 From mole-end    Mark Terribile
 mat@mole-end.matawan.nj.us, Somewhere in Matawan, NJ
 (Training and consulting in C, C++, UNIX, etc.)




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: Sun, 27 Feb 1994 14:07:18 GMT
Raw View
In article <2kn3k0$gvp@news.delphi.com> bobkf@news.delphi.com (BOBKF@DELPHI.COM) writes:

> >If it were up to me, I would abolish exception specifications altogether.

> At last, a workable proposal!

I'm afraid not.  The first version of the exception proposal that
Bjarne and I wrote did not have exception specifications.  We added
them after several committee members indicated they would argue strongly
against the proposal without them and would extend their own implementations
to include them.
--
    --Andrew Koenig
      ark@research.att.com




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: 28 Feb 94 04:43:15 GMT
Raw View
In article <1994Feb25.192710.18677@datascope.com> sfc@datascope.com (Steve F. Cipolli (P/M)) writes:

> It seems to me that this whole discussion is less about static vs. dynamic
> checking than it is about that age old issue of compatibility vs. usability.
> Static type checking for exceptions is obviously the right choice.  Anyone
> who denies that denies C++.  For it is the same theory which has popularized
> C++ over languages with loose type checking.  Languages which lack static
> type checking, gamble at run-time.  I see no reason for exceptions to be
> any less important.

I'm sorry, but I must still disagree.

The key to this issue, I think, is whether you wish to treat an exception
as an `alternate return,' in which case the exceptions possibly thrown
are quite reasonably part of a function type, or as a way to handle possibly
unanticipated situations, in which case they just don't fit into the
static type checking model.

Bjarne and I thought about this when making our original proposal, and we
noted the following prior art:

 1. CLU has static type checking on exceptions.  In looking
 at CLU usage, Bjarne discovered that it was not uncommon for
 exceptions to go unchecked by their immediate caller and
 therefore transmute to `failure.'

 2. ML has much stronger type checking than C++.  Nevertheless,
 it has no exception checking at all, except that the type thrown
 must (exactly) match the type caught.  Exception matching is
 done entirely at execution time.  They thought about compile-time
 exception checking and concluded it was too much extra complexity
 in the type system without a corresponding increase in utility.

Theoretical arguments aren't very convincing, at least to me, so here
is a pragmatic one.

Suppose you're using a library from vendor X that in turn uses a library from
vendor Y.  The Y library has I/O routines, which may potentially throw
I/O error exceptions.  Suppose, for example, that the Y library contains

 extern void write (Device, Buffer) throw (IOerror);

Now suppose that vendor Y adds networking capabilities to the Y library.
That means that if you apply `write' to a network device, it might now
raise a network error:

 extern void write (Device, Buffer) throw (Networkerror, IOerror);

Now, this happens only if you're using a network device.  In that sense,
it is strictly an upward compatible change.  Yet if exceptions are statically
checked, there is no way to make this an upward compatible change to the
Y library: adding Networkerror to the throw list will break the library
from vendor X.

That in turn means that you can't accept changes from vendors X and Y
independently: you must first take changes from X (to accept the extra
exception) and then from Y.  Indeed, Y cannot begin shipping a new version
of the Y library until X has sent out new versions to all X's customers.

In other words, static checking imposes the following required sequence:

 Y decides to add a new exception.
 Y tells all vendors whose libraries depend on Y that the
  new exception is coming.
 All the vendors change their libraries.
 All the cusomters install the new versions.
 Finally, Y can ship the new version.

Obviously, if there are more dependencies, this sequence becomes even
longer.  As it is, any customer of Y can block any new version of the
Y library that throws any new exceptions.  Ick.

It was scenes like this that argued convincingly that checking exception
specifications must not be done at compile time.  In effect, it would mean
that the exceptions thrown by a function must never change once that function
has been defined.

--
    --Andrew Koenig
      ark@research.att.com




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 28 Feb 1994 08:41:23 GMT
Raw View
barmar@think.com (Barry Margolin) writes:

>In article <CLC2Iy.M6F@biles.com> daniels@biles.com (Brad Daniels) writes:
>>That is a spurious argument.  Adding new kinds of exceptions to a function
>>constitutes an incompatible change in a library.  If your library calls a
>>function which suddenly and unexpectedly starts throwing exceptions, the
>>library is broken because something it uses has changed incompatibly.
>
>It seems that the dilemma X3J16 faced was that they wanted to make such an
>incompatible change to the standard C++ libraries.  If they also required
>throw signatures to be enforced at compile time, this would mean that just
>about everything would need to be recompiled when a vendor incorporated the
>exceptions into its library.

But everything should be recompiled anyway, since the headers will have
changed.  For starters, the new headers will include definitions for
some new identifiers, in particular the types which occur in the exception
specifications.  Since when was recompilation such a problem?

The problem is not the need for recompilation, but the danger that
the change might break code.  If code is going to break, I'd personally
prefer if it broke loudly, rather than silently.  Hence I'd prefer
static checking.

>The current specification appears to be a compromise that allows these
>changes, without causing a domino effect of recompilation.

The current specification appears to be likely to cause many programs
to break silently.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 28 Feb 1994 15:26:57 GMT
Raw View
In article <9402251715.AA22080@tdat.ElSegundoCA.NCR.COM>, swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen) writes:
> In article <CLqyyE.75y@biles.com>, daniels@biles.com (Brad Daniels) writes:
> |> Bill Leonard writes:
> |>
> |> I would arrgue that the time required to add exception signatures (or
> |> consistent use of "const") given a compiler which can point out exactly
> |> where the errors occur is insignificant compared to the time and
> |> potential lost business involved in fixing a bug introduced because you
> |> didn't use one of these features.

Just for the record, I didn't say the above. :-)  It apparently was a reply
to something I said.

The argument made above, though, deserves a response.  It sounds like the
responder is arguing that every possible bug should be fixed immediately,
right now, before any software is released.  That's a fine, high-minded
sentiment, but hardly practical when dealing with an existing product.

I don't have the option of introducing exceptions wholesale, complete, into
my software all in one go.  It will never happen.  If that is my *only*
choice, then they'll *never* get incorporated; I would hate to see that
happen.  So I would prefer the option of introducing exceptions gradually.

> Not when you have several hundred to several thousand *functions*
> that have all been well tested.  At least not unless there is some way
> to *automate* the changes.

Amen.

> |> Propagating fixes of this sort back
> |> through the code can actually take surprisingly little time given the
> |> number of places you need to modify.  I've removed const qualifiers from
> |> functions resulting in the need to propagate changes back through 40
> |> or more other modules in the space of an hour or so with the help of
> |> emacs' next-error feature.
>
> Oh, a mere 40 modules!  No wonder you think it possible, you are only
> dealing with a small to medium sized project!

Amen again!  What's more, _removing_ const is much less disruptive than
_adding_ const, which is more comparable to adding throw signatures.
Adding one const qualifier to an argument can propagate to _hundreds_ of
functions.

Besides the issue of time, though, there is the issue of effect.  What bugs
were introduced by making all those changes?  When we are close to
releasing our product, we try not to make *ANY* change that is not
absolutely necessary.  If we make any kind of a major change, that
necessitates starting over completely with the testing cycle, which is
*very expensive*.

Like I said, I think adding _some_ exceptions to my software will improve
its quality and reliability.  That doesn't mean it's perfect, but if I
can't even get started it will never get any better at all.  So in my
judgment, it's better to get a little added quality now and improve it
gradually than to never get any better at all.

> |> The scope of such changes is more manageable than one might expect.
> |> To add exceptions gradually, though, it's probably better to start at
> |> the high level functions and work down than to do it from the bottom
> |> up.  When you add an exception to a low-level function, you are really
> |> adding it to every function which uses that function as well, whether
> |> or not the exception signatures are enforced.

So what, if those functions aren't interested in that exception?  Right now
I have lots of functions that check error returns from functions they call,
just so they can return an error return, which their caller uses only so it
can return an error code, etc.  All those checks would go away if the error
return became an exception.

One of the big advantages I see with C++ exceptions over Ada-like
exceptions is that they can pass information along.  So the low-level
routine can generate an exception and pass along enough information to
generate an intelligible error message at a high level (where the knowledge
of how to present an error message resides).  Intermediate routines need to
be involved only if (a) they need to do some cleanup, or (b) they wish to
add to the exception information for a more informative message, or (c) the
exception doesn't affect their operation and they can completely recover.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: chase@Think.COM (David Chase)
Date: 28 Feb 1994 15:38:09 GMT
Raw View
|> > ark@tempel.research.att.com (Andrew Koenig) writes:
|> > |> Easy in theory; insurmountably complicated in practice.

I wrote:
|> > I think "insurmountably" stretches things a little bit.

ARK wrote again:
|> Not if you include the pragmatic difficulties of convincing the
|> standards committee to accept a change of the magnitude you describe.

Well yes, there is that.  My experience with committees has left me
somewhat skeptical of their usefulness.  Perhaps you want a critic's
committee to provide feedback ("did you remember this?") but actual
negotiation over features tends to arrive at some pretty weird
compromises (or, less charitably, I view a committee as a good way to
take a bunch of smart people and make them act stupid.  The best
negotiators are not necessarily also the best designers or
implementors.)

Still, I think all this chit-chat about backward compatibility is a
little bit silly.  Last hunk of C++ I worked on (in the 100-200kloc
range) didn't use virtual base classes, didn't use multiple
inheritance, didn't use templates, didn't use exceptions, was
conservative in its use of overloading and destructors, and I was the
only person using virtual member functions, but the damn thing still
broke with every new release (2.0 -> 2.1, 2.1 -> 3.0, cfront ->
"other") of the language or different compiler.

I'm afraid I also hadn't thought of the template problems.  Yuck.
(On the other hand, I also think that templates are generally
busted, but that is another story altogether.)

David Chase, speaking for myself
Thinking Machines Corp.




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 28 Feb 1994 15:54:44 GMT
Raw View
In article <CLsyqF.JI5@biles.com>, daniels@biles.com (Brad Daniels) writes:
> This seems again to be an argument not to use exception signatures at all.

Hurrah!  You finally got it!

> Not using exception signatures is certainly an understandable approach,
> and certainly the easiest and quickest one for retrofitting exceptions
> into a large program.  If you plan to use exception signatures at all,
> however, you will be in for a shock the first time one of your functions
> that thinks it's only throwing its own locally generated exceptions receives
> instead a disk full exception.  At that point, with run-time exception
> signature checking, your program will go into unexpected() and probably
> die quite messily, since unexpected() is tough to recover from.

I don't get it.  If the default signature is changed to "nothing means
throws nothing", how can I ever use exceptions and not use signatures?

> People keep harping on what a pain it might be to add exceptions if
> the kind of static checking I've discussed were to be added.  The point
> of this whole discussion, though, is that any other kind of checking is
> *dangerous*.

I think that is scare-mongering rather than a thoughtful argument.  How can
using exceptions without checking be more dangerous than not using
exceptions at all?  Besides, dangerous is a relative term and far too
strong to be applied to most software.  If my software fails, no one is
going to be injured or killed, so dangerous hardly seems applicable.
Remember, we're talking about a standard that *ALL* software will have to
live with.

> To do it right, you need to either have no checking at all,
> or do full-blown static checking with all that implies.

This is *your* definition of "right", correct?  I think the argument boils
down to a disagreement over that definition.

> I'd like to be
> able to do the former to support retrofitting exceptions to old code more
> easily, and the latter for new development.

Then you'd better not change the default to "nothing means throws no
exceptions", because then you'll be *forced* to use signatures.

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: daniels@biles.com (Brad Daniels)
Date: Mon, 28 Feb 1994 15:57:45 GMT
Raw View
In article <CLx3s3.HFL@tempel.research.att.com>,
Andrew Koenig <ark@tempel.research.att.com> wrote:
...
>Theoretical arguments aren't very convincing, at least to me, so here
>is a pragmatic one.
>
>Suppose you're using a library from vendor X that in turn uses a library from
>vendor Y.  The Y library has I/O routines, which may potentially throw
>I/O error exceptions.  Suppose, for example, that the Y library contains
>
> extern void write (Device, Buffer) throw (IOerror);
>
>Now suppose that vendor Y adds networking capabilities to the Y library.
>That means that if you apply `write' to a network device, it might now
>raise a network error:
>
> extern void write (Device, Buffer) throw (Networkerror, IOerror);
>
>Now, this happens only if you're using a network device.  In that sense,
>it is strictly an upward compatible change.  Yet if exceptions are statically
>checked, there is no way to make this an upward compatible change to the
>Y library: adding Networkerror to the throw list will break the library
>from vendor X.

This is an argument against *all* exception signature checking, and indeed
against exception signatures.  This is what I've been talking about the whole
time.  If I have some code which calls their write routine and has a signature
of throw(IOerror), my code will blow up at run-time if I get a Networkerror.
If I didn't have a signature on my function, the exception would get passed
on to someone who might be able to handle it.  In other words, run-time
exception signature checking is worse than no checking at all.  If we are
to have exception signatures, the only thing which makes sense is static
checking.

As to your chain of vendor upgrades argument, I really don't think that's
a valid argument against statically checked exception signatures.  We already
have that problem now on a variety of other scores.  If vendor A adds a private
member to a class used by vendor B, all vendor B's code needs to be recompiled.
If I get B's code in object form, that means I have to wait for an upgrade
from vendor B.  Similarly, if vendor A makes an incompatible change to any
aspect of its library's interface used by vendor B, I'll have to get an
upgrade from vendor B.  Statically checked exception signatures would just
be a new form of an existing problem on that score.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Mon, 28 Feb 1994 17:57:54 GMT
Raw View
Bill Leonard (bill@amber.csd.harris.com) wrote:
: This issue seems, to me, to be somewhat related to the issue of putting
: "const" on things.  In the project I am currently working on, we tend to
: avoid using const because it generates too many warnings.  Why?  Because in
: order to use const effectively and without warnings, it has to be used
: consistently _everywhere_.  No exceptions.  Once one interface fails to
: specify const on an argument that should have it, then you will inevitably
: get warnings about it because somewhere or other you will eventually pass
: an object marked "const" to that routine.

I guess you believe the C/C++ committes designed the "const" mechanism to
annoy you, not to allow checking which prevents modification to read-only
or ROM'ed data.  Not using const in C++ is either unsafe or inefficent, since
when passing an (large) object into a function you will either waste time
passing the whole object or pass it by reference which opens up the possibility
that the callee could modify your object without your knowledge.

: Warnings are a distraction, or else they just get totally ignored.  If you
: intend to pay attention to some warnings, then you have to examine all of
: them to discern which ones are interesting.  If throw signatures, or lack
: thereof, generates lots of warnings, then there is an increased chance I
: will miss the ones I am really interested in.  In other words, if the
: default means "throws nothing", I don't care whether violation is an error
: or warning, because both will be a major irritant to me.

This assume you plan to undermine the exception mechanism in much the same way
as you did with const.

: I agree with the choice of "default means throws nothing", for much the same
: reason as I avoid "const" if I can.  I don't have the time to go back and
: change all my code to consistently use "const", nor would there likely ever
: be time to go through and change all the function signatures in a large
: project all at once.

I hope you mean you agree with "default means throws everything", since new
will throw xalloc for all your existing code.  In any case I'll take
"default means throws nothing except system specific emergencies".

So your argument is that you don't like "const" and so you mis-use it,
and so therefore exceptions should be implemented in a way as to
let you mis-use it more easily.  I think your argument better supports the
argument for static checking (like const) by precedent.  Your use of const
argues strongly for strict static checking to prevent mis-use of potentially
lethal language mechanisms like dynamic checking of exceptions.

: I would just like to be able to use exceptions, but I don't have a compiler
: that supports it.  When we get one, we'll have to incorporate usage of
: exceptions gradually.  One way to do that is change only a few low-level
: routines to throw exceptions in certain well-defined circumstances, and
: change just a few high-level routines to catch those exceptions and deal
: with them.  Meanwhile, the intermediate routines don't have to change.
: With the "default means throws nothing", nearly every routine in the
: project would have to be modified, and there would never be time enough to
: do that.

I hope and pray that the software you are writing does not hold the balance
of human life.  If the modification of what should be const objects does not
kill someone, the time spent in unexpected() certainly will.

: I haven't really been able to understand this "emergency" versus
: "non-emergency" distinction very well.  Out of memory doesn't necessarily
: mean I can't continue -- it might mean I need to switch to using another
: memory pool, or it might mean the memory pool reserved for a particular
: subsystem is full, but the rest of the application can continue.

I think better terminology would be "user exceptions" and "system exceptions".
Both of which can be caught, but only user exceptions need be prototyped (the
system exceptions are assumed in all functions).

: I dislike "all or nothing" approaches, especially when I'm dealing with
: existing code.  I want to make my application more robust with exceptions,
: but it will take time.  I don't regard robust as a yes-or-no proposition,
: so I don't expect my application to become 100% robust overnight.  Besides,
: using exceptions gets you more than robustness -- it also buys time and
: space over such techniques as status codes.  I'd like to get those benefits
: without having to delay my next product release by 6 months of mostly
: wasted effort.

I dislike approaches which undermine the integrity of the applications written
in a language just so someone doesn't have to type in some prototypes.
As for the "existing code" argument, if you were not planning on having to
change your code, why did you choose a language which is still evolving? or
at least why would you expect to be able to upgrade your code to a later
version of the language definition without altering your existing code?

As for robustness see David Chase's (I believe) response which basically
said (paraphrase) ...
How robust can your code be if it can throw exceptions which you might not
know about.

Sorry if this response is a little rough, but that "const" thing real bugs me.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.




Author: dag@control.lth.se (Dag Bruck)
Date: 28 Feb 1994 18:45:21 GMT
Raw View
>>>>> On Fri, 25 Feb 1994 19:27:10 GMT, sfc@datascope.com (Steve F. Cipolli (P/M)) said:

> This, like the overflow possibilities inherent in the ++ operator
> for the new bool type leave serious questions in my mind as to the long term
> viability of C++ in critical applications.

You have mis-interpreted the ++ operator for bool.

Operator ++ sets a bool variable to true.  It does not increment it.
There is no restriction on the number of times you can use ++ on a
bool.  It is equivalent to

 b = true;

This is indeed an example where the committee has gone the "purer" way
instead of just pushing C compatibility to the bitter end.

    -- Dag




Author: daniels@biles.com (Brad Daniels)
Date: Mon, 28 Feb 1994 19:29:50 GMT
Raw View
In article <2kt444$eh2@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
>In article <CLsyqF.JI5@biles.com>, daniels@biles.com (Brad Daniels) writes:
>> This seems again to be an argument not to use exception signatures at all.
>
>Hurrah!  You finally got it!

Finally?  When did you jump into this discussion?  All I've ever said is
that *if* we have exception signatures, they should not be enforced at
run-time.  We're then left with either eliminating the things altogether,
or enforcing them properly (i.e. statically.)  I've said exactly the same
thing more times than I can count, but people seem to keep misinterpreting
me.  I personally like the idea of having statically enforced signatures,
but in many ways, I'd be just as happy with no signatures.  I just don't
want to see unexpected() errors at run time because somebody put in an
exception signature which became inaccurate at some later point.

>> Not using exception signatures is certainly an understandable approach,
>> and certainly the easiest and quickest one for retrofitting exceptions
>> into a large program.  If you plan to use exception signatures at all,
>> however, you will be in for a shock the first time one of your functions
>> that thinks it's only throwing its own locally generated exceptions receives
>> instead a disk full exception.  At that point, with run-time exception
>> signature checking, your program will go into unexpected() and probably
>> die quite messily, since unexpected() is tough to recover from.
>
>I don't get it.  If the default signature is changed to "nothing means
>throws nothing", how can I ever use exceptions and not use signatures?

Use "throw(...)" everywhere?  :-)  Seriously, though, what I'm hoping
is that rigidly enforced exception signatures will become a widely available
compile time option on most implementations of C++.  In order for that to
happen, though, the standard committee will probably have to make exception
signatures part of a function's type.  Overloading on exception signatures
is unlikely to be feasible because of the same kinds of problems as over-
loading on return values, but there is still benefit to be had from differen-
tiating the types of functions with different exception signatures.  Not least
of these benefits is that of encouraging implementors to provide options
to use that information for optional strict checking.

>> People keep harping on what a pain it might be to add exceptions if
>> the kind of static checking I've discussed were to be added.  The point
>> of this whole discussion, though, is that any other kind of checking is
>> *dangerous*.
>
>I think that is scare-mongering rather than a thoughtful argument.  How can
>using exceptions without checking be more dangerous than not using
>exceptions at all?

THAT'S NOT WHAT I'VE BEEN SAYING.  What's dangerous is putting in
exception signatures which will be checked at run-time.  Having no checking
at all is not terribly dangerous, though it's not as safe as rigid static
checking.  Have I made my stand clear yet?  Sheesh.

>Besides, dangerous is a relative term and far too
>strong to be applied to most software.  If my software fails, no one is
>going to be injured or killed, so dangerous hardly seems applicable.
>Remember, we're talking about a standard that *ALL* software will have to
>live with.

Oh, I see.  Those of us whose software may be used to control, say, an
explosives factory, a nuclear power plant, or maybe some kind of life-saving
medical equipment should live with something that makes their code unsafe
so that those of you who write programs where a crash is just a minor
inconvenience can avoid a little extra work to make your code work right.
Double sheesh!

>> To do it right, you need to either have no checking at all,
>> or do full-blown static checking with all that implies.
>
>This is *your* definition of "right", correct?  I think the argument boils
>down to a disagreement over that definition.

If you think it's right for your program to blow up unexpectedly at run-time,
well...  Just remind me not to stand near any equipment controlled by any
software you wrote.

>> I'd like to be
>> able to do the former to support retrofitting exceptions to old code more
>> easily, and the latter for new development.
>
>Then you'd better not change the default to "nothing means throws no
>exceptions", because then you'll be *forced* to use signatures.

The fact is, at this point, there is little chance of getting static checking
into the first draft of the standard.  What I'm hoping is that the underlying
support mechanisms needed for static checking will go into place so that we
can get optional static checking from several compiler vendors.  I will
never use the current run-time checking because it is so dangerous.  I would
love to use static checking were it available.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: cbarber@bbn.com (Christopher Barber)
Date: 28 Feb 94 12:26:40
Raw View
In article <CLx3s3.HFL@tempel.research.att.com>
ark@tempel.research.att.com (Andrew Koenig) writes:

   The key to this issue, I think, is whether you wish to treat an exception
   as an `alternate return,' in which case the exceptions possibly thrown
   are quite reasonably part of a function type, or as a way to handle possibly
   unanticipated situations, in which case they just don't fit into the
   static type checking model.

Truly unanticipated situations are not really capable of being handled
through anything except some sort of 'abort' exception.  If you can
anticipate it, then you should be able to include the exception in the
signature as part of the function's type.

   ...

   Suppose you're using a library from vendor X that in turn uses a
   library from vendor Y.  The Y library has I/O routines, which may
   potentially throw I/O error exceptions.  Suppose, for example, that the
   Y library contains

           extern void write (Device, Buffer) throw (IOerror);

   Now suppose that vendor Y adds networking capabilities to the Y library.
   That means that if you apply `write' to a network device, it might now
   raise a network error:

           extern void write (Device, Buffer) throw (Networkerror, IOerror);

   Now, this happens only if you're using a network device.  In that sense,
   it is strictly an upward compatible change.  Yet if exceptions are
   statically checked, there is no way to make this an upward compatible
   change to the Y library: adding Networkerror to the throw list will
   break the library from vendor X.

If you allow overloading on exception signature then the library vendor
can support both signatures, have the backwards compatible version of
the function call the new one and intercept the new exception and either
turn it into something appropriate (perhaps turn it into an IOerror or
an abort, for example).  This is certainly no worse than having
Networkerror turn into unexpected, is it?

Even if overloading were not allowed, I don't see this as any different
from the problem created when a library vendor wants to change the
signature of a function in some other way.  In fact, your same line of
reasoning could just as well be used to argue against function prototypes
in C!

Another approach would be to derive Networkerror from IOerror.  Provided
that the exception objects have been well-designed by the library vendor,
this should be a perfectly acceptable mechanism.

   That in turn means that you can't accept changes from vendors X and Y
   independently: you must first take changes from X (to accept the extra
   exception) and then from Y.  Indeed, Y cannot begin shipping a new version
   of the Y library until X has sent out new versions to all X's customers.

   In other words, static checking imposes the following required sequence:

           Y decides to add a new exception.
           Y tells all vendors whose libraries depend on Y that the
                   new exception is coming.
           All the vendors change their libraries.
           All the cusomters install the new versions.
           Finally, Y can ship the new version.

Gee, looks kind of like the sequence you get when an OS vendor makes
incompatible changes in the OS interface.  I think that this example
is overly contrived for economic reasons:  most people are going to
be extremely wary of using tools which rely overly on the tools of
yet another party in any case, regardless of what happens with exceptions.
I think that software developers will be *extremely* unhappy if a
supposedly backward compatible library change ends up causing unanticipated
runtime errors in their applications, especially when they are discovered
first by clients!  I would much rather wait to receive a totally compatible
set of libraries, down to the exception signature, and *know* what
exceptions can occur at runtime than risk having an upgrade ruin my
reputation with my clients.

   It was scenes like this that argued convincingly that checking exception
   specifications must not be done at compile time.  In effect, it would
   mean that the exceptions thrown by a function must never change once
   that function has been defined.

Just like the arguments of a function may never change once the function
has been defined?  IMHO, the ability to subclass exception objects provides
sufficient flexibility to overcome this restriction.

It seems to me, that your main argument with static checking of exception
signatures is that it would create too much work for compiler vendors
(because of the need to include exception signatures in a function's type)
and would inconvenience library vendors (because of the restrictions placed
on changing the exception signatures of functions in the API).  While these
might seem to be compelling arguments for representatives of those
interests, it is far less convincing for those of us that are concerned
with writing actual applications reliably.  After all, the total man hours
that goes into compilers and support libraries is *far* less than that
going into application development using these tools.  Why not expend the
extra time on the tools, so that the application developers can spend less
time debugging hidden library incompatibilities?

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: daniels@biles.com (Brad Daniels)
Date: Mon, 28 Feb 1994 20:32:38 GMT
Raw View
Before I proceed here, I want to say again:  In my opinion, there are two
acceptable approaches to exception signatures.  One is to have none at all
and no checking.  The other is to have full, static checking of exception
signatures with all it implies.  The current situation, where exception
signatures are checked only at run time with an incorrect exception resulting
in a usually disastrous call to unexpected(), is not safe, and makes
exception signatures a dangerous and therefore worse than useless feature.
All the rest of my discussion here should be viewed in that light.

In article <2kt2g1$eh2@travis.csd.harris.com>,
Bill Leonard <bill@ssd.csd.harris.com> wrote:
>In article <9402251715.AA22080@tdat.ElSegundoCA.NCR.COM>, swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen) writes:
>> In article <CLqyyE.75y@biles.com>, daniels@biles.com (Brad Daniels) writes:
>> |> I would arrgue that the time required to add exception signatures (or
>> |> consistent use of "const") given a compiler which can point out exactly
>> |> where the errors occur is insignificant compared to the time and
>> |> potential lost business involved in fixing a bug introduced because you
>> |> didn't use one of these features.
...
>The argument made above, though, deserves a response.  It sounds like the
>responder is arguing that every possible bug should be fixed immediately,
>right now, before any software is released.  That's a fine, high-minded
>sentiment, but hardly practical when dealing with an existing product.

I didn't say that.  What I said is that problems which can be fixed up
front using compiler-directed techniques should be fixed up front.  If the
compiler can detect a bug, it should tell you.  If it tells you about a
bug, you should fix it.  There's nothing "high-minded" or pure about that.
I'm not expecting someone to seek out every bug before releasing code, but
if the compiler can tell you about a bug you're introducing, it's plain
stupidity to ignore it.

>I don't have the option of introducing exceptions wholesale, complete, into
>my software all in one go.  It will never happen.  If that is my *only*
>choice, then they'll *never* get incorporated; I would hate to see that
>happen.  So I would prefer the option of introducing exceptions gradually.
>
>> Not when you have several hundred to several thousand *functions*
>> that have all been well tested.  At least not unless there is some way
>> to *automate* the changes.
>
>Amen.

So write a preprocessor to put "throw (...)" after every function declaration
or definition.  You can then change over as needed.  Actually, I'd prefer
to see this feature initially provided as a compiler option, but I've
discussed the details of how I feel about that to death in other recent
notes.  Actually, given GNU emacs and a few days to hack compiler output,
I think it should be feasible to fully automate putting the correct exception
signature on your functions, assuming the compiler gives sufficient info.

>> |> Propagating fixes of this sort back
>> |> through the code can actually take surprisingly little time given the
>> |> number of places you need to modify.  I've removed const qualifiers from
>> |> functions resulting in the need to propagate changes back through 40
>> |> or more other modules in the space of an hour or so with the help of
>> |> emacs' next-error feature.
>>
>> Oh, a mere 40 modules!  No wonder you think it possible, you are only
>> dealing with a small to medium sized project!
>
>Amen again!  What's more, _removing_ const is much less disruptive than
>_adding_ const, which is more comparable to adding throw signatures.
>Adding one const qualifier to an argument can propagate to _hundreds_ of
>functions.

Depends on where you add it...  Removing const is more disruptive in my
experience because I have to change every function which passes a const
argument into a function which used to require a const argument but which
now require a non-const argument.  If I add const to a function's argument
specification, I never have to change any of its callers, and can frequently
avoid changing the functions it calls.

As to the whole number of modules things, I discussed that in my earlier
response that seems to have gotten skipped over here.

>Besides the issue of time, though, there is the issue of effect.  What bugs
>were introduced by making all those changes?  When we are close to
>releasing our product, we try not to make *ANY* change that is not
>absolutely necessary.  If we make any kind of a major change, that
>necessitates starting over completely with the testing cycle, which is
>*very expensive*.

You mean a change like adding a run-time checked exception signature in
the middle of your code?  If it's statically checked, the odds of introducing
a bug are substantially reduced.

>Like I said, I think adding _some_ exceptions to my software will improve
>its quality and reliability.  That doesn't mean it's perfect, but if I
>can't even get started it will never get any better at all.  So in my
>judgment, it's better to get a little added quality now and improve it
>gradually than to never get any better at all.

Yes, but as I've been saying all along, these run-time checked exception
signatures are nothing but trouble from any standpoint.  With the current
(run-time) enforcement mechanism, they should never be used.

>> |> The scope of such changes is more manageable than one might expect.
>> |> To add exceptions gradually, though, it's probably better to start at
>> |> the high level functions and work down than to do it from the bottom
>> |> up.  When you add an exception to a low-level function, you are really
>> |> adding it to every function which uses that function as well, whether
>> |> or not the exception signatures are enforced.
>
>So what, if those functions aren't interested in that exception?  Right now
>I have lots of functions that check error returns from functions they call,
>just so they can return an error return, which their caller uses only so it
>can return an error code, etc.  All those checks would go away if the error
>return became an exception.

If the intervening exception signatures are correct, you can be completely
certain at compile time that your catch-all handler correctly deals with
all possible exceptions.  This is a very useful feature.  Also, any discip-
line which can be enforced by the compiler can be of substantial use in a large
system where multiple programmers have the opportunity to introduce new and
exciting bugs because of interaction among multiple subsystems.  If you're
going to use exceptions in a large program, there's a whole lot to be said
for static checking from the safety and maintainability standpoints.

>One of the big advantages I see with C++ exceptions over Ada-like
>exceptions is that they can pass information along.  So the low-level
>routine can generate an exception and pass along enough information to
>generate an intelligible error message at a high level (where the knowledge
>of how to present an error message resides).  Intermediate routines need to
>be involved only if (a) they need to do some cleanup, or (b) they wish to
>add to the exception information for a more informative message, or (c) the
>exception doesn't affect their operation and they can completely recover.

In general, they needn't be involved apart from having the right exception
signature.  I have discussed the benefits of a correct exception signature
elsewhere.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: chase@Think.COM (David Chase)
Date: 18 Feb 1994 20:04:31 GMT
Raw View
In article <JSS.94Feb17200309@summit.lucid.com>, jss@summit.lucid.com (Jerry Schwarz) writes:

|> Where we difer, apparently, is in whether we believe that the C++
|> exception mechansim should support programmers who want to use
|> exceptions for emergencies.

I wrote, comparing experience with Modula-3 and C++:

|> >   Note well -- in M-3 there is a "fatal" exception that any function can
|> >   raise, so the raising of an out-of-signature exception is converted into
|> >   "fatal".

|> Thereby loosing whatever information about the nature of the emergency
|> was trying to be conveyed.  I'm not a Modula-3 user, but it is my
|> understanding that when it runs out of memory it aborts rather than
|> throwing an exception (which would immediately become "fatal" in most
|> circumstances).  This illustrates the M-3 bias towards use of
|> exceptions as alternate returns.

I don't think it's exactly like that.  The Modula-3 "fatal" exception is
more or less a formalization of "how the program dies when this happens".
(Of course, I'm the guy who just one post before said that you have no
control over the wacky things people will write in your shiny new language,
but never mind that.)  If-there-is-a-debugger around, then this is something
that should pop you into it pronto.  Note that, with a sensible compiler
(and sensible compilers are available) this fatal exception will always
correspond to a compile-time warning message that the programmer decided
could safely be ignored.  For all other emergencies, an ordinary exception
is thrown, and no information is lost.

M-2+ used exceptions in an operating system, both for alternate returns,
and to deal with exceptional stuff.  In the Olivetti M-3 compiler, I used
exceptions in the back end as a backstop to deal with deficiencies in
the code generator -- I signal an "oops", and someone much further up the
chain catches it, and proceeds to the next file.  This let us write a
compiler server, among other things.

The abort-on-out-of-memory is a combination of two things -- I believe that
there was some sort of ideological opposition to throwing an exception,
plus the implementors (me among them) never quite got around to it, partly
because we were still dickering about how some of these things ought to be
dealt with.  Remember that M-3 was garbage collected, so that out-of-memory
means that you've got to "forget something" -- the recovery code is really
rather tricky.

|> No matter how "safely" you code, you always have the problem of
|> having to deal with emergencies.  If I write

|>         void f() throw(X) ;

|> then the implied "contract" for f is that it will abort in case of
|> emergency.

[did you mean "really abort" or "throw X"?]

Again, in Modula-3 "throwing fatal" is just the formalism for "abort".
It lets you deal with things on the way out (closing files, flushing
buffers, shutting down windows, releasing locks).

Non-fatal emergencies (file not found, disk is full, disk is offline,
permission denied) should be declared in the signature, as well as
non-local transfers of control.  Again, note that C++ actually allows
more flexibility here than Modula-3 because of the ability to throw
objects -- you can even package up one thrown exception and its
information include it within a rethrown exception, should you care to
do this.

|> If I write

|>         void g(X&) ;

|> then the contract is that it will throw an exception in case of
|> emergency and report the "X" result in the argument.

|> The difference is that for "f" I have to worry that I won't get
|> back control.  While "g" promises always to give back control to
|> the caller.  If I'm trying to write robust code it seems to me
|> that "g" has the more sensible contract.  If I'm just trying to
|> program without worrying about emergencies, then "f" may be
|> easier to use.

I think I hope that you have f and g backwards here -- from the point
of view of style, g throwing an exception and reporting the result
back in the argument is pretty ugly, and it is also a bit of a pain
for compiler writers (basically, in a world where exceptions may be
thrown at many places, one must be exceptionally careful about how
accesses to global variables and reference parameters are scheduled
and optimized).  This, again, was a problem for Modula-2+, because it
could expose a distinction between a compiler using pass-by-address
versus pass-by-value-result for "out" and "in/out" parameters.
There is a certain sanity to using value-result, with no update in
the event of an exception, though I realize that this is at serious
variance with C and C++ semantics.

I don't know if I've ever stated this plainly, so let me try again:

I don't think I've ever seen a decent justification for "missing signature"
equals "throws anything".  The one time where someone actually studied the
problem, with real code, written by several people with a variety of styles,
the conclusion was that "missing signature" should mean "throws nothing".  In
particular, in that exercise, they converted a body of existing code, and
found latent bugs in the process.  Static analysis of out-of-signature
exceptions is relatively easy, and update of the code is similarly relatively
easy.  Because existing libraries don't throw exceptions, it is perfectly
reasonable to say that they don't throw anything, but it is also relatively
easy to update any code that calls these libraries, guided by warning
messages from a compiler, if it is decided that those libraries should have
signatures.  Exception signatures can and should be applied to function
pointers and to (virtual) member function functions (again -- it worked for
Modula-3), with obvious rules about type matching.

Grumble.  It's one thing to leave features out because experience is lacking,
and an entirely different matter to completely ignore existing experience and
experiments.  Missing=nothing simplifies production of reliable code, and
provides more opportunities for an optimizer, and makes it easier for
maintainers to tell what is going on.  What could be wrong with that?

David Chase, speaking for myself
Thinking Machines Corp.




Author: mat@mole-end.matawan.nj.us
Date: Sat, 19 Feb 1994 04:02:47 GMT
Raw View
In article <2k0ddr$8k0@news.delphi.com>, bobkf@news.delphi.com (BOBKF@DELPHI.COM) writes:

>      ... It seems clear that w.r.t. exceptions (as in
> most programming language discussions) that there are strong and weak
> typing proponents. Weak typing advocates are likely to view exception
> signatures as superfluous but pleased that a) signatures are optional and
> b) if they are specified, they need not be enforced. Strong typing fans
> are not as well pleased, I think, as this discussion shows, because they
> really want signatures to be rigorously enforced and they want this
> enforcement done at compile time.

That's not the way I see it.

The whole point of exceptions as they are now designed is that a fault
can propagate backward and over and past levels that don't know how to
deal with it until it reaches a level that _does_ know how to deal with it.

If you write a function that insists on catching exceptions, even those
it doesn't know what to do with, you make it impossible for exceptions to
reach the handlers that _do_ know what to do with them.  And if you write
a function whose exception declaration is  throw()  you have written a
function that insists on catching exceptions whether or not it knows what
to do with them.

--
 (This man's opinions are his own.)
 From mole-end    Mark Terribile
 mat@mole-end.matawan.nj.us, Somewhere in Matawan, NJ
 (Training and consulting in C, C++, UNIX, etc.)




Author: dag@control.lth.se (Dag Bruck)
Date: 19 Feb 1994 07:38:05 GMT
Raw View
>>>>> On 18 Feb 1994 20:04:31 GMT, chase@Think.COM (David Chase) said:
> I don't think I've ever seen a decent justification for "missing signature"
> equals "throws anything".

The best argument, in my view, is that it makes the transition to
"exceptional" code easier because you don't have to re-write
everything (every function signature) at once.

> Because existing libraries don't throw exceptions, it is perfectly
> reasonable to say that they don't throw anything, ....

I don't think so.  Two examples:

 1.  Library functions may use call-backs to the application,
     "qsort" is a trivial example, container classes (e.g.,
     constructor) another.

 2.  Library routines may run out of memory, which in C++
     is typically handled with an exception.

> .... but it is also relatively
> easy to update any code that calls these libraries, guided by warning
> messages from a compiler, if it is decided that those libraries should have
> signatures.

I think you assume that (1) you have access to the source code of all
libraries, and (2) that you have the man-power to convert the
libraries and to maintain the conversion when the next release comes
out.

Maybe you suggest that I could write wrapper-routines with exception
specifications and appropriate handlers, but that is a major effort too.

> .....  Missing=nothing simplifies production of reliable code, and
> provides more opportunities for an optimizer, and makes it easier for
> maintainers to tell what is going on.  What could be wrong with that?

I agree completely, but I think your view is unrealistic.  I get the
impression that you speak from experience of a project where you had
almost full control of the source code, including libraries.

      -- Dag Bruck




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 20 Feb 1994 01:23:08 GMT
Raw View
In article <9402041709.AA10627@tdat.ElSegundoCA.NCR.COM> swf@tdat.ElSegundoCA.NCR.COM writes:
>In article <760211747snz@vsfl.demon.co.uk>, clint@vsfl.demon.co.uk writes:
>|>
>|> There's been a lot said in the recent exceptions debate about
>|> exception signatures; eg. "int fred() throw(FredDead);"
>|> guarantees only to throw the exception FredDead.
>|>
>|> My question is, when is this enforced: compile-time or run-time?
>
>I do not think it is, in practice, *possible* to do it at compile
>time.

 Its simple to do at compile time and involves no
great complexity.

>That is, it would involve an arbitrarily complex search
>tree to locate possible violations.  This would make compiles too
>expensive (ie too long).

 No. If a function declares it can throw
A, B or C, then it can only call functions that throw
A, B, C, classes derived from them, or, the calls
must be wrapped in  try blocks that only throw A, B, or C,
or classes derived from them.
>
>Think about the case where fred() calls george() which calls sam()
>which calls ... which calls libmatrix::determinant() which calls
>some private routine in the matrix library, which throws an exception.
>How is the compiler to detect this???

 It has to be declared.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 20 Feb 1994 04:34:51 -0500
Raw View
mat@mole-end.matawan.nj.us writes:

>The whole point of exceptions as they are now designed is that a fault
>can propagate backward and over and past levels that don't know how to
>deal with it until it reaches a level that _does_ know how to deal with it.

>If you write a function that insists on catching exceptions, even those
>it doesn't know what to do with, you make it impossible for exceptions to
>reach the handlers that _do_ know what to do with them.  And if you write
>a function whose exception declaration is  throw()  you have written a
>function that insists on catching exceptions whether or not it knows what
>to do with them.

Functions catch exceptions, e.g., with catch(...), that they don't know
what to do with because they *do* know what they have to do if any
exception occurs. Such functions rethrow the exceptions so they reach the
handlers that know what to do with them. Putting an exception
specification on a function like that seems like makework to me.

C++ exception specifications have the interesting property that the
function with the specification must take it seriously but callers should
not. A function specified as throw() is not a special case.

Bob Foster
Object Factory




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sun, 20 Feb 1994 12:37:21 GMT
Raw View
clint@vsfl.demon.co.uk (Ian Cameron Smith) writes:

>In article <CL07H1.9ow@cwi.nl> olaf@cwi.nl writes:
>
>> [I have an `old' edition of the ARM, and a printout of appendix A as
>> available on the net, and have been unable to verify that `g' above,
>> is declared correctly, and that
>>         void (*g() throw())();
>> would be wrong.  Does anyone know the definitive answer to this?]
>
>Definitive, no.  But the answer as I see it is that an exception
>signature is *not* part of a function's type, and so the
>exception signature is not "remembered" by the compiler when
>handling pointers to functions.

According to the ARM, the exception signature isn't considered
par of the type, but the syntax is legal.  Compilers which
emit compile-time warnings about possible exception signature
violations _will_ remember the exception signature attatched
to each function type.  The ARM does not require that compilers
do this, but on the other hand it doesn't forbid them from doing
it either, so long as they only emit warnings when the exeception
signature is violated, not errors.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sun, 20 Feb 1994 12:43:09 GMT
Raw View
chase@Think.COM (David Chase) writes:

>Note well -- in M-3 there is a "fatal" exception that any function can
>raise, so the raising of an out-of-signature exception is converted into
>"fatal".

You can do similar things in C++ with set_unexpected() and a little
creativity.

>Several of the worries about exception handling seem to be red herrings --
>existing libraries don't throw exceptions now, so if missing==none, then their
>existing signatures are fine.

Existing libraries _will_ throw exceptions once `new' starts throwing
exceptions.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: olaf@cwi.nl (Olaf Weber)
Date: Thu, 10 Feb 1994 10:22:09 GMT
Raw View
In article <MATT.94Feb8113020@physics1.berkeley.edu>, matt@physics1.berkeley.edu (Matt Austern) writes:

> In article <9403920.1926@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU
> (Fergus Henderson) writes:
[...]
>> Yes.  The advantages of static checking are well known, and static
>> checking is part of the C++ philosophy.  Why should function
>> exception signatures be any different?

> I tend to agree with this too.

> On the other hand, "enforcement" can meen two different things.
> There's no reason at all, even with the standard as defined in the
> ARM, why compilers shouldn't at least warn that a function might
> raise an exception not in its signature.

A problem is that static checking is not possible in the presence of
function pointers:

 void f() throw(int) { throw 0; }

 void (*g())() throw() { return &f; }

 void h() throw()
 {
  (*g())();  // May throw an int.
 }

Only a run-time check will now prevent `h' from throwing an int.

To make this statically checkable, it should be possible to add throw-
specifiers to function pointers, giving (using `obvious' syntax):

 void (*gg() throw())() throw(int) { return &f; }

 void h1() throw()
 {
  (*gg())(); // Compile time error/warning.
 }

 void h2() throw(int)
 {
  (*gg())(); // Ok.
 }

[I have an `old' edition of the ARM, and a printout of appendix A as
available on the net, and have been unable to verify that `g' above,
is declared correctly, and that
 void (*g() throw())();
would be wrong.  Does anyone know the definitive answer to this?]

It seems that a change of the syntax would be needed to support such a
feature.  Of course one could always create function pointers of the
correct type with a `typedef':
 typedef void (*pf)() throw(int);
 pf g() throw();
which is more readable anyway.  However, this would introduce the
first instance (that I know of) of a declaration where a typedef
_must_ be used.

Of course, exception signatures on function pointers would raise other
`interesting' questions, like ``which conversions are safe'', and
``should overloading on the exception specifiers be allowed''.  The
committee may have more important things to do.

I do wonder, however, whether these matters have already been
discussed, and if so, which conclusions were reached.

-- Olaf Weber




Author: daniels@biles.com (Brad Daniels)
Date: Thu, 10 Feb 1994 14:34:35 GMT
Raw View
In article <9402091821.AA22792@tdat.elsegundoca.ncr.com>,
Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
>In article <CKpu3v.Byu@biles.com>, daniels@biles.com (Brad Daniels) writes:
>|> >..., and restricting fred() to calling routines
>|> >with the same exception signature except in try blocks with a catch(...)
>|> >clause is too restrictive of the programmer.
>|>
>|> I think it should be done exactly that last way, and I don't think it's too
>|> restrictive.
>
>Well, let's see.  Any routine with an exception signature cannot use
>iostreams, new, any class for which the constructors have no exception
>signature (e.g. the standard String class), ...

That's hardly necessary.  A function without an exception signature should be
treated from the standpoint of static checking as throwing no exceptions.
This is certainly a reasonable approach, since a function with no exception
signature probably either never heard of exceptions or doesn't throw them
in any case.  Since most functions won't need to throw exceptions, this
approach becomes even more reasonable.

>That seems like a pretty nasty restriction to me.  It basically means that
>the *first* statement of *every* routine with a signature must be 'try',
>and the entire main body of every such routine must be in the try block.
>If you do not do this, most of the power of C++ is gone, since most everything
>you have not written yourself is forbidden.  This boils down to run-time
>checking anyway.

You are right that it would be a nasty restriction, but it need not be a
restriction, as I mentioned.  Even if it were a restriction, it wouldn't
be as bad as you seem to think.  You'd just be adding try blocks to protect
the rest of your program from rogue functions, and once you got into your
own class hierarchy, you'd be fine.

>|> If they
>|> are not enforced at compile time, they serve either as a documentation aid,
>|> in which case comments would do as well, or as a source of unexpected run-
>|> time errors, in which case they are an unsafe and undesirable feature.
>
>So, install an unexpected() handler that throws a standard exception
>you add to every signature.

What if unexpected() gets called in a library routine which doesn't have this
"standard" exception in its signature?  Sounds like infinite unexpected()
loop time to me?

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Fri, 11 Feb 1994 12:38:54 GMT
Raw View
swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen) writes:

>daniels@biles.com (Brad Daniels) writes:
>|> >..., and restricting fred() to calling routines
>|> >with the same exception signature except in try blocks with a catch(...)
>|> >clause is too restrictive of the programmer.
>|>
>|> I think it should be done exactly that last way, and I don't think it's too
>|> restrictive.
>
>Well, let's see.  Any routine with an exception signature cannot use
>iostreams, new, any class for which the constructors have no exception
>signature (e.g. the standard String class), ...
>
>That seems like a pretty nasty restriction to me.

I would presume that the standard should specify exception signatures for
ALL the functions in the standard library.  This would be quite easy;
all the standard exceptions will probably be derived from a single
base class, so the standard could specify that every function in the standard
library has the exception signature `throw (std_exception)'.

--
Fergus Henderson - fjh@munta.cs.mu.OZ.AU




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sat, 12 Feb 1994 02:41:41 GMT
Raw View
olaf@cwi.nl (Olaf Weber) writes:

>A problem is that static checking is not possible in the presence of
>function pointers:
[...]
>To make this statically checkable, it should be possible to add throw-
>specifiers to function pointers
[...]
>It seems that a change of the syntax would be needed to support such a
>feature.

I don't think so.  The grammar in the ARM is incomplete - it defines
`exception-specification' as an non-terminal in the grammar, but it
isn't referenced anywhere.  (The same is true of all the new grammar
items introduced by templates and exceptions.)  However, the ARM does
say that "An exception-specification can be used as a suffix of a
function declarator".  Note that it refers to function _declators_,
not function _declarations_.  This indicates that the production

 declarator:
  declarator ( argument-declaration-list ) cv-qualifier-list[opt]

in the grammar should be replaced with

 declarator:
  declarator ( argument-declaration-list ) cv-qualifier-list[opt]
   exception-specification[opt]

and that exception-specifications therefore _can_ be used for function
pointers.

>Of course, exception signatures on function pointers would raise other
>`interesting' questions, like ``which conversions are safe'', and
>``should overloading on the exception specifiers be allowed''.  The
>committee may have more important things to do.

The answers to these questions are straightforward:

A pointer to function throwing X may be converted to a pointer to
function throwing Y only if a Y is at least as permissive as X, i.e.
only if as function throwing X cannot throw any object that could not
be thrown by a function throwing Y, i.e. only if for every type in X,
that type or a base class of that type is a member of Y.

Overloading on exception specifiers should not be allowed.

>I do wonder, however, whether these matters have already been
>discussed, and if so, which conclusions were reached.

The current discussion on static checking notwithstanding, I
suspect that the committee is likely to leave static checking
of exception signatures as purely optional; compilers would
be allowed to issue warnings, but not errors.

--
Fergus Henderson - fjh@munta.cs.mu.OZ.AU




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sat, 12 Feb 1994 02:51:35 GMT
Raw View
daniels@biles.com (Brad Daniels) writes:

>A function without an exception signature should be
>treated from the standpoint of static checking as throwing no exceptions.

This would open up a huge hole in the static checking.

>This is certainly a reasonable approach, since a function with no exception
>signature probably either never heard of exceptions or doesn't throw them
>in any case.  Since most functions won't need to throw exceptions, this
>approach becomes even more reasonable.

I suspect that the vast majority of functions will call something
that could throw an out of memory exception.

>>So, install an unexpected() handler that throws a standard exception
>>you add to every signature.
>
>What if unexpected() gets called in a library routine which doesn't have this
>"standard" exception in its signature?  Sounds like infinite unexpected()
>loop time to me?

Unfortunately, this is not at all clear from the main text of the ARM.
But the commentary makes it clear that exceptions thrown from
unexpected() are not subject to the exception signature check.
See the example (ARM 15.6.2, page 365)

 void pass_through() { throw; }
 ... set_unexpected(&pass_through); ...

which effectively disables exception signature checks.

--
Fergus Henderson - fjh@munta.cs.mu.OZ.AU




Author: clint@vsfl.demon.co.uk (Ian Cameron Smith)
Date: Mon, 14 Feb 1994 11:18:30 +0000
Raw View
In article <9402091821.AA22792@tdat.ElSegundoCA.NCR.COM> swf@tdat.ElSegundoCA.NCR.COM writes:

> In article <CKpu3v.Byu@biles.com>, daniels@biles.com (Brad Daniels) writes:
> |> >..., and restricting fred() to calling routines
> |> >with the same exception signature except in try blocks with a catch(...)
> |> >clause is too restrictive of the programmer.
> |>
> |> I think it should be done exactly that last way, and I don't think it's too
> |> restrictive.
>
> Well, let's see.  Any routine with an exception signature cannot use
> iostreams, new, any class for which the constructors have no exception
> signature (e.g. the standard String class), ...
>
> That seems like a pretty nasty restriction to me.  It basically means that
> the *first* statement of *every* routine with a signature must be 'try',
> and the entire main body of every such routine must be in the try block.

Not true.  You simply add xmesg to all your throw signatures (I
believe this is right):

    void DiskRead(...) throw (DiskError, xmesg)

allowing you to throw "standard exceptions" as well as DiskError,
without coding an explicit try block in every routine.

As for iostreams, etc., I do not believe they raise exceptions;
if they are changed to do so, well, then, they become different
iostreams.  In any case, I would hope that the standard library
routines would use exception signatures and a sensible hierarchy
of exceptions, derived from a common base (like xmesg), in which
case DiskRead as declared above still works.
--
Ian Cameron Smith                clint@vsfl.demon.co.uk
Principal Software Engineer      +44 (0)425 474484
Virtual Software Factory LTD
"Don't quote me"
  -- me




Author: clint@vsfl.demon.co.uk (Ian Cameron Smith)
Date: Mon, 14 Feb 1994 11:23:00 +0000
Raw View
In article <CL07H1.9ow@cwi.nl> olaf@cwi.nl writes:

> A problem is that static checking is not possible in the presence of
> function pointers:

[ ... stuff about compile-time checking of exception signatures
  on function pointers ... ]

> [I have an `old' edition of the ARM, and a printout of appendix A as
> available on the net, and have been unable to verify that `g' above,
> is declared correctly, and that
>         void (*g() throw())();
> would be wrong.  Does anyone know the definitive answer to this?]

Definitive, no.  But the answer as I see it is that an exception
signature is *not* part of a function's type, and so the
exception signature is not "remembered" by the compiler when
handling pointers to functions.  Maybe it should be part of the
type?  But at the moment, I believe that run-time checking is
the only answer to this example.  (For the record, I still
think that compile-time checking would be a valuable asset that
would handle most cases.)
--
Ian Cameron Smith                clint@vsfl.demon.co.uk
Principal Software Engineer      +44 (0)425 474484
Virtual Software Factory LTD

"Go ahead, punk, make my day"




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Tue, 8 Feb 1994 09:34:48 GMT
Raw View
daniels@biles.com (Brad Daniels) writes:

>Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
>...
>>Think about the case where fred() calls george() which calls sam()
>>which calls ... which calls libmatrix::determinant() which calls
>>some private routine in the matrix library, which throws an exception.
>>How is the compiler to detect this???  Searching the entire call tree
>>is prohibitively expensive, and restricting fred() to calling routines
>>with the same exception signature except in try blocks with a catch(...)
>>clause is too restrictive of the programmer.
>...
>
>I think it should be done exactly that last way, and I don't think it's too
>restrictive.

I agree.  If compile-time checking of exception signatures is too
restrictive, then you shouldn't be using them, because when
libmatrix::determinant() throws an exception, your program will crash
with a call to unexpected().

>If they
>are not enforced at compile time, they serve either as a documentation aid,
>in which case comments would do as well, or as a source of unexpected run-
>time errors, in which case they are an unsafe and undesirable feature.

Yes.  The advantages of static checking are well known, and static
checking is part of the C++ philosophy.  Why should function exception
signatures be any different?

--
Fergus Henderson - fjh@munta.cs.mu.OZ.AU




Author: matt@physics1.berkeley.edu (Matt Austern)
Date: 08 Feb 1994 19:30:20 GMT
Raw View
In article <9403920.1926@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:

> >If they
> >are not enforced at compile time, they serve either as a documentation aid,
> >in which case comments would do as well, or as a source of unexpected run-
> >time errors, in which case they are an unsafe and undesirable feature.
>
> Yes.  The advantages of static checking are well known, and static
> checking is part of the C++ philosophy.  Why should function exception
> signatures be any different?

I tend to agree with this too.

On the other hand, "enforcement" can meen two different things.
There's no reason at all, even with the standard as defined in the
ARM, why compilers shouldn't at least warn that a function might raise
an exception not in its signature.
--
Matthew Austern                       Never express yourself more clearly
matt@physics.berkeley.edu             than you think.    ---N. Bohr




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Wed, 9 Feb 94 10:21:57 PST
Raw View
In article <CKpu3v.Byu@biles.com>, daniels@biles.com (Brad Daniels) writes:
|> >..., and restricting fred() to calling routines
|> >with the same exception signature except in try blocks with a catch(...)
|> >clause is too restrictive of the programmer.
|>
|> I think it should be done exactly that last way, and I don't think it's too
|> restrictive.

Well, let's see.  Any routine with an exception signature cannot use
iostreams, new, any class for which the constructors have no exception
signature (e.g. the standard String class), ...

That seems like a pretty nasty restriction to me.  It basically means that
the *first* statement of *every* routine with a signature must be 'try',
and the entire main body of every such routine must be in the try block.
If you do not do this, most of the power of C++ is gone, since most everything
you have not written yourself is forbidden.  This boils down to run-time
checking anyway.

Well, as it stands, the standard simply says that the compiler will do that
*for* you.  That way you do not have to remember to do it yourself.  If you
do not like calling 'unexpected()' for this case, you can always put in
your own try block around the whole routine body anyway, and handle
unexpected errors any way you like.

|> If exception signatures are to be part of the language, they should be
|> enforced by requiring every routine to conform to its signature at compile
|> time (by only allowing calls to functions with the same signature unless the
|> call is within a try block which catches all non-conforming exceptions and
|> prohibiting them from throwing exceptions outside their signatures.)

I think the last *is* in the proposal.

|> If they
|> are not enforced at compile time, they serve either as a documentation aid,
|> in which case comments would do as well, or as a source of unexpected run-
|> time errors, in which case they are an unsafe and undesirable feature.

So, install an unexpected() handler that throws a standard exception
you add to every signature.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Wed, 9 Feb 94 10:30:06 PST
Raw View
In article <760637802snz@vsfl.demon.co.uk>, clint@vsfl.demon.co.uk write:
|>
|> I disagree with this part.  I mean, maybe I misunderstand
|> here, but what are exception signatures *for*?  I thought the
|> whole idea is that a routine can guarantee *not* to call routines
|> with incompatible exception signatures except in try blocks.

Nope.  It is to guarentee that a function never effectively *throws*
any exception not in the signature.  It has the effect of implicitly
wrapping the entire function body in:

 try
 {
  // function body here
 }
 catch(...)
 {
  unexpected();
 }

That's *all* it does.

|> If you're worried about maintainability, then I *think* I
|> addressed this in my latest post to "Exceptions: are they a Bad
|> Thing" in comp.lang.c++.  ...

No, I am worried about the scope of the source code changes it will
require to existing source code to allow the use of exception
signatures *and* continue to use existing 3rd party libraries
from within the signature guarded code.

If this were being put into a *new* language, with no existing
source base, I would probably agree with you.  In that case there
is no penalty for your scheme of restricting unguarded calls to
functions that have subset exception signatures, since there is
no existing set of calls in existing functions to be maintained.

|> Hum.  But anyway... aside from what's *right*, does anyone know
|> what ANSI are proposing at the moment?  ( :-) )

What I said above - the implicit try - catch;

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: jss@summit.lucid.com (Jerry Schwarz)
Date: 15 Feb 1994 00:28:40 GMT
Raw View
In article <761224710snz@vsfl.demon.co.uk> clint@vsfl.demon.co.uk (Ian Cameron Smith) writes:

>   As for iostreams, etc., I do not believe they raise exceptions;

The iostream operations themselves (by default) don't throw
exceptions.  But saying that doesn't resolve all questions.  In
particular how should they behave if the streambuf virtuals throw
exceptions?  Remember that the streambuf virtuals are in general user
supplied code.  The situation is quite complicated, and many options
were considered.  The current proposal is

        If a streambuf virtual throws an exception, this must be
        caught by the iostream operation and "badbit" set in the error
        state.  If the "exception state" indicates that setting
        bad bit should throw an exception, then the original
        exception is rethrown.

Other possibilitites were considered such as

        a) Using throw() signatures to enforce a rule that
           streambuf's only throw ios::failure.

        b) Rather than rethrowing the original exception
           "translating" it to an ios::failure.

        c) Only catching ios::failures (and letting other exceptions
           just propogate).


I believe that the resolution outlined above is most consistent with
the notion that exceptions are "truly exceptional" events.  In
particular, any attempt to insist that only ios::failures could be
thrown by iostream operations seems wrong to me.  Little would be
gained, and the specific information contained in the originally
thrown exception could be lost.


  -- Jerry Schwarz(jss@lucid.com)










Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Mon, 14 Feb 94 17:36:22 PST
Raw View
In article <CL0J5o.GIp@biles.com>, daniels@biles.com (Brad Daniels) wrote:
|> In article <9402091821.AA22792@tdat.elsegundoca.ncr.com>,
|> Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
|> >
|> >Well, let's see.  Any routine with an exception signature cannot use
|> >iostreams, new, any class for which the constructors have no exception
|> >signature (e.g. the standard String class), ...
|>
|> That's hardly necessary.  A function without an exception signature should be
|> treated from the standpoint of static checking as throwing no exceptions.
|> This is certainly a reasonable approach, since a function with no exception
|> signature probably either never heard of exceptions or doesn't throw them
|> in any case.

The problem with this is that such a function may call a function in some
library that has been changed to throw exceptions.  This results in the
no-signature function effectively throwing an exception.

The only *safe* assumption is that a function with no exception signature
may throw *any* exception.   The ARM proposal makes this assumption.

|> >That seems like a pretty nasty restriction to me.  ...
|>
|> You are right that it would be a nasty restriction, but it need not be a
|> restriction, as I mentioned.  Even if it were a restriction, it wouldn't
|> be as bad as you seem to think.  You'd just be adding try blocks to protect
|> the rest of your program from rogue functions, and once you got into your
|> own class hierarchy, you'd be fine.

But, as things stand, the compiler is providing those additional try
blocks *for* you.  Why be required to something error prone like
add new try block to hundreds of lines of code as you convert to
exeptions when the compiler can do it for you?

If it were necessary to do this sort of thing to convert existing
code to use exceptions signatures, few people will do so.  The result
will be that exceptions signatures remain a rarity, rather than the
norm.

|>
|> What if unexpected() gets called in a library routine which doesn't have this
|> "standard" exception in its signature?  Sounds like infinite unexpected()
|> loop time to me?

I don't know for sure.  I think the ARM says something about this.

Also, a properly written library with excpetion signautres should be
doing much the same thing.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: daniels@biles.com (Brad Daniels)
Date: Tue, 15 Feb 1994 22:04:18 GMT
Raw View
In article <9402150136.AA03450@tdat.elsegundoca.ncr.com>,
Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
>In article <CL0J5o.GIp@biles.com>, daniels@biles.com (Brad Daniels) wrote:
>|> In article <9402091821.AA22792@tdat.elsegundoca.ncr.com>,
>|> Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
>|> >
>|> >Well, let's see.  Any routine with an exception signature cannot use
>|> >iostreams, new, any class for which the constructors have no exception
>|> >signature (e.g. the standard String class), ...
>|>
>|> That's hardly necessary.  A function without an exception signature should be
>|> treated from the standpoint of static checking as throwing no exceptions.
>|> This is certainly a reasonable approach, since a function with no exception
>|> signature probably either never heard of exceptions or doesn't throw them
>|> in any case.
>
>The problem with this is that such a function may call a function in some
>library that has been changed to throw exceptions.  This results in the
>no-signature function effectively throwing an exception.

No...  It results in the function with no signature failing to compile because
the library routines it uses changed incompatibly.  A nuisance, yes, but it
is perfectly sensible behavior.

>The only *safe* assumption is that a function with no exception signature
>may throw *any* exception.   The ARM proposal makes this assumption.

I realize it makes that assumption, but it's not the only safe assumption,
and IMO it's a really bad one.

>|> >That seems like a pretty nasty restriction to me.  ...
>|>
>|> You are right that it would be a nasty restriction, but it need not be a
>|> restriction, as I mentioned.  Even if it were a restriction, it wouldn't
>|> be as bad as you seem to think.  You'd just be adding try blocks to protect
>|> the rest of your program from rogue functions, and once you got into your
>|> own class hierarchy, you'd be fine.
>
>But, as things stand, the compiler is providing those additional try
>blocks *for* you.  Why be required to something error prone like
>add new try block to hundreds of lines of code as you convert to
>exeptions when the compiler can do it for you?

Que?  Error prone?  It's very  unlikely to be "error prone".  You
will get errors at compile time until you fix them all, at which point
you should get no more errors.  The alternative is to get unexpected
exceptions thrown when some library changes, with no opportunity to
detect the change.

>If it were necessary to do this sort of thing to convert existing
>code to use exceptions signatures, few people will do so.  The result
>will be that exceptions signatures remain a rarity, rather than the
>norm.

Exceptions are difficult to retrofit into existing code reliably.  With
statically enforced exception signatures, the compiler will point out
everyplace where you need to deal with exceptions.  With dynamically
enforced exceptions, you have to go through your code and pray you hit
everyplace.  Guess which I'd rather do.  Maybe you'd like to see overloading
on exception signatures?  That would address the issue, but the semantics
would be a major pain to specify.

>|>
>|> What if unexpected() gets called in a library routine which doesn't have this
>|> "standard" exception in its signature?  Sounds like infinite unexpected()
>|> loop time to me?
>
>I don't know for sure.  I think the ARM says something about this.
>
>Also, a properly written library with excpetion signautres should be
>doing much the same thing.

If exception signatures are rigidly enforced and functions without exception
signatures are not allowed to throw any exceptions, unexpected() becomes an
anachronism.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 16 Feb 1994 04:33:48 -0500
Raw View
Brad Daniels wrote, in part:

>If exception signatures are rigidly enforced and functions without exception
>signatures are not allowed to throw any exceptions, unexpected() becomes an
>anachronism.

(I would have quoted more, but for this crappy Delphi editor)

The writer and others seem to feel that unless a program is prepared to
deal specifically with every possible exception, it is somehow ill-formed.
On the contrary, most exception recovery in my experience is not sensitive
to the nature of the exception, but to the fact that some kind of
exception has occurred. Higher-level modules - except those which talk to
users - seem usually to be more concerned with responding to the fact that
a lower-level module has "failed" than with the specific nature of the
failure.

Function signatures as they are defined in the draft standard are not a
contract with the caller, but merely tell what exceptions the function
writer expected. As such, they are useful comments, rather like the lists
of error codes in operating system manuals. Perhaps I'm not as much a fan
of strong typing as some, but the current definition seems workable.

Bob Foster
Object Factory




Author: daniels@biles.com (Brad Daniels)
Date: Wed, 16 Feb 1994 15:22:43 GMT
Raw View
In article <2jsp9s$q0c@news.delphi.com>,
BOBKF@DELPHI.COM <bobkf@news.delphi.com> wrote:
...
>Function signatures as they are defined in the draft standard are not a
>contract with the caller, but merely tell what exceptions the function
>writer expected. As such, they are useful comments, rather like the lists
>of error codes in operating system manuals. Perhaps I'm not as much a fan
>of strong typing as some, but the current definition seems workable.

The above is not accurate.  If they were not enforced at all, they would
simply be comments, and a waste of syntax.  As proposed, these signatures
are supposed to be enforced *at run time*.  This means you program can run
fine for weeks until an exception occurs that's not in your signature, at
which point your program will do an unexpected() and die instead of passing
the exception on up the chain to someone who might know what to do with it
(which would be the effect of having unenforced signatures).

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: chase@Think.COM (David Chase)
Date: 16 Feb 1994 15:48:08 GMT
Raw View
|> Brad Daniels wrote, in part:
|>
|> >If exception signatures are rigidly enforced and functions without exception
|> >signatures are not allowed to throw any exceptions, unexpected() becomes an
|> >anachronism.

bobkf@news.delphi.com (BOBKF@DELPHI.COM) writes:
|> The writer and others seem to feel that unless a program is prepared to
|> deal specifically with every possible exception, it is somehow ill-formed.
|> On the contrary, most exception recovery in my experience is not sensitive
|> to the nature of the exception, but to the fact that some kind of
|> exception has occurred. Higher-level modules - except those which talk to
|> users - seem usually to be more concerned with responding to the fact that
|> a lower-level module has "failed" than with the specific nature of the
|> failure.

I've seen them used differently, in Modula-2+ and Modula-3.  I suspect that this
has something to do with those languages being mostly "safe".  The worst
breakage usually encountered is loss of resources.  People also use them for
non-local transfer of control (never mind what anyone *says* about how exceptions
are supposed to be used, they *can* be used like this, and they have been used
like this in the past in other languages.) Again, consider the Modula-3
experience -- this language started out with the default (missing) signature
meaning "throws anything", but based on a study of Real Code (some Acorn OS and
library) carried out with a modified compiler (default changed to "throws none")
they decided to change the language.  My recollection was that the conversion of
the existing body of code was not onerous, and that several latent bugs were
uncovered.

Note well -- in M-3 there is a "fatal" exception that any function can
raise, so the raising of an out-of-signature exception is converted into
"fatal".

Several of the worries about exception handling seem to be red herrings --
existing libraries don't throw exceptions now, so if missing==none, then their
existing signatures are fine.  In cases where either (a) function pointers are
passed in to a function or (b) virtual member functions are overridden, the
signature of the function pointer or VMF specifies what exceptions may be thrown,
and the function filled into that slot must conform to that interface.  The fact
that (unlike M-3) you can have a hierarchy of exception types means that this is
actually quite flexible; if you simply must have your own sort of exception, then
you just subclass it from the defined one (and, I hate to say it, but you could
even use multiple inheritance in this situation, gag -- handler matching is going
to be as slow as Christmas, but so it goes).

David Chase, speaking for myself
Thinking Machines Corp.




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Wed, 16 Feb 94 09:01:15 PST
Raw View
In article <CLADB8.5sq@biles.com>, daniels@biles.com (Brad Daniels) writes:
|> In article <9402150136.AA03450@tdat.elsegundoca.ncr.com>,
|> Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
|> >The problem with this is that such a function may call a function in some
|> >library that has been changed to throw exceptions.  This results in the
|> >no-signature function effectively throwing an exception.
|>
|> No...  It results in the function with no signature failing to compile because
|> the library routines it uses changed incompatibly.

Considering that the no-signature function may itself be a library function
(in a *different* library), for which there is no source code, no attempt
need be even *made* to compile it.

Are you saying that the *linker* ought to  fail to link it now?
[That is, must type-safe linking be extended to exception
signatures?].

|> >The only *safe* assumption is that a function with no exception signature
|> >may throw *any* exception.   The ARM proposal makes this assumption.
|>
|> I realize it makes that assumption, but it's not the only safe assumption,
|> and IMO it's a really bad one.

It would not be *if* there were not a large base of existing code.

It is to be compatible with existing code that this assumption was put in.
[That is, it was chosen because it does not change the meaning of
existing code].

|> >But, as things stand, the compiler is providing those additional try
|> >blocks *for* you.  Why be required to something error prone like
|> >add new try block to hundreds of lines of code as you convert to
|> >exeptions when the compiler can do it for you?
|>
|> Que?  Error prone?  It's very  unlikely to be "error prone".

I can state from experience (recent) that adding routine changes of
that sort to several hundred files *is* error prone.

It is easy to forget a file.  It is easy to make many other small errors,
not all of which get caught by the compiler.

And I am talking about a situation where the main changes *were*
checked by the compiler.  (I was retrofitting K&R C code with ANSI
style function protoypes and careful type checking (C++ style)).

I *still* ended up with three coding errors that passed the compiler.


If I had to do the retrofit of the sort you want to require,
I would most certainly recommend *against* it on the grounds
of too high a risk of breaking something.

I.e., I would simply write a script that added 'throw ...' or
its equivalent to every function signature, and forget about
doing a real retrofit.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: daniels@biles.com (Brad Daniels)
Date: Wed, 16 Feb 1994 20:06:33 GMT
Raw View
In article <9402161701.AA00304@tdat.elsegundoca.ncr.com>,
Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
>In article <CLADB8.5sq@biles.com>, daniels@biles.com (Brad Daniels) writes:
>|> In article <9402150136.AA03450@tdat.elsegundoca.ncr.com>,
>|> Stan Friesen <swf@tdat.ElSegundoCA.NCR.COM> wrote:
>|> >The problem with this is that such a function may call a function in some
>|> >library that has been changed to throw exceptions.  This results in the
>|> >no-signature function effectively throwing an exception.
>|>
>|> No...  It results in the function with no signature failing to compile because
>|> the library routines it uses changed incompatibly.
>
>Considering that the no-signature function may itself be a library function
>(in a *different* library), for which there is no source code, no attempt
>need be even *made* to compile it.

That is a spurious argument.  Adding new kinds of exceptions to a function
constitutes an incompatible change in a library.  If your library calls a
function which suddenly and unexpectedly starts throwing exceptions, the
library is broken because something it uses has changed incompatibly.  It's
the same as if a C function in a library added a new argument, or a C++
function decided to interpret arguments in a different and incompatible way.
If throw() signatures are enforced, when somebody recompiles the library,
they will at least be able to find out that their code has been broken by
a change in a library they use.

>Are you saying that the *linker* ought to  fail to link it now?
>[That is, must type-safe linking be extended to exception
>signatures?].

I'm not saying that, but I don't think it would be a bad thing if it were.

>|> >The only *safe* assumption is that a function with no exception signature
>|> >may throw *any* exception.   The ARM proposal makes this assumption.
>|>
>|> I realize it makes that assumption, but it's not the only safe assumption,
>|> and IMO it's a really bad one.
>
>It would not be *if* there were not a large base of existing code.
>
>It is to be compatible with existing code that this assumption was put in.
>[That is, it was chosen because it does not change the meaning of
>existing code].

Oh, nonsense.  I realize that may have been the original reasoning, but it's
still nonsense.  Existing code will be broken by adding exceptions.  End of
story.  The choice is whether to wait till run time to tell people their
code has become broken, or to tell them when they try to recompile after
adding exceptions.

>|> >But, as things stand, the compiler is providing those additional try
>|> >blocks *for* you.  Why be required to something error prone like
>|> >add new try block to hundreds of lines of code as you convert to
>|> >exeptions when the compiler can do it for you?
>|>
>|> Que?  Error prone?  It's very  unlikely to be "error prone".
>
>I can state from experience (recent) that adding routine changes of
>that sort to several hundred files *is* error prone.
>
>It is easy to forget a file.  It is easy to make many other small errors,
>not all of which get caught by the compiler.
>
>And I am talking about a situation where the main changes *were*
>checked by the compiler.  (I was retrofitting K&R C code with ANSI
>style function protoypes and careful type checking (C++ style)).
>
>I *still* ended up with three coding errors that passed the compiler.

... And assume the compiler hadn't checked any of your changes?  How many
coding errors would have crept through then?  I don't know the exact number,
but I'm sure it would have been substantially more than three.  Failing to
handle all exceptions which might be thrown by a function you call is a
programming error.  If the compiler doesn't at least warn you about it,
you are guaranteed to miss a number of places where you should have a try
block.

>If I had to do the retrofit of the sort you want to require,
>I would most certainly recommend *against* it on the grounds
>of too high a risk of breaking something.
>
>I.e., I would simply write a script that added 'throw ...' or
>its equivalent to every function signature, and forget about
>doing a real retrofit.

Again, it's a matter of breaking it one way or breaking it another way.
If you put throw ... everywhere, you've basically said you don't care
who throws what, assume everybody does the right thing, and crash only
if an exception doesn't get caught by anyone.  This is not horrendously
bad behavior, though it is not as airtight as one might like.  If you put
correct throw signatures everywhere and checking happens at run time as you
advocate, the second somebody throws an exception not in their signature,
your entire program will crash and burn horribly.  In my opinion (call me
foolish if you wish), this is a Bad Thing.  If the cheking happens at compile
time, you will be told immediately whether or not there is a problem, and
you will be able to take appropriate action.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Thu, 17 Feb 1994 04:35:38 GMT
Raw View
bobkf@news.delphi.com (BOBKF@DELPHI.COM) writes:

>Function signatures as they are defined in the draft standard are not a
>contract with the caller, but merely tell what exceptions the function
>writer expected. As such, they are useful comments, rather like the lists
>of error codes in operating system manuals.

But they _are_ a contract with the caller.  If the contract is violated,
then unexpected() will be called, and the default behaviour of unexpected()
is to abort the program.  For "comments", they have a rather nasty semantic
side-effect if you get them wrong.

Now it _is_ possible to do modify this behaviour, so that they
are treated as comments:

 void pass_through() { throw; }

 //--- ensure that exception signatures will be ignored
 void disable_exception_signature_checking() {
  set_unexpected(pass_through);
 }

But I don't think that this is how exception signatures were designed
to be used.  I'm not convinced that it's a good idea to subvert
run-time checking in this way.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 17 Feb 1994 00:55:40 -0500
Raw View
daniels@biles.com (Brad Daniels) writes:

>In article <2jsp9s$q0c@news.delphi.com>,
>BOBKF@DELPHI.COM <bobkf@news.delphi.com> wrote:
>...
>>Function signatures...are not a contract with the caller...they are
>>useful comments...

>The above is not accurate.  If they were not enforced at all, they would
>simply be comments, and a waste of syntax.  As proposed, these signatures
>are supposed to be enforced *at run time*.  This means you program can run
>fine for weeks until an exception occurs that's not in your signature, at
>which point your program will do an unexpected() and die instead of passing
>the exception on up the chain to someone who might know what to do with it
>(which would be the effect of having unenforced signatures).

Well, if you choose to use signatures on functions which depend on
library code whose definition is out of (your) control *and* you choose
not to rethrow exceptions in unexpected(), then you've got a problem. It
would seem a prudent person wouldn't make those choices.

The fact that you can easily arrange to have unexpected() rethrow
arbitrary exceptions was the inspiration for comparing
exception-specifications to comments. It was not meant literally, but
should be taken with the same grain of salt that one might consume when
reading exception-specifications in library code.

I still believe that the exception feature as currently specified is a
reasonable compromise and will be useful without being overly (and
experimentally) restrictive.

Bob Foster
Object Factory




Author: daniels@biles.com (Brad Daniels)
Date: Thu, 17 Feb 1994 14:47:03 GMT
Raw View
In article <2jv0ss$qpi@news.delphi.com>,
BOBKF@DELPHI.COM <bobkf@news.delphi.com> wrote:
>daniels@biles.com (Brad Daniels) writes:
>
>>In article <2jsp9s$q0c@news.delphi.com>,
>>BOBKF@DELPHI.COM <bobkf@news.delphi.com> wrote:
>>...
>>>Function signatures...are not a contract with the caller...they are
>>>useful comments...
>
>>The above is not accurate.  If they were not enforced at all, they would
>>simply be comments, and a waste of syntax.  As proposed, these signatures
>>are supposed to be enforced *at run time*.  This means you program can run
>>fine for weeks until an exception occurs that's not in your signature, at
>>which point your program will do an unexpected() and die instead of passing
>>the exception on up the chain to someone who might know what to do with it
>>(which would be the effect of having unenforced signatures).
>
>Well, if you choose to use signatures on functions which depend on
>library code whose definition is out of (your) control *and* you choose
>not to rethrow exceptions in unexpected(), then you've got a problem. It
>would seem a prudent person wouldn't make those choices.
>
>The fact that you can easily arrange to have unexpected() rethrow
>arbitrary exceptions was the inspiration for comparing
>exception-specifications to comments. It was not meant literally, but
>should be taken with the same grain of salt that one might consume when
>reading exception-specifications in library code.

I was going to respond to this, but I just noticed Fergus Henderson's
response to your earlier message, which says effectively what I was about
to say.

>I still believe that the exception feature as currently specified is a
>reasonable compromise and will be useful without being overly (and
>experimentally) restrictive.

Sigh.  Anyone using exceptions (or much of C++, for that matter) is the knowing
or unknowing subject of a series of experiments.  Restrictions can always be
removed if they cause problems.  Adding them in is a real pain because of the
"it will break code" argument.  If I'm going to be part of an experiment in
syntax, I want to be apprised of the effects of the experimental syntax on my
code before my program gets terminal exceptionitis and dies.

Exceptions in general make me nervous because I can't be sure who's going to
throw what, or when.  I have held off using them partially for that reason.
Exception signatures promise a way to guarantee that I would know exactly what
exceptions might get thrown at me, meaning I wouldn't have to worry about
unknown effects.  Unfortunately, with run-time semantics, exception signatures
increase, rather than decrease, the level of uncertainty and the danger posed
by exception abuse in my code.

- Brad
-------------------------------------------------------------------------------
+ Brad Daniels                  | "There's only one sweeping generalization   +
+ Biles and Associates          |  which is completely true, and this is it." +
+ These are my views, not B&A's |                              - Me (1994)    +
-------------------------------------------------------------------------------




Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 17 Feb 1994 13:35:39 -0500
Raw View
Fergus Henderson - fjh@munta.cs.mu.oz.au writes:
[example of using set_unexpected() to set up a function that throw()s]

>But I don't think that this is how exception signatures were designed
>to be used.  I'm not convinced that it's a good idea to subvert
>run-time checking in this way.

Well, really, if they were not intended to work this way, this shouldn't
appear as the example in the draft standard. It seems that this is
exactly how exceptions were intended to be used.

...at least, by some members of the committee (who are welcome to clarify
this discussion at any time). It seems clear that w.r.t. exceptions (as in
most programming language discussions) that there are strong and weak
typing proponents. Weak typing advocates are likely to view exception
signatures as superfluous but pleased that a) signatures are optional and
b) if they are specified, they need not be enforced. Strong typing fans
are not as well pleased, I think, as this discussion shows, because they
really want signatures to be rigorously enforced and they want this
enforcement done at compile time.

There is merit to both sides, but unless the committee can be persuaded to
throw in with one or the other, it is another case of we asked for a horse
but got a camel. Does this mean we can't go for a ride?

Bob Foster
Object Factory




Author: barmar@think.com (Barry Margolin)
Date: 17 Feb 1994 22:52:23 GMT
Raw View
In article <CLC2Iy.M6F@biles.com> daniels@biles.com (Brad Daniels) writes:
>That is a spurious argument.  Adding new kinds of exceptions to a function
>constitutes an incompatible change in a library.  If your library calls a
>function which suddenly and unexpectedly starts throwing exceptions, the
>library is broken because something it uses has changed incompatibly.

It seems that the dilemma X3J16 faced was that they wanted to make such an
incompatible change to the standard C++ libraries.  If they also required
throw signatures to be enforced at compile time, this would mean that just
about everything would need to be recompiled when a vendor incorporated the
exceptions into its library.

The current specification appears to be a compromise that allows these
changes, without causing a domino effect of recompilation.
--
Barry Margolin
System Manager, Thinking Machines Corp.

barmar@think.com          {uunet,harvard}!think!barmar




Author: jss@summit.lucid.com (Jerry Schwarz)
Date: 18 Feb 1994 04:03:08 GMT
Raw View
In article <2jtf7oINN62a@early-bird.think.com> chase@Think.COM (David Chase) writes:
>   I've seen them used differently, in Modula-2+ and Modula-3.  I suspect that this
>   has something to do with those languages being mostly "safe".  The worst
>   breakage usually encountered is loss of resources.  People also use them for
>   non-local transfer of control (never mind what anyone *says* about how exceptions
>   are supposed to be used, they *can* be used like this, and they have been used
>   like this in the past in other languages.)

I'm in basic agreement with this: if you're going to use exceptions
for what David calls non-local transfer of control and I call
"alternate returns" then you must have exception signatures on all
your functions.  Otherwise you risk being confused by a function that
throws an exception that looks like one you think means something.
For example if you have an array class that throws a "subscript range
error" and you write
        try {
                for( int i = 0 ; ; ++i ) {
                        int j = A[i] ; // throws subscript to get us
                                       // out of loop
                        f(j) ;
        } catch (SubscriptRangeError) { }

Then you want to be sure that f doesn't throw SubscriptRangeError.

I believe the reverse is also true. If the language requires exception
signatures then it is intended that exceptions be used for alternate
returns and not "emergencies". (An emergency is a situation where the
only sensible alternatives are to abort or throw an exception)

Where we difer, apparently, is in whether we believe that the C++
exception mechansim should support programmers who want to use
exceptions for emergencies.

My sense (having been hanging around when Stroustrup and Koenig
designed the feature and having beeen present when it was accepted by
the committee) is that dealing with emergencies was at least
significant a motivator (maybe more) in the introduction of exceptions
as was the possibility of using them as alternate returns.


>   Again, consider the Modula-3
>   experience -- this language started out with the default (missing) signature
>   meaning "throws anything", but based on a study of Real Code (some Acorn OS and
>   library) carried out with a modified compiler (default changed to "throws none")
>   they decided to change the language.  My recollection was that the conversion of
>   the existing body of code was not onerous, and that several latent bugs were
>   uncovered.
>
>   Note well -- in M-3 there is a "fatal" exception that any function can
>   raise, so the raising of an out-of-signature exception is converted into
>   "fatal".

Thereby loosing whatever information about the nature of the emergency
was trying to be conveyed.  I'm not a Modula-3 user, but it is my
understanding that when it runs out of memory it aborts rather than
throwing an exception (which would immediately become "fatal" in most
circumstances).  This illustrates the M-3 bias towards use of
exceptions as alternate returns.

No matter how "safely" you code, you always have the problem of
having to deal with emergencies.  If I write

        void f() throw(X) ;

then the implied "contract" for f is that it will abort in case of
emergency.

If I write

        void g(X&) ;

then the contract is that it will throw an exception in case of
emergency and report the "X" result in the argument.

The difference is that for "f" I have to worry that I won't get
back control.  While "g" promises always to give back control to
the caller.  If I'm trying to write robust code it seems to me
that "g" has the more sensible contract.  If I'm just trying to
program without worrying about emergencies, then "f" may be
easier to use.

  -- Jerry Schwarz(jss@lucid.com)




Author: clint@vsfl.demon.co.uk (Ian Cameron Smith)
Date: Wed, 2 Feb 1994 17:55:47 +0000
Raw View
There's been a lot said in the recent exceptions debate about
exception signatures; eg. "int fred() throw(FredDead);"
guarantees only to throw the exception FredDead.

My question is, when is this enforced: compile-time or run-time?
The ARM says run-time, and goes into great detail as to why this
decision was made; but I've seen someone state that he believes
this decision to have been altered to require compile-time
checking.  Does anyone know the current state of play?

For what it's worth, My Humble Opinion is that run-time checking
is wrong.  Compile-time detection would (ideally) simply make it
impossible to violate a function's exception specification.
Run-time checking (undeclared exceptions
call the function `unexpected()', which calls `abort()') relies on
testing to detect errors.  This is much less satisfactory; after all,
testing has never been capable of totally eradicating bugs, or else
why are there so many bugs in software?

For example, say I have a particular exception which is not
declared in the function signature.  It may be raised by a function
several calls below the function in question; and it might only occur
if a particular error (like disk full) occurs while writing a
particular data structure to disk in a particular module.  This is
*precisely* the sort of error which is very difficult to test for,
and comes back later to bite you after your product has been released
into the field.

Of course, careful coding can reduce the risk of this sort of thing
happening; but you can say that about any kind of bug, and there are
still bugs.  Any checking of this sort which can be moved from
run-time to compile-time can only help the programmer, although I can
accept that this might not be easy to implement.
--
Ian Cameron Smith                clint@vsfl.demon.co.uk
Principal Software Engineer      +44 (0)425 474484
Virtual Software Factory LTD

"Go ahead, punk, make my day"