dsouza@gwen.cad.mcc.com (Desmond Dsouza) (03/19/91)
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