🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

1 total messages Started by Desmond Dsouza Tue, 19 Mar 1991 04:35
Dynamic Type checking and Exception Handling (was: Re: type/member tags)
#956
Author: Desmond Dsouza
Date: Tue, 19 Mar 1991 04:35
145 lines
5742 bytes
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
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads