Previous Table of Contents Next


10.4.3.4. Using the Instruments Hierarchy: Polymorphic Dispatch

Before examining the implementations of the instruments packages, see how they might be used. Specifically, the next few listings show how to build a linked list of instruments, which will constitute a dashboard. First I give a package interface HB.Instruments.Aux (for “auxiliary”).

    1 with HB.Instruments.Basic, HB.Instruments.Clocks;
    2 use  HB.Instruments.Basic, HB.Instruments.Clocks;
    3 package HB.Instruments.Aux is
    4
    5   type InstrumentPointer is access all Instrument’Class;
    6
    7   Speed : aliased Speedometer;
    8   Fuel : aliased Gauge;
    9   Oil, Water : aliased Graphic_Gauge;
   10   Time : aliased Clock;
   11   Chrono : aliased Chronometer;
   12
   13   SpeedPointer: InstrumentPointer := Speed’Access;
   14   FuelPointer:  InstrumentPointer := Fuel’Access;
   15   OilPointer:   InstrumentPointer := Oil’Access;
   16   WaterPointer: InstrumentPointer := Water’Access;
   17   TimePointer:  InstrumentPointer := Time’Access;
   18   ChronoPointer:InstrumentPointer := Chrono’Access;
   19
   20   procedure Display (P: InstrumentPointer);
   21
   22 end HB.Instruments.Aux;

This package declares a general access type InstrumentPointer that can designate (point to) instrument objects. The reserved word all (in this context) indicates that an object of type InstrumentPointer can designate either a statically declared instrument or one whose space is heap-allocated; if all were omitted, only heap-allocated objects could be designated.

The designated type Instrument’Class consists of the entire type hierarchy of which Instrument is the root. An InstrumentPointer object can thus be made to point to an object of type Speedometer, Gauge, Graphic_Gauge, Clock, or Chronometer, or of any additional types that may be derived from any of these in the future. (Recall that Instrument itself is abstract, so no Instrument objects can exist.)

Lines 7-11 declare some objects in the class Instrument’Class. The reserved word aliased indicates that these statically declared objects may be designated by access objects; omitting aliased would disallow such designation. Lines 13-18 declare some access objects. Each is initialized with a “pointer” to its associated instrument, using the ‘Access attribute.

The procedure Display takes an access value as its parameter; its implementation is shown here.

    1 package body HB.Instruments.Aux is
    2
    3   procedure Display (P: InstrumentPointer) is
    4   begin
    5     Display_Value (P.all);
    6   end Display;
    7
    8 end HB.Instruments.Aux;

In line 5, the expression P.all dereferences P—that is, finds the object designated by P. The type of P.all can be anything in Instrument’Class and can change each time Display is called. Moreover, each type in this class has its own Display_Value. Every time line 5 is executed, the actual type of P.all, at that instant, determines which of many Display_Value operations is called. This is an example of dynamic or runtime polymorphism. The Ada term for this is dispatching; line 5 is a dispatching operation.

10.4.3.5. Building a Dashboard

I build a dashboard as a linked list of instruments. This linked list is heterogeneous; it is a “container” consisting of nodes, each of whose contained object can be a different instrument. Indeed, sometime in the future I could define more instruments, and the list structure must be flexible enough to contain them as well.

I shall use an instance of a generic list package HB.Lists_Generic, which supports creating lists like this one:

This is an interesting package that illustrates several important concepts.

    1 with HB.Lists_Generic;
    2 with HB.Instruments.Aux;
    3 package HB.Dashboards is new HB.Lists_Generic
    4   (ElementType => HB.Instruments.Aux.InstrumentPointer,
    5    DisplayElement => HB.Instruments.Aux.Display);

HB.Dashboards is an instance of HB.Lists_Generic. The generic requires a client to supply two parameters. The first, ElementType, must specify the type of contained objects; the second, a procedure parameter DisplayElement, must specify how to display an element of type ElementType.

The instantiated package provides a type List and several operations. Of these, I use AddToEnd—which, given a list and a contained value, adds a new node containing that value to the tail of the list—and Display—which walks through a list node-by-node, displaying all the contained objects.

I now have all the pieces with which to build and display a dashboard; the main program, Show_Dashboard, accomplishes the task.

    1 with HB.Instruments.Basic; use HB.Instruments.Basic;
    2 with HB.Instruments.Clocks; use HB.Instruments.Clocks;
    3 with HB.Instruments.Aux; use HB.Instruments.Aux;
    4 with HB.Dashboards; use HB.Dashboards;
    5 procedure Show_Dashboard is
    6
    7    Dashboard : List;
    8
    9 begin
   10
   11    Set_Name (Speed, “Speed”);
   12    Set_Name (Fuel, “Fuel”);
   13    Set_Name (Water, “Water”);
   14    Set_Name (Oil, “Oil”);
   15    Set_Name (Time, “Time”);
   16    Set_Name (Chrono, “Chronometer”);
   17
   18    Speed.Value := 45; --  mph
   19    Fuel.Value := 60; --  %
   20    Water.Value := 80; -- %
   21    Oil.Value := 30; --  %
   22    Init (Time, 12, 15, 00);
   23    Init (Chrono, 22, 12, 56);
   24
   25    AddToEnd (Dashboard, SpeedPointer);
   26    AddToEnd (Dashboard, FuelPointer);
   27    AddToEnd (Dashboard, WaterPointer);
   28    AddToEnd (Dashboard, OilPointer);
   29    AddToEnd (Dashboard, TimePointer);
   30    AddToEnd (Dashboard, ChronoPointer);
   31
   32    Display (Dashboard);
   33
   34 end Show_Dashboard;

The context clauses do not include the root package HB.Instruments because this program makes no direct reference to anything there.


Previous Table of Contents Next