4.11 Access Types - 2

[ Table of Contents ] Prev ] Chapter Overview ] Next ] [ Glossary/Index ]

We continue our discussion of access types, and provide an example illustrating the use of general access types. First, we discuss dereferencing with the reserved word all, which pertains to both pool-specific and general access types. Then we discuss two other features of the language, which pertain only to general access types.

Dereferencing

Suppose we had declared a record type, My_Rec_Type, with two integer components, Comp1 and Comp2, and then declared an access type, Rec_Ptr_Type, whose values point to values of My_Rec_Type. Now, we can write, in an executable part:

P1:= new My_Rec_Type'(2,4);  -- dynamic allocation and initialization
P2:= new My_Rec_Type'(6,8);  -- dynamic allocation and initialization
P1.Comp1 := 5;               -- change 1st component of 1st object
P1.Comp2 := 10;              -- change 2nd component of 1st object
P2.all   := (20,30);         -- change (6,8) to (20,30) in 2nd object
P1       := P2;              -- now P1 also points to (20,30) object

Whereas the name P2 designates or "references" an unnamed record object, the expression P2.Comp1 designates a component of the object and the expression P2.all designates the whole object -- a dereferencing operation.

Aliased Objects and the Access Attribute

A declared object can be pointed to by an access value only if it is originally declared as an aliased object -- one that can have multiple names. Now suppose we wrote, in a block that can "see" My_Rec_Type:

declare
  GAT_Rec_Ptr is access all My_Rec_Type; -- general access type
  PA, PB, PC : GAT_Rec_Ptr;              -- 3 general access values
  RecA, RecB : aliased My_Rec_Type;      -- 2 aliased objects
begin
  RecA     := (15,7);
  RecB     := ( 3,9);
  PA       := RecA'Access;      -- points to RecA
  PB       := RecB'Access;      -- points to RecB
  PB.Comp2 := PA.Comp1;         -- RecB now (3,15)
  PC       := PA;               -- points to RecA (15,7)
  PA.all   := PB.all;           -- RecA now (3,15)
  PB.all   := PC.all;           -- RecB now (15,7)  
end;

RecA and PA.all are two names (aliases) for the same object, and RecB and PB.all are two names (aliases) for another object. PC.all is another name, which can represent either RecA or RecB. Note the use of the 'Access attribute, not to be confused with the reserved word access.

Lifetime Rule for Access Types

There is a lifetime rule that says: given an access type T, X'Access yielding a result of type T is allowed only if X can live at least as long as T. This rule prevents the potential for dangling references.

Example Program Illustrating General Assess Types

Our example involves a callback situation in which a look-up table is used to get access values that select appropriate response procedures and parameters, such as the response to a button click or other event.  (The events are simulated here with keyboard inputs.) We are using a technique described in Section 9.3 of [Ben-Ari98], but applied to a different situation.

Im4-12.gif (3589 bytes)

The types declared in this main procedure include two general access types -- Message_Ptr, pointing to aliased constant strings -- and Procedure_Ptr, pointing to procedures having identical profiles.

One procedure represents the response to the pushing of a button (up or down). The other represents the response to other events, such as an elevator arriving at a floor.

A look-up table named Callback holds pairs of access values -- one of each access type.

In this example the event responses are simply to display one-line messages indicating that those events have occurred.

Source Code Listing

----------------------------------------------------------
----------------- Test_General_Accesses ------------------
--  This interactive program employs single keystrokes to 
--  represent two classes of events. Lower case 'u' and 
--  'd' represent a person pushing an up or down button. 
--  Upper case 'U' and 'D' represent the arrival of an 
--  up-going or down-going elevator, respectively. General
--  access types are used to reference appropriate 
--  responses and display messages. A 'Q' input is used to
--  quit the program. 
----------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_General_Accesses is
    
  -- types (1 enumeration, 2 general access, 1 record)  
  type Inputs is (Up, Down, Stop_Up, Stop_Down); -- 2 button events
                                                 -- & 2 other events
  type Message_Ptr is access constant String;
  type Procedure_Ptr is access procedure(I : in Inputs;
                                         S : in String);
  type Callbacks is
    record 
      Message : Message_Ptr;
      Action  : Procedure_Ptr;
    end record;
  
  -- aliased objects (constant)  
  Msg_Up      : aliased constant String := "the up light is on.";
  Msg_Down    : aliased constant String := "the down light is on.";
  Msg_Stop_Up : aliased constant String := "the up light is off.";
  Msg_Stop_Dn : aliased constant String := "the down light is off.";
  
  -- procedures (2 with same parameter profile)
  --------------------------------------------------
  procedure Respond_To_Button(I : in Inputs;
                              S : in String) is
  begin
    Put_Line("A traveler pushed a button and " & S);  
  end Respond_To_Button;    
  --------------------------------------------------
  procedure Respond_To_Event(I : in Inputs;
                             S : in String) is
  begin
    Put_Line("The elevator arrived and " & S);  
  end Respond_To_Event;
  --------------------------------------------------
  --  look-up table for pairs of access values    
  Callback : constant array (Inputs) of Callbacks := 
      ((Msg_Up'Access,      Respond_To_Button'Access),
       (Msg_Down'Access,    Respond_To_Button'Access),
       (Msg_Stop_Up'Access, Respond_To_Event'Access) ,
       (Msg_Stop_Dn'Access, Respond_To_Event'Access));
       
  Key         : Character;          -- keyboard input
  Valid_Input : Boolean;
  Inp         : Inputs  := Stop_up; -- button or event inputs
      
begin
    
  Put_Line("Enter u, d, U, D or Q");
  
  loop
    Valid_Input := True;
    Put(">>"); Get(Key);
    case Key is               -- translate to button or event
      when 'u' => Inp := Up;
      when 'd' => Inp := Down;
      when 'U' => Inp := Stop_Up;
      when 'D' => Inp := Stop_Down;
      when 'Q' =>
        Put_Line("Goodbye"); 
        exit;
      when others => 
        Valid_Input := False; 
        Put_Line("Invalid input, try again.");
    end case;
    
    if Valid_Input then
      Callback(Inp).Action(Inp, Callback(Inp).Message.all);
      -- Note dereferencing (Message.all) to obtain string 
      -- as second parameter of procedure call to Action. 
      -- Action turns out to be either Respond_To_Button 
      -- or Respond_To_Event.
    end if;
    
  end loop;
  
end Test_General_Accesses;
-----------------------------------------------------------

Here is a sample output from the above program:

     Enter u, d, U, D or Q
     >>u
     A traveler pushed a button and the up light is on.
     >>d
     A traveler pushed a button and the down light is on.
     >>d
     A traveler pushed a button and the down light is on.
     >>D
     The elevator arrived and the down light is off.
     >>U
     The elevator arrived and the up light is off.
     >>y
     Invalid input, try again.
     >>u
     A traveler pushed a button and the up light is on.
     >>Q
     Goodbye

Related Topics

4.1 Type System Overview A.3 List of Attributes

[ Back to top of page ]  Prev ] Next ]