Topic: Proposal for default Exception specification for functions


Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/15
Raw View
Paresh Adhia wrote:
>
> Dave Abrahams wrote:
> >
> > This scheme breaks down as soon as anything in namespace SQLAccess wants to
> > use a standard library function which is allowed to throw. Then you have to
> > find some way to translate every possible exception into a SQLException. It
> > seems like it would exhibit all of the same maintenance problems that
> > ordinary exception-specifications do, but by limiting the number of places
> > where exception-specifications were actually written, might reduce the
> > number of places in the source code which had to be inspected. You're still
> > left with a maintenance hassle and the danger of unexpected termination in
> > situations which might have been handled perfectly well otherwise. I don't
> > think it changes anything fundamentally.
>
> Probably I wan't clear enough in my earlier post. I just wanted to
> highlight the fact that in C++ functions "by default" are not required
> to specify all the exceptions that they can throw. In this regard, I
> like Java'a enforcement better. Way back, I asked several experts about
> why C++ functions are free to omit exception specification (which I
> consider part of function's interface as much as parameter declarations
> are), the reason was obviously compatibility.
>
> More to the point you raised in my example. All functions in namespace
> SQLAccess DONOT have to throw only SQLException. Only those functions
> which do not declare exception specification EXPLICITLY.  I am just
> pointing to the fact that omitting exception specification does not
> always have to default to throw(...). So if I want Java style strict
> enforcement for my code, I can still have it - I just define all my
> namespaces with empty throw(), so every exception I throw directly or
> indirectly (called functions that throw) will be validated by compiler.
> Let me repost my code again (clearer comments this time), and
> let me know what part implies that functions in namespace are
> restricted to throwing only SQLException
>
> namespace SQLAccess throw(SQLException) {
>
> /* defaults to throw(SQLExeception), since no throw specified
>    compiler will flag an error if tries to throws any other excpetion
>    or does not handle anyother exception thrown by called functions */

Here is a mistake: The compiler will *not* flag an error.
Instead at runtime, if another exception is thrown, unexpected()
is called, which defaults to call terminate(). If overriden,
it may well throw an SQLUnknownException derived from SQLException,
making the code quite fine. Since the compiler cannot know what
unexpected_handler is currently set for any call of a function in
the namespace (it could even be a different one for each call),
it cannot determine if there might be an "unallowed" exception
at runtime which is not handled properly.
Changing this would be a fundamental change to the exception
mechanism, could break C++ programs depending on current
behaviour, and would make calling foreign code where no
exception spec is given a real pain.
Moreover, correct exception-specifications on templates are
impossible today, and it's very hard (if not impossible)
to find an at least remotely readable syntax which covers
all cases. Even simple templates can result in a huge number
of possible inherently different expansions:

template<class T> T f(T t) { return t+t; }

This can result in
- adding a built-in type to itself, and returning it
- calling a member T::operator+, taking either a T reference,
  a T copy (which can involve a T(T&) distinct from T(T const&)
  used for return type (if operator+ returns by value)
- calling a global operator+, taking two references, or two values
- in the most general case, calling U operator+(V, W), with
  three types U, V, W, where the conversions T->V, T->W
  and U->T exist, either through constructors, or through
  conversion functions

Each copy constructor (taking const or non-const T),
each conversion constructor and/or conversion function,
and finally each operator+, be it global or member,
can throw different exceptions individually. And
generally, the user of a template expects all the
exceptions to get through (he knows the type and
the requirements, so he knows what might be thrown).
So, how do you pack all those different cases into an
exception specification for the template?
And remember: This is a *simple* case.

For current C++, it does't hurt, since the caller knows what
he can expect, and the compiler doesn't care. However, if
exception specifications were checked by the compiler, you
would _need_ a way to specify them - otherwise templates
would be completely unusable.

>   int countRows(SQLQuery& q);
>
> /* can throw any exception (bad!), throw explicitly specified,
>    hence overrides defualt specification */
>   void execCommand(SQLCommand& c) throw(...);
>
> /* throws only the named exception, (again overrides defualt)
>    compiler will flag an error if tries to throws any other excpetion
>    or does not handle anyother exception thrown by called functions */
>   void closeCursor(SQLCursor& cursor) throw(SQLExcpCursorNotOpen);
> }
>
> I also may add that, I don't like Java's arbitrary selection of
> runtimeexception and logicexception (I don't remember exact names) which
> are excused from its enforcement. Language like C++, if modified little
> can allow Java style enforcement but at the same time do not have to
> make arbitrary exception rules. (Probably some clever modification of my
> syntax, such as instead of overriding, adds to default throw exceptions)

Probably an appropriate compiler switch, without modifying the
language at all, would be even better.

Indeed, I could imagine two different compiler switches:

One to warn about functions where no exception specification
is given at all (probably excluding templates, and excluding
headers included with <...> syntax), and one which warns about
functions where explicit exception specifications are broken
(that is, functions which have _no_ specification at all would
be considered as throwing only what may be thrown, but all
explicit specifications would be checked).

By using both flags and treating warnings as errors, you would
get nearly the Java behaviour, except that
- you still have to write throw() - but the compiler complains
  if you forget
- templates and external functions without throw specifications
  are not checked - but that would be impossible anyway

And all that without a language change.

The correct place to ask for that would of course be your
compiler vendor.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]


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





Author: Paresh Adhia <adhia_paresh@jpmorgan.com>
Date: 1999/02/12
Raw View
Dave Abrahams wrote:
>
> This scheme breaks down as soon as anything in namespace SQLAccess wants to
> use a standard library function which is allowed to throw. Then you have to
> find some way to translate every possible exception into a SQLException. It
> seems like it would exhibit all of the same maintenance problems that
> ordinary exception-specifications do, but by limiting the number of places
> where exception-specifications were actually written, might reduce the
> number of places in the source code which had to be inspected. You're still
> left with a maintenance hassle and the danger of unexpected termination in
> situations which might have been handled perfectly well otherwise. I don't
> think it changes anything fundamentally.

Probably I wan't clear enough in my earlier post. I just wanted to
highlight the fact that in C++ functions "by default" are not required
to specify all the exceptions that they can throw. In this regard, I
like Java'a enforcement better. Way back, I asked several experts about
why C++ functions are free to omit exception specification (which I
consider part of function's interface as much as parameter declarations
are), the reason was obviously compatibility.

More to the point you raised in my example. All functions in namespace
SQLAccess DONOT have to throw only SQLException. Only those functions
which do not declare exception specification EXPLICITLY.  I am just
pointing to the fact that omitting exception specification does not
always have to default to throw(...). So if I want Java style strict
enforcement for my code, I can still have it - I just define all my
namespaces with empty throw(), so every exception I throw directly or
indirectly (called functions that throw) will be validated by compiler.
Let me repost my code again (clearer comments this time), and
let me know what part implies that functions in namespace are
restricted to throwing only SQLException

namespace SQLAccess throw(SQLException) {

/* defaults to throw(SQLExeception), since no throw specified
   compiler will flag an error if tries to throws any other excpetion
   or does not handle anyother exception thrown by called functions */
  int countRows(SQLQuery& q);


/* can throw any exception (bad!), throw explicitly specified,
   hence overrides defualt specification */
  void execCommand(SQLCommand& c) throw(...);


/* throws only the named exception, (again overrides defualt)
   compiler will flag an error if tries to throws any other excpetion
   or does not handle anyother exception thrown by called functions */
  void closeCursor(SQLCursor& cursor) throw(SQLExcpCursorNotOpen);
}

I also may add that, I don't like Java's arbitrary selection of
runtimeexception and logicexception (I don't remember exact names) which
are excused from its enforcement. Language like C++, if modified little
can allow Java style enforcement but at the same time do not have to
make arbitrary exception rules. (Probably some clever modification of my
syntax, such as instead of overriding, adds to default throw exceptions)

>
> -Dave
>

Thanks for taking time for reading my post.

Paresh


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






Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/11
Raw View

Paresh Adhia wrote in message
<36BF510F.4662E02E@DELETE.THIS.jpmorgan.com>...
>But I guess, exception-specification can still be useful for many
>projects where problem at hand is well defined.

Useful for what?

>And for such cases where
>it makes sense to use exeception-specifications, I propose for new
>syntax to the line of following example.

<snip>

>namespace SQLAccess throw(SQLException) {
>
>  int countRows(SQLQuery& q);    /* defaults to throw(SQLExeception) */
>  void execCommand(SQLCommand& c) throw(...); /* throws any exception,
>overrides defualt specification */
>  void closeCursor(SQLCursor& cursor) throw(SQLExcpCursorNotOpen); /*
>throws only the named exception, again overrides defualt */
>}

<snip>

>To me, this is an easier way to add exception-specificaiton for closely
>related functions for a class or a namespace. It also spares anyone not
>wishing to use exception-specification, from writing throw(...) for
>every function.


But we are already spared from writing throw(...) for every function.


>Any thoughts ?


This scheme breaks down as soon as anything in namespace SQLAccess wants to
use a standard library function which is allowed to throw. Then you have to
find some way to translate every possible exception into a SQLException. It
seems like it would exhibit all of the same maintenance problems that
ordinary exception-specifications do, but by limiting the number of places
where exception-specifications were actually written, might reduce the
number of places in the source code which had to be inspected. You're still
left with a maintenance hassle and the danger of unexpected termination in
situations which might have been handled perfectly well otherwise. I don't
think it changes anything fundamentally.

-Dave





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


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Paresh Adhia <adhia_paresh@jpmorgan.com>
Date: 1999/02/10
Raw View
Since I am a casual reader of csc++ ng, forgive me if following was
discussed before.

A while ago, I posted a question as to why a function declaration
without exception-specification defaults to function being able to
throw any exception, whereas I would have assumed it to be "throw
nothing". Beside the obvious compatibility reason, to my surprise, many
argued that, exception-specification becomes
a maintenance nightmare in large projects, and it is best to omit them.
But I guess, exception-specification can still be useful for many
projects where problem at hand is well defined. And for such cases where
it makes sense to use exeception-specifications, I propose for new
syntax to the line of following example. Please do not consider
appropriateness for the specific example I chose (it was the immediate
thing that came to my mind) but the reasoning behind such a need. Also
this is a very simple case and syntax might need to be changed for
complex constructs like nested namespaces/classes/templates etc.

namespace SQLAccess throw(SQLException) {

  int countRows(SQLQuery& q);    /* defaults to throw(SQLExeception) */
  void execCommand(SQLCommand& c) throw(...); /* throws any exception,
overrides defualt specification */
  void closeCursor(SQLCursor& cursor) throw(SQLExcpCursorNotOpen); /*
throws only the named exception, again overrides defualt */
}

I would also use above syntax to define classes too. To maintain
compatibility with existing programs, C++ can assume throw(...) for all
namespace and class definition which do not specify default
exception-specification.

To me, this is an easier way to add exception-specificaiton for closely
related functions for a class or a namespace. It also spares anyone not
wishing to use exception-specification, from writing throw(...) for
every function.

Any thoughts ?

-Paresh

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