Topic: HEY! What is the point? (Exceptions++)


Author: djones@megatest.com (Dave Jones)
Date: Tue, 15 Mar 1994 00:37:54 GMT
Raw View
Everybody is furiously trying to answer the question, how should exception
signatures work, and the answers are getting klugier and klugier. May I
suggest that an answer will only be possible if first we answer the question,
WHAT ARE EXCEPTION SIGNATURES FOR?

Exactly what is the problem that are we trying to solve?



    -- Dave





Author: pascual@peggy.tid.es (Pascual Juan)
Date: Tue, 15 Mar 1994 11:11:35 GMT
Raw View
In article <CMoKtt.H6p@megatest.com>, djones@megatest.com (Dave Jones) writes:
|> Everybody is furiously trying to answer the question, how should exception
|> signatures work, and the answers are getting klugier and klugier. May I
|> suggest that an answer will only be possible if first we answer the question,
|> WHAT ARE EXCEPTION SIGNATURES FOR?
|>
|> Exactly what is the problem that are we trying to solve?
|>
|>     -- Dave

Ok. Let's review the question. Maybe we can achieve a better answer doing so:

C++ language offers the following syntaxis to use exceptions:
(I'll use "/*...*/" to say "more code unrelevant").
(NOTE: This is not ARM. Maybe some errors, but concepts are fine.)

class (Xception_1) { /*...*/ };
class (Xception_2) { /*...*/ };
class (Xception_3) { /*...*/ };

ReturnValue_1 f_1(/* list of params. */) throws(Xception_2, Xception_3)
{
  /*...*/
  try
  {
    /*...*/
    if( is_detected_something_nasty_of_type_1() )
    {
      throw Xception_1(/* list of params. */);
    }
    /*...*/
    if( is_detected_something_nasty_of_type_2() )
    {
      throw Xception_2(/* list of params. */);
    }
    /*...*/
    if( is_detected_something_nasty_of_type_3() )
    {
      throw Xception_3(/* list of params. */);
    }
    /*...*/
  } // End of sentence tried.
  catch (Xception_1& current_instance_of_Xception_1)
  {
    // Deal with current_instance_of_Xception_1 to handle the situation.
  }
  catch (Xception_3& current_instance_of_Xception_3)
  {
    // Deal with current_instance_of_Xception_3 to handle PARTIALLY
    // the LOCAL situation.
    throw;
  } // End of try block.
  /*...*/
} // End of f_1.

This code assumes the following behaviour:

.- Xception_1, Xception_2 and Xception_3 are classes designed to be
constructed with data related to a given EXCEPTIONAL situation. Their
public interface (member functions, for example) are designed either
to fix the situation, either to log information about it.

.- In "throw Xception_i(/*Parameters*/)" sentences, an object of class
Xception_i is instanced with the parameters, and code flow is redirected
to the NEAREST "catch" sentence who matches with the type of the instance
throw, that is "catch (The_same_class)" or "catch (Base_class)".

.- When Xception_1 is catched and the catch block ends, code flow continues
out of the try block in f_1 function.

.- Exceptions that are not catched in the try block and appears in the
"exception signature" (at the end of the prototyping of the function)
like "Xception_2" are thrown to the caller function, who can catch it
in a "higer level point of view". This can be done recursively, if the
callers puts it in their signature.

.- Xception_3 is catched in the try block, and once it has been locally
dealed, it is "re-thrown" to the caller like Xception_2.

This is what we all agree.

//////////////////////////////////////////////////////////////////////////////

But things are not so easy. When an exception is thrown and the exception
signature of the function doesn't match with it, current definition of
the language says that "void unexpected()" is called in RUN-TIME. Its
default behaviour is exit program, but it can be changed with your own
function. This is, at best, to log something UNEXPECTED is happening and
you have no information about it. Your function can exit or continue, but
due to the lack of information, it has to be decided in a blind way for
every unexpected exception.

Maybe your code would handle all the possible exceptions, but if you
forget to put it in a catch block or the exception signature, your
code will die now (if you decide to exit) or will die in a few seconds
(if you decide to continue) because IT WAS HAPPENED AN EXCEPTIONAL
SITUATION (like "there is no memory"), and it can propagate fatal errors
through the code flow. So no matter what you choose, your code is (as
actually defined) unsafe and can't be repared because you won't know
where is the bug.

This is what lots of people agree.

//////////////////////////////////////////////////////////////////////////////

There are some solutions:

.- Remove exceptions of the language. It's troubles doesn't worth its
usefullness. Let's wait ten years to solve it in next languages (D++).

.- Current exceptions are fine. That critical situations won't happen to me.

.- Let's make a safe ANSI C++. We are in the right moment. It just has to
be a COMPILE-TIME checking of the syntaxis, and exceptions thrown in a
wrong way will be rejected by the compiler as a syntax error. It has been
made some proposals I collected to achieve this target. Now the matter is
to get safer templates who can adapt themselves to the signatures of the
parametrized types they deal with.

ANSI committee and great gurus (Andrew Koening et al) are thinking about
the first solution.

People who has developed C++ code WITH exceptions arrives the discussion
with the second one solution. When they see the problem some of them goes to
first solution and some goes to third solution. If they are involved in
big projects who leans to first solution, they will have to TOUCH ALL their
code. If they lean to third solution, they will have to TOUCH JUST the
wrong signatures who can throw a potential unexpected (maybe none).

I am a supporter of the third point (you guess it ;-) and I try to collect
all the good (orthogonal) ideas the people are "throwing" to the NEWS.
This collect has been posted twice to comp.std.c++ adding corrections.
I'll post when templates' signature get clearer.
I agree with persons who does safe (even non-critical) software.

--
-------------------------------------------------------------------------------
 ||||  ##     ####                    |         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: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Tue, 15 Mar 94 07:50:51 PST
Raw View
In article <CMoKtt.H6p@megatest.com>, djones@megatest.com (Dave Jones) write:
|> Everybody is furiously trying to answer the question, how should exception
|> signatures work, and the answers are getting klugier and klugier. May I
|> suggest that an answer will only be possible if first we answer the question,
|> WHAT ARE EXCEPTION SIGNATURES FOR?
|>
|> Exactly what is the problem that are we trying to solve?
|>
They are a mechanism for signaling unusual or abnormal conditions that
cannot be handled locally, but *may* be handled at a higher level in
a convenient and predictable manner.

One of the most important features of the mechanism is the guarenteee
that destructors get called for intervening stack objects, along with
the elimination of the need to explicitely cascade return values up
through several levels of calls.

One area where this is important is functions that have some obvious
return type that lacks an obvious error value.  Currently many such
functions are written to return the status in the normal way, and to
"return" the value the funtion computes via a pointer or reference
parameter.

Another place where this is utterly critical is in constructors,
which currently have *no* sure way of reporting errors.


[Our proposed C++ coding standards forbids using exceptions for
non-exceptional alternate returns].

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

The peace of God be with you.




Author: cbarber@bbn.com (Christopher Barber)
Date: 17 Mar 1994 16:22:22 GMT
Raw View
>>>>> "PJ" == Pascual Juan <pascual@peggy.tid.es> writes:

    PJ> There are some solutions:

    PJ> .- Remove exceptions of the language. It's troubles doesn't worth
    PJ> its usefullness. Let's wait ten years to solve it in next languages
    PJ> (D++).

I don't think this is a viable solution, especially since compiler vendors
have expended a fair amount of effort putting them in their compilers.
Besides it is a lot nicer to be able to write:

    try {
        Foo foo() ;

        value = foo.bar() ;
    }
    catch (...)
    {
        value = alternate_value() ;
    }

than to write something like:

    for (int done=0; !done; done++)
    {
        Foo foo() ;

        if (foo.invalid())
            break ;

        if (OK != foo.bar(value) ;
            break ;
    }

    if (!done)
        value = alternate_value() ;

especially because you can omit the try-catch blocks if you know the
exception can be caught and handled somewhere else.

    PJ> .- Current exceptions are fine. That critical situations won't
    PJ> happen to me.

    PJ> .- Let's make a safe ANSI C++. We are in the right moment. It just
    PJ> has to be a COMPILE-TIME checking of the syntaxis, and exceptions
    PJ> thrown in a wrong way will be rejected by the compiler as a syntax
    PJ> error. It has been made some proposals I collected to achieve this
    PJ> target. Now the matter is to get safer templates who can adapt
    PJ> themselves to the signatures of the parametrized types they deal
    PJ> with.

    PJ> ANSI committee and great gurus (Andrew Koening et al) are thinking
    PJ> about the first solution.

I haven't gotten that impression at all, or did you mean the second solution?

Although I would like static checking of exception specifications, I will
use exceptions even if there is only dynamic checking.  However, I am
hedging my bet -- I will write all my exception specifications using a macro:

   void foo() CAN_THROW((X,Y)) ;

where CAN_THROW is defined as:

    #ifdef NO_EXCEPTION_SPECS
    #define CAN_THROW(ex)
    #else
    #define CAN_THROW(ex) throw ex
    #endif

This will allow me to put specifications in my code as I write it and
make use of whatever static checking tools become available, while at
the same time allowing me to recompile my code without the specifications
if I don't want to risk getting a call to unexpected().

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