The abstract typeEventserves as the ancestor of all the event types. Promoting one of the actual events to be the parent of all others would be arbitrary and inappropriate, so we declare an abstract event even though objects of this type are meaningless. In the simulation program, the abstract type Root_Event.Event is a convenient place to declare the common component Time. More commonly, an abstract type is declared as a null record.
The abstract primitive subprograms serve as ancestors of the real primitive subprograms to be declared upon derivation.
§3.9.3
1 Anabstract type is a tagged type intended for use as a parent type for type exten- sions, but which is not allowed to have objects of its own. Anabstract subprogram
is a subprogram that has no body, but is intended to be overridden at some point when inherited. Because objects of an abstract type cannot be created, a dispatch- ing call to an abstract subprogram always dispatches to some overriding body.
‡306is an example of the last sentence of the above paragraph.
§3.9.3
4 For a derived type, if the parent or ancestor type has an abstract primitive subpro- gram, . . . then:
5 If the derived type is abstract or untagged, the inherited subprogram is abstract.
6 Otherwise, the subprogram shall be overridden with a nonabstract subprogram; . . .
7 A call on an abstract subprogram shall be a dispatching call; nondispatching calls to an abstract subprogram are not allowed.
8 The type of an aggregate, or of an object created by an object_declaration or an allocator, . . . shall not be abstract. The type of the target of an assignment, operation (see 5.2) shall not be abstract. The type of a component shall not be abstract. . . .
13 A class-wide type is never abstract. Even if a class is rooted at an abstract type, the class-wide type for the class is not abstract, and an object of the class-wide type can be created; the tag of such an object will identify some nonabstract type in the class.
While a abstracttypemust be tagged §3.9.3(1), an abstractsubprogramcan be primitive for an untagged derived type §3.9.3(5); see Section 8.6. Such a subprogram is never callable and can be used to avoid exporting inherited operations.
6.11
Implementation of dispatching**
This book presents the Ada language as seen by a programmer and is not normally concerned with the implementation techniques used in the compiler and run-time system. Nevertheless, an outline of a possible implementation of dynamic dispatching will enable you to use the technique with the knowledge of the run-time overhead that is incurred.
6.11 Implementation of dispatching** 96
Figure 6.6 shows a data structure that can be used in the implementation of dispatching for the rocket simulator.6
Dispatch Table
Jump Tables Subprograms
Engine Steering Telemetry Main Aux - - - - - Simulate Create Simulate Create Simulate Create - - - - - -
Figure 6.6: Implementation of dynamic dispatching
A dispatch table is created by the compiler and loaded at run-time. Tags are represented by offsets into the dispatch table. When a dispatching call is made, the offset is used to obtain the address of thejump tablecorresponding to the specific type of the controlling operands. The jump table contains a pointer to each primitive subprogram; an indirect call on this pointer calls the subprogram. SinceMain_Engine_EventinheritsSimulatefromEngine_Event, a new procedure is not created for the derived type. Instead, the jump table pointer forMain_Engine.Simulateis directed at the procedure already declared for the parent type.
An implication of this implementation is that the run-time overhead issmalland, more importantly,
fixed. Two or three machine instructions will suffice for doing the double indirection, and for any given machine and compiler the overhead can be computed or measured.Alldispatching calls will have exactly this overhead, so there is no uncertainty that would prevent the use of dispatching in real-time systems.
Once a tagged type or extension is declared, additional derived descendants can be declared with- out recompiling the package specification that declares the parent. Each additional derivation will add an entry to the dispatch table, a new jump table and code for any primitive subprograms overridden or added upon derivation. Existing tags (offsets) and jump tables are not affected. Primitive subprogram must be declared in the package specification: since no more entries can be made to the jump table for this type, the table can be created during the compilation of the specifi- cation. This implementation is possible because derivation can only add primitive operations, not remove them, and any operation not overridden is inherited. Thus if a primitive operationProc
is defined for a tagged typeT, thenProcwill also be defined for any type in T’Class. Further- more, the primitive operation can only be called with a controlling operand of the class-wide type
T’Classor with the specific typeT. This is checked at compile-time, so at run-time the dispatching call can be made without a run-time check.