Topic: Dynamic Type checking and Exception Handling (was: type/member tags)
Author: dsouza@gwen.cad.mcc.com (Desmond Dsouza)
Date: 19 Mar 91 04:35:12 GMT Raw View
This came up in the context of arguing that explicit dynamic type
checking is sometimes needed. In a previous posting I described three
cases where such dynamic type checks were "indispensible" :), the third
being exception handling.
Come to think of it, as I understand exception handling:
The C++ exception handling scheme REQUIRES a limited form
of dynamic type checking, including identifying derived classes.
Objects *may not* need access to their types, but type-IDs are
needed by the run-time system, as are checks of the sort:
"is class X derived from class Y".
Do I understand this correctly ? See the detailed discussion below.
Reid Ellis <rae@utcs.toronto.edu> writes:
> Desmond Dsouza <dsouza@optima.cad.mcc.com> writes:
> >Consider the proposed (now accepted) Exception Handling scheme for
> >C++. The statement:
> > catch (ErrorClass& o) {
> > ....
> > }
> >is supposed to 'catch' a deeply nested call to:
> > throw ErrorClass("someError");
> >
> >Any implementation of this involves a run-time type check, including
> >class derivation, on the object being thrown. Can you say 'Meta-class'?
>
> This is simply overloading based on argument type. Just like having
> methods named "foo(Tbase &)" and "foo(Tderived &)". No "Meta-class"
> required.
>
> Reid
Sorry, but it is *not* simply static overloading. The exception model is:
- you CATCH a CLASS of objects (including derived class instances)
- you THROW an OBJECT ( whose class is determined statically ?? )
The run-time exception handler searches the stack for the first catch
(handler) which can handle the object you are throwing and transfers
control there.
Consider:
--------------------------------------------------
class Error {
virtual msg() {printf ("Error\n");}
};
class File_Error : public Error {
virtual msg() {printf ("File_Error\n");} };
};
main (){
try { read_in_file ("non_existent_file"); } // [1]
catch (File_Error fe) { ... } // [2]
}
// in another compilation unit:
class Permission_Error : public File_Error {
virtual msg() {printf ("Permission_Error\n");} };
};
read_in_file (char* file) {
throw ( Permission_Error (file)); // [3]
}
--------------------------------------------------
Line [2] establishes a handler for the *class* File_Error, and hence
for ALL classes derived from it.
Line [3] raises an exception, by creating and THROWing an *object* of
class Permission_Error. This should transfer control to line [2]
The combined compile-time and run-time system MUST establish, at line
[3], that the class of the thrown object is derived from File_Error.
QUESTION: Is it true that that only the static type of the thrown
object is used in selecting a handler ?
a. If so, we statically know the type being thrown, and at run-time
merely need a derived-class check. Line [3] can pass information
like "Permission_Error:File_Error:Error" to the run-time system,
line [2] encodes "File_Error", and so we transfer to line [2].
We do not need run-time access to the dynamic class of an object.
But what of pointers? Use the statically declared pointer type?
If so, the example below prints: "Error::Permission_Error", and not
"File_Error::Permission_Error", since the static return type is
an Error*, which should not be caught by the File_Error handler.
main () {
try { read_in_file ("non_existent_file"); }
catch (File_Error* fe) { printf("File_error::"); fe->msg(); }
catch (Error* e) { printf("Error::"); e->msg(); }
}
Error* perm_error ();
read_in_file (char* file) { throw ( perm_error() ); }
Error* perm_errror() { return new Permission_Error ; }
b. If not, it appears we need run-time access to the type of an object:
The run-time object would be of dynamic type Permission_Error*, which
would be caught by the File_Error Handler.
The example above would then print: "File_Error::Permission_Error"
I suspect [a] is right. The ARM hints at [a], but is not explicit.
ARM, p 356:
A 'throw-expression' initializes a temporary of the static
type of the operand of 'throw' and uses that temporary to
initialize the appropriately typed variable named in the
handler.
In either case, we need a representation of the class Permission_Error
and its base classes in line [3]. The compiler also encodes into line
[2] some representation of the class File_Error.
Lets call this representation, just for grins :), an 'object'. This
'object' needs to encode a class and its base classes, at least by
name, and needs to know something about initializations (e.g. for
variable 'fe' on line [2]).
The class of this 'object' thus has instances which themselves
represent (some aspects of) classes.
Sounds a lot like a "meta-class" to me.
Desmond D'Souza
--
-------------------------------------------------------------------------------
Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324
Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza
Author: dsouza@gwen.cad.mcc.com (Desmond Dsouza)
Date: 5 Mar 91 04:19:00 GMT Raw View
This came up in the context of arguing that explicit dynamic type
checking is sometimes needed. In a previous posting I described three
cases where such dynamic type checks were indispensible :), the third
being exception handling.
Come to think of it, as I understand it:
The C++ exception handling scheme REQUIRES a limited form
of dynamic type checking, including identifying derived classes.
Objects may NOT need access to their types, but type-IDs are
needed by the run-time system, and checks of the sort:
"is class X derived from class Y" are needed at run-time.
Do I understand this correctly ?
rae@utcs.toronto.edu (Reid Ellis) writes:
> Desmond Dsouza <dsouza@optima.cad.mcc.com> writes:
> >Consider the proposed (now accepted) Exception Handling scheme for
> >C++. The statement:
> > catch (ErrorClass& o) {
> > ....
> > }
> >is supposed to 'catch' a deeply nested call to:
> > throw ErrorClass("someError");
> >
> >Any implementation of this involves a run-time type check, including
> >class derivation, on the object being thrown. Can you say 'Meta-class'?
>
> This is simply overloading based on argument type. Just like having
> methods named "foo(Tbase &)" and "foo(Tderived &)". No "Meta-class"
> required.
>
> Reid
> --
Sorry, but it is *not* simply static overloading. The exception model is:
- you CATCH a CLASS of objects (including derived class instances)
- you THROW an OBJECT ( whose class is known statically ?? )
The run-time exception handler searches the stack for the first catch
(handler) which can handle the object you are throwing and transfers
control there.
Consider:
--------------------------------------------------
class File_Error { .... };
class No_Access : public File_Error { ... };
main (){
try {
read_in_file ("non_existent_file"); // [1]
}
catch (File_Error fe) { // [2]
...
}
}
...
...
read_in_file (char* file) {
if (cannot_access_file (file))
throw ( No_Access (file)); // [3]
...
}
--------------------------------------------------
Line [2] establishes a handler for the *class* File_Error, and hence
for ALL classes derived from it.
Line [3] raises an exception, by creating and THROWing an *object* of
class No_Access. This should transfer control to line [2]
The combined compile-time and run-time system MUST establish, at line
[3], that the thrown object is an instance of a class derived from
File_Error.
Is it true that that only the static type of the thrown
object is used ? If so, what of pointers e.g. throw (new No_Access)?
If not, dont we need run-time access to the type of an object?
ARM, p 356:
A 'throw-expression' initializes a temporary of the static
type of the operand of 'throw' and uses that temporary to
initialize the appropriately typed variable named in the
handler.
>
Thus, the compiler must encode into line [3] some representation of
the class No_Access and its base class, File_Error. (It must also
encode into line [2] some representation of the class File_Error.)
Lets call this representation, just for the heck of it :), an
'object'. This 'object' needs to encode a class and its base classes,
at least by name, and needs to know something about initializations
(e.g. for variable 'fe' on line [2]).
The class of this 'object' thus has instances which themselves
represent (some aspects of) classes.
Can you say "meta-class" ?
(See Chapter 15 of the ARM for details, or Stroustrup's paper
"Exception Handling for C++ (revised), USENIX C++ Conf, April 1990)
--
Desmond D'Souza
--
-------------------------------------------------------------------------------
Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324
Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza