Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

346
Matthew Heaney 1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney <mailto:[email protected]>

Transcript of Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Page 1: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 1

Implementing Design Patterns

in Ada95Tips, Tricks,and Idioms

by Matthew Heaney<mailto:[email protected]>

Page 2: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 2

Send a message with the body

subscribe patterns <your full name>

to the ACM mailing list server:

<mailto:[email protected]>

Search the archives for pattern implementations:

<http://www.acm.org/archives/patterns.html>

Join the Ada95 patterns list!

Page 3: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 3

Interpreter

Page 4: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 4

What’s An Interpreter?

• Interpret sentences in a language specified by a grammar.

• Each production in the grammar is implemented as a type.

• As a lexical expression is parsed, an object is created for each production in the sentence.

Page 5: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 5

Boolean Expression Grammar

<exp> ::= <and exp> | <or exp> |<not exp> | <var exp> | <const exp>

<and exp> ::= <exp> and <exp>

<or exp> ::= <exp> or <exp>

<not exp> ::= not <exp>

<var exp> ::= <name>

<const exp> ::= true | false

Page 6: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 6

package Bool_Exps is

type Bool_Exp (<>) is abstract tagged limited private;

type Bool_Exp_Access is access all Bool_Exp'Class;

function Eval (Exp : access Bool_Exp; Context : in Exp_Context) return Boolean is abstract;

function Copy (Exp : access Bool_Exp) return Bool_Exp_Access is abstract; ...

Page 7: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 7

... procedure Free (Exp : in out Bool_Exp_Access);

private

type Bool_Exp is abstract tagged limited null record; procedure Do_Free (Exp : access Bool_Exp);

...end Bool_Exps;

Page 8: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 8

The Need For Indirection

• Some expressions (<and>, <or>, <not>) contain other expressions.

• We don’t know the size of the expression component (of type Bool_Exp’Class, which is indefinite), so we must refer to it indirectly.

• “Containment by reference” instead of “containment by value.”

Page 9: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 9

package Bool_Exps.And_Exps is

type And_Exp is new Bool_Exp with private;

function New_And (L, R : access Bool_Exp'Class) return Bool_Exp_Access;

function Eval (Exp : access And_Exp; Context : in Exp_Context) return Boolean;...

Page 10: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 10

...private

type And_Exp is new Bool_Exp with record L, R : Bool_Exp_Access; end record;

procedure Do_Free (Exp : access And_Exp);

end Bool_Exps.And_Exps;

Page 11: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 11

Desiderata

• We want prevent the client from creating or destroying instances directly, to allow each type to define and enforce its own storage management policy, and hide the details.

• We want to minimize syntactic overhead (don’t want to have to explicitly dereference a pointer).

Page 12: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 12

Implementation

• Designated type is limited and indefinite. Limited-ness prevents (shallow) copies, and indefinite-ness prevents direct allocation.

• Each specific type declares a constructor, so a client can create instances of type. (A client never calls allocator new directly.)

Page 13: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 13

• Primitive operations take access parameters. Therefore, no explicit dereferencing is necessary.

• Each type declares its own private deconstructor, which performs type-specific clean-up prior to actual deallocation.

• The client reclaims storage for an object by explicitly calling a public, class-wide deconstructor, which is implemented by calling the type’s private deconstructor.

Page 14: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 14

((True and X) or (Y and (not X)))

declare Exp : Bool_Exp_Access := New_Or (New_And (New_Const (True), New_Var ('X')), New_And (New_Var ('Y'), New_Not (New_Var ('X'))));

Exp_Value : constant Boolean := Eval (Exp, Context); begin Free (Exp);end;

Page 15: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 15

package body Bool_Exps.And_Exps is ... function Eval (Exp : access And_Exp; Context : in Exp_Context) return Boolean is begin return Eval (Exp.L, Context) and Eval (Exp.R, Context); end Eval;

Page 16: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 16

package body Bool_Exps.And_Exps is

type And_Exp_Access is access all And_Exp;

function New_And (L, R : access Bool_Exp'Class) return Bool_Exp_Access is

Exp : constant And_Exp_Access := new And_Exp; begin Exp.L := Bool_Exp_Access (L); Exp.R := Bool_Exp_Access (R);

return Bool_Exp_Access (Exp); end New_And;

Page 17: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 17

Deallocation• Client manually calls a class-wide Free

operation to deallocate an expression object.

• Free can’t deallocate the (class-wide) object directly, because type-specific clean-up may be required.

• Free internally calls the private operation Do_Free, which dispatches according to the object’s tag. The type itself does the clean-up and actual deallocation.

• Free is an example of a “template method.”

Page 18: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 18

body Bool_Exps is ... procedure Free (Exp : in out Bool_Exp_Access) is begin if Exp /= null then

Do_Free (Exp); -- dispatches

Exp := null;

end if; end Free;

end Bool_Exps;

Page 19: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 19

package body Bool_Exps.And_Exps is ... procedure Do_Free (Exp : access And_Exp) is

procedure Deallocate is new Ada.Unchecked_Deallocation (And_Exp, And_Exp_Access);

EA : And_Exp_Access := And_Exp_Access (Exp);

begin

Do_Free (Exp.L); Do_Free (Exp.R);

Deallocate (EA);

end Do_Free;

end Bool_Exps.And_Exps;

Page 20: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 20

Constant Expressions

• The type Const_Exp has only two values: True and False.

• Because objects are referred to indirectly, multiple clients can share the same object, thus avoiding allocation of duplicate values.

• This is an example of the Flyweight pattern.

Page 21: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 21

package Bool_Exps.Const_Exps is pragma Elaborate_Body;

type Const_Exp is new Bool_Exp with private;

...

function New_Const (Value : Boolean) return Bool_Exp_Access;

...

Page 22: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 22

package body Bool_Exps.Const_Exps is

type Const_Exp_Array is array (Boolean) of aliased Const_Exp;

Const_Exps : Const_Exp_Array;

function New_Const (Value : Boolean) return Bool_Exp_Access is begin return Const_Exps (Value)'Access; end;

...

Page 23: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 23

...

begin

for Value in Const_Exps'Range loop Const_Exps (Value).Value := Value; end loop;

end Bool_Exps.Const_Exps;

Page 24: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 24

Smart Pointers

Page 25: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 25

Motivation

• One issue with the Interpreter example is that deallocation of expression objects must be done manually by the client, by explicitly calling Free.

• This is an obvious source of memory leaks and dangling references.

• Besides its being prone to error, explicit deallocation also carries a fair amount of syntactic overhead.

Page 26: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 26

Perform_Mental_Gymnastics: declare Replacement : Bool_Exp_Access := New_Not (New_Var ('Z'));

Rep_Exp : constant Bool_Exp_Access := Replace (Exp, 'Y', Replacement); begin Free (Replacement); Free (Exp); Exp := Rep_Exp; end Perform_Mental_Gymnastics;

Page 27: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 27

Desiderata

• Low syntactic overhead. Manipulation of smart pointers should be similar to regular access objects.

• By-reference semantics implies a reference-counting scheme.

• No explicit deallocation is ever required. Implies use of a Controlled type.

Page 28: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 28

package Bool_Exps is

type Bool_Exp (<>) is abstract tagged limited private;

type Bool_Exp_Access is access all Bool_Exp'Class;

type Exp_Handle is private;

function "+" (Handle : Exp_Handle) return Bool_Exp_Access;

function Null_Handle return Exp_Handle; ...

Page 29: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 29

private

type Bool_Exp is abstract tagged limited record Count : Natural; end record;

type Exp_Handle_Rep is new Controlled with record Exp : Bool_Exp_Access; end record;

procedure Adjust (Handle : ...)

procedure Finalize (Handle : ...);

type Exp_Handle is record Rep : Exp_Handle_Rep; end record;

Page 30: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 30

((True and X) or (Y and (not X)))declare Exp : constant Exp_Handle := New_Or (New_And (New_Const (True), New_Var ('X')), New_And (New_Var ('Y'), New_Not (New_Var ('X'))));

Exp_Value : constant Boolean := Eval (+Exp, Context);begin null;end;

Page 31: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 31

declare Replacement : Bool_Exp_Access := New_Not (New_Var ('Z'));

Rep_Exp : constant Bool_Exp_Access := Replace (Exp, 'Y', Replacement);begin Free (Replacement); Free (Exp); Exp := Rep_Exp;end;

Without Smart Pointers

Page 32: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 32

declare Replacement : constant Exp_Handle := New_Not (New_Var ('Z'));begin Exp := Replace (+Exp, 'Y', Replacement);end;

With Smart Pointers

Page 33: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 33

function Eval (Exp : access And_Exp; Context : in Exp_Context) return Boolean is begin return Eval (+Exp.L, Context) and Eval (+Exp.R, Context); end Eval;

Page 34: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 34

Implementation

• A “smart pointer” is a non-limited type that privately derives from Controlled, and has an access object as its only component.

• It uses unary plus “+” to return the value of the internal access object, which is used immediately as the actual parameter in subprogram calls.

Page 35: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 35

• The type designated by the access type has a Count component to store the number of references.

• When the reference count drops to zero (meaning there are no more references to the object), the designated object is automatically returned to storage.

Page 36: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 36

Consequences

• Constructors now return Exp_Handle instead of Bool_Exp_Access.

• Expression components (of <and>, <or>, and <not> expressions) are now of type Exp_Handle.

• Small syntactic penalty necessary to dereference handle object.

Page 37: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 37

package Bool_Exps.And_Exps is

type And_Exp is new Bool_Exp with private;

function New_And (L, R : Exp_Handle) return Exp_Handle;

function Eval (Exp : access And_Exp; Context : in Exp_Context) return Boolean; ...

Page 38: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 38

...private

type And_Exp is new Bool_Exp with record L, R : Exp_Handle; end record;

procedure Do_Free (Exp : access And_Exp);

end Bool_Exps.And_Exps;

Page 39: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 39

function New_And (L, R : Exp_Handle) return Exp_Handle is

Exp : constant And_Exp_Access := new And_Exp;

Handle_Rep : constant Exp_Handle_Rep := (Controlled with Exp => Exp.all’Access); begin Exp.Count := 1; Exp.L := L; Exp.R := R;

return (Rep => Handle_Rep); end New_And;

Allocation

Page 40: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 40

Assignment

• During assignment, the private operation Adjust is called to increment the reference count of the object designated by the pointer.

Page 41: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 41

package body Bool_Exps is

...

procedure Adjust (Handle : in out Exp_Handle_Rep) is begin if Handle.Exp /= null then

Handle.Exp.Count := Handle.Exp.Count + 1;

end if; end Adjust;

...

Page 42: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 42

Deallocation

• When a smart pointer is assigned a new value, or goes out of scope, then the private operation Finalize is called.

• Finalize decrements the reference count of the designated object.

• If the reference count is zero, then Finalize also returns the object to storage, by calling (dispatching operation) Do_Free.

Page 43: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 43

package body Bool_Exps is ... procedure Finalize (Handle : in out Exp_Handle_Rep) is begin if Handle.Exp /= null then

Handle.Exp.Count := Handle.Exp.Count - 1;

if Handle.Exp.Count = 0 then Do_Free (Handle.Exp); end if;

end if; end Finalize;

Page 44: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 44

package body Bool_Exps.And_Exps is ... procedure Do_Free (Exp : access And_Exp) is

EA : And_Exp_Access := And_Exp_Access (Exp);

procedure Deallocate is new Ada.Unchecked_Deallocation (And_Exp, And_Exp_Access); begin pragma Assert (Exp.Count = 0); Exp.L := Null_Handle; Exp.R := Null_Handle; Deallocate (EA); end;

end Bool_Exps.And_Exps;

Page 45: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 45

Dereferencing• The “deference” operator (“+”) for the smart

pointer has a trivial implementation: it simply returns the internal access value.

• A weakness of the whole approach is that it depends on clients never making a copy or otherwise manipulating the access value.

• Limited access types or garbage-collecting storage pools would be a helpful addition to the language.

Page 46: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 46

package body Bool_Exps is ... function "+" (Handle : Exp_Handle) return Bool_Exp_Access is begin return Handle.Rep.Exp; end;

function Null_Handle return Exp_Handle is begin return (Rep => Controlled with null); end; ...end Bool_Exps;

Page 47: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 47

Observer

Page 48: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 48

Motivation

• It’s often the case that when the state changes in one object, another object needs to be notified of the change. There are a couple of ways of implementing this.

• The subject can know who its observers are by name, and tell them directly about the state change; or,

Page 49: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 49

• The subject only knows that it’s being observed. It tells its observer that its state has changed, and then the observer queries the subject for the new state.

• A consequence of the former approach is that every time a new observer is added to the system, the subject must be modified to update yet another observer.

• The latter approach doesn’t suffer from this, because the observer just inserts itself into a list of anonymous observers.

Page 50: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 50

package Subjects_And_Observers is

type Subject is tagged limited private;

procedure Notify (Sub : in out Subject'Class);

type Observer is abstract tagged limited private;

procedure Update (Obs : access Observer) is abstract; ...

Page 51: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 51

... procedure Attach (Obs : access Observer'Class; To : in out Subject);

procedure Detach (Obs : access Observer'Class; From : in out Subject);

private ...end Subjects_And_Observers;

Page 52: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 52

Subjects_And_Observers (public)

• Abstractions that wish to be observed derive from Subject. When the state changes, the abstraction calls Notify to let observers know about the change.

• Abstractions that wish to observe a subject derive from Observer, and Attach themselves to a subject. They must override Update, which is called by the subject during the notification.

Page 53: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 53

private

type Observer_Access is access all Observer’Class;

type Subject is tagged limited record Head : Observer_Access; end record;

type Observer is abstract tagged limited record Next : Observer_Access; end record;

end Subjects_And_Observers;

Page 54: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 54

Subjects_And_Obsrvrs (private)

• The subject type is implemented as a linked list of observers.

• When an observer wants to be notified of a state change in the subject, it places itself on the subject’s list of observers.

• During a notification, the subject traverses the list, updating each observer in turn.

Page 55: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 55

package body Subjects_And_Observers is

procedure Notify (Sub : in out Subject'Class) is

Obs : Observer_Access := Sub.Head; begin

while Obs /= null loop

Update (Obs);

Obs := Obs.Next;

end loop;

end Notify; ...

Page 56: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 56

package Clock_Timers is

type Clock_Timer is new Subject with private;

procedure Tick (Timer : in out Clock_Timer);

subtype Hour_Number is Natural range 0 .. 23;

function Get_Hour (Timer : Clock_Timer) return Hour_Number;

...end Clock_Timers;

Page 57: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 57

Clock_Timer Subject

• The subject Clock_Timer publicly derives from Subject, which allows it to be observed.

• Tick is the operation that updates the state of the clock timer, and then notifies any observers.

• Selector operations Get_Hour, Get_Minute, etc allow an observer to query the state.

Page 58: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 58

Alternate Technique

• Derive from Subject privately, and provide public operations to attach an observer.

Page 59: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 59

package Clock_Timers is

type Clock_Timer is private;

procedure Attach (Obs : access Observer’Class; To : in out Clock_Timer);…private

type Clock_Timer is new Subject with record ...

Page 60: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 60

package body Clock_Timers is

procedure Tick (Timer : in out Clock_Timer) is

begin

<update hour, min, sec attributes>

Notify (Timer); -- Update observers

end Tick; ...end Clock_Timers;

Page 61: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 61

package Digital_Clocks is

type Digital_Clock (Timer : access Clock_Timer'Class) is new Observer with null record;

procedure Update (Clock : access Digital_Clock);

end Digital_Clocks;

Page 62: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 62

Digital_Clock Observer

• The observer Digital_Clock derives from Observer type.

• The clock observer binds to its timer subject via an access discriminant. This guarantees that the (clock) subject lives at least as long as the observer, and therefore ensures that no dangling references from observer to subject can occur.

Page 63: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 63

• Update is called by Notify, which is called by the timer subject just after it (the subject) has changed its state.

• The clock observer can “see” its timer subject through its access discriminant. During the Update, the clock queries the state of the timer, and then displays the time in a format specific to that observer.

Page 64: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 64

package body Digital_Clocks is

procedure Update (Clock : access Digital_Clock) is

Hour : constant Hour_Number := Get_Hour (Clock.Timer.all);

Hour_Image : constant String := Integer'Image (Hour); ... Clock_Image : constant String := Hour_Image & ...; begin Put_Line (Clock_Image); end Update;

Page 65: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 65

declare Timer : aliased Clock_Timer; Clock : aliased Digital_Clock (Timer'Access);begin Attach (Clock’Access, To => Timer); Tick (Timer);end;

Page 66: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 66

Dynamic Observers

• You may have an application in which observers of a subject are added and removed dynamically.

• We need to automate calls to Attach and Detach, to ensure no dangling reference from subject to observer occurs.

Page 67: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 67

declare Timer : aliased Clock_Timer;begin … declare Clock : aliased Digital_Clock(Timer’Access); begin Attach (Clock’Access, To => Timer); Tick (Timer); end; -- Oops! Forget to Detach...

Tick (Timer); -- Notify non-existent observer!end;

Page 68: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 68

package Digital_Clocks is

type Digital_Clock (Timer : access Clock_Timer'Class) is limited private;

private

end Digital_Clocks;

Page 69: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 69

private

type Control_Type (Clock : access Digital_Clock) is new Limited_Controlled with null record;

procedure Initialize (Control : in out Control_Type);

procedure Finalize (Control : in out Control_Type);

type Digital_Clock (Timer : access Clock_Timer'Class) is new Observer with record Control : Control_Type (Digital_Clock'Access); end record;

procedure Update (Clock : access Digital_Clock);

end Digital_Clocks;

Page 70: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 70

Adding Controlled-ness

• We don’t really need to advertise that Digital_Clock derives from Observer, so we declare the partial view of the type as limited private, and implement the full view as a derivation.

• Controlled-ness is added as a component of the extension, because Ada doesn’t have multiple inheritance (and doesn’t need it).

Page 71: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 71

• During its initialization, the observer inserts itself on its subject’s observer list.

• During its finalization, the observer removes itself from its subject’s observer list. This guarantees that no dangling reference from subject to observer can occur, because removal is automatic when the lifetime of the observer ends.

Page 72: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 72

package body Digital_Clocks is

procedure Initialize (Control : in out Control_Type) is

Clock : Digital_Clock renames Control.Clock.all; Timer : Clock_Timer renames Clock.Timer.all; begin Attach (Clock’Access, To => Timer); end;

procedure Finalize (Control : in out Control_Type) is

Clock : Digital_Clock renames Control.Clock.all; Timer : Clock_Timer renames Clock.Timer.all; begin Detach (Clock'Access, From => Timer); end;…

Page 73: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 73

declare Timer : aliased Clock_Timer;begin… declare Clock : Digital_Clock (Timer’Access); -- automatically Attach begin Tick (Timer); end; -- automatically Detach

Tick (Timer); -- OKend;

Page 74: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 74

Dynamic Observer Note

• This example was rather contrived, and was really designed to illustrate how to add Controlled-ness to an existing type hierarchy.

• Realistically, a dynamic observer would be declared on the heap. In that case, you could simply Attach in the constructor, and Detach in the deconstructor. A Controlled observer wouldn’t be necessary.

Page 75: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 75

package Digital_Clocks is

type Digital_Clock(<>) is limited private;

function New_Clock (Timer : access Clock_Timer) return Digital_Clock_Access;...private

type Digital_Clock (Timer : access Clock_Timer) is new Observer with record ...;

Page 76: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 76

package body Digital_Clocks is

function New_Clock (Timer : access Clock_Timer) return Digital_Clock_Access is

Clock : constant Digital_Clock_Access := new Digital_Clock(Timer);begin

Attach(Clock, To => Timer);

return Clock;end;

Page 77: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 77

Subject Displays Itself

• You might argue that a subject should be able to display itself, and that providing public selector operations to query the state is actually exposing implementation details about the abstraction.

• In that case, you may decide to make the observer more closely related to the subject, so that it can privately get the state it needs.

Page 78: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 78

• We can do this very simply in Ada95, by making the observer a child of the subject. This gives the observer access to the private part of the subject, obviating the need for the subject to provide any public query functions.

Page 79: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 79

package Clock_Timers is

type Clock_Timer is limited private;

procedure Tick (Timer : in out Clock_Timer);

private ... type Clock_Timer is new Subject with record Hour : Hour_Number; Minute : Minute_Number; Second : Second_Number; end record;

end Clock_Timers;

Page 80: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 80

Clock_Timer Subject

• Since the Digital_Clock observers are going to be children, we can privately derive the Clock_Timer subject from Subject.

• Since non-observer clients don’t care that it’s a subject, the public view of the Clock_Timer type is just (non-tagged) limited private.

Page 81: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 81

package Clock_Timers.Digital_Clocks is

type Digital_Clock (Timer : access Clock_Timer) is limited private;

private ... type Digital_Clock (Timer : access Clock_Timer) is new Observer with null record Control : Control_Type (D_Clock’Access); end record;

procedure Update (Clock : access Digital_Clock);

end Clock_Timers.Digital_Clocks;

Page 82: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 82

Digital_Clock Observer

• The package Digital_Clocks is now a (public) child of Clock_Timers.

• As before, the Digital_Clock type privately derives from Observer.

• Update now queries the state of the timer directly, without using a query function. The clock has visibility to its subject’s representation because the clock is a child.

Page 83: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 83

package body Clock_Timers.Digital_Clocks is

procedure Update (Clock : access Digital_Clock) is

Hour_Image : constant String := Integer'Image (Clock.Timer.Hour + 100); ... Clock_Image : constant String := ...; begin Put_Line (Clock_Image); end Update;

end Clock_Timers.Digital_Clocks;

Page 84: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 84

Observers Observed

• We now introduce another variation of our original example, which allows an observer itself to be observed, by another observer.

• As before, a Digital_Clock observes a Clock_Timer. Here, we add another observer, a Clock_Watcher, to observe the Digital_Clock.

Page 85: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 85

package Digital_Clocks is

type Digital_Clock (Timer : access Clock_Timer'Class) is new Subject with private;

type Meridian_Type is (AM, PM);

function Get_Meridian (Clock : Digital_Clock) return Meridian_Type;

private …

Page 86: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 86

Digital_Clock (public)• The Digital_Clock must announce the fact that it

can be observed, so it publicly derives from Subject.

• But it’s also an observer, so it binds to its Clock_Timer subject via an access discriminant.

• Like any subject, the Digital_Clock provides selector operations to allow its state to be queried by observers.

Page 87: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 87

type Timer_Obs_Type (Clock : access Digital_Clock) is new Observer with null record;

procedure Update (Timer_Obs : access Timer_Obs_Type);

type Control_Type (Clock : access Digital_Clock) is new Limited_Controlled with record Timer_Obs : aliased Timer_Obs_Type (Clock); end record;

procedure Initialize (Control : in out Control_Type);

procedure Finalize (Control : in out Control_Type);

type Digital_Clock (Timer : access Clock_Timer'Class) is new Subject with record Control : Control_Type (Digital_Clock'Access); Meridian : Meridian_Type; end record;

end Digital_Clocks;

Page 88: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 88

Digital_Clock (private)

• The Digital_Clock already derives from Subject, so in order to be an observer too it will have to have an Observer component.

• A helper type, Timer_Obs_Type, which derives from Observer, is used as the component.

• Here we also use a Controlled type to automatically Attach and Detach the observer. This wouldn’t be necessary if you were to manually Attach to the subject.

Page 89: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 89

package body Digital_Clocks is

procedure Initialize (Control : in out Control_Type) is begin Attach (Obs => Control.Timer_Obs'Access, To => Control.Clock.Timer.all); end;

procedure Finalize (Control : in out Control_Type) is begin Detach (Obs => Control.Timer_Obs'Access, From => Control.Clock.Timer.all); end;

Page 90: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 90

• The Control_Type can see its enclosing record (Digital_Clock) via its access discriminant.

• The Digital_Clock observer can see its Clock_Timer subject via its access discriminant.

• Together, these allow the Control_Type to Attach its Timer_Obs component to the Timer subject during Initialize, and Detach it during Finalize.

Page 91: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 91

procedure Update (Timer_Obs : access Timer_Obs_Type) is begin

<get time from Timer_Obs.Clock.Timer.all> <display new time>

if Hour < 12 then Timer_Obs.Clock.Meridian := AM; else Timer_Obs.Clock.Meridian := PM; end if;

Notify (Timer_Obs.Clock.all);

end Update;

end Digital_Clocks;

Page 92: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 92

• As an observer, the Clock_Timer (really, the Timer_Obs_Type) must provide an implementation of Update.

• Update displays the new time (plays its observer role), then updates its own state and Notify’s its own observers (plays its subject role).

• This organization has the effect of propagating a signal all the way back from the ultimate subject to the ultimate observer.

Page 93: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 93

Clock_Watcher

• A very simple observer that observes a Digital_Clock.

• Per the idiom, it binds to its subject via an access discriminant.

• Here we manually Attach and Detach to the subject, instead of using Controlled-ness to do it automatically.

Page 94: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 94

package Clock_Watchers is

type Clock_Watcher (Clock : access Digital_Clock'Class) is limited private;

procedure Start_Watching_Clock (Watcher : access Clock_Watcher);

procedure Stop_Watching_Clock (Watcher : access Clock_Watcher);

private

type Clock_Watcher (Clock : access Digital_Clock'Class) is new Observer with null record;

procedure Update (Watcher : access Clock_Watcher);

end Clock_Watchers;

Page 95: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 95

package body Clock_Watchers is

procedure Start_Watching_Clock (Watcher : access Clock_Watcher) is begin Attach (Watcher, To => Watcher.Clock.all); end;

procedure Stop_Watching_Clock (Watcher : access Clock_Watcher) is begin Detach (Watcher, From => Watcher.Clock.all); end; ...

Page 96: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 96

... procedure Update (Watcher : access Clock_Watcher) is begin case Get_Meridian (Watcher.Clock.all) is when AM => Put_Line ("It's still morning."); when PM => Put_Line ("It's afternoon."); end case; end Update;

end Clock_Watchers;

Page 97: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 97

declare

Timer : aliased Clock_Timer;

Clock : aliased Digital_Clock (Timer'Access);

Watcher : aliased Clock_Watcher (Clock'Access);

begin

Start_Watching_Clock (Watcher);

Tick (Timer);

end;

Page 98: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 98

Observable-Observer Note

• There is another way to allow an observer to be both an observer and a subject.

• Simply change the declaration of Observer type in package Subjects_And_Observers so that it derives from Subject.

• Implementing the observing subject with an observer component isn’t necessary, because the type is already an observer.

Page 99: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 99

package Subjects_And_Observers is

type Subject is tagged limited private;

...

type Observer is abstract new Subject with private;

...

end Subjects_And_Observers;

Page 100: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 100

package Digital_Clocks is

type Digital_Clock (Timer : access C_Timer'Class) is new Subject with private; ...private

type Control_Type (Clock : access Digital_Clock) is new Limited_Controlled with null record; ... type Digital_Clock (Timer : access C_Timer'Class) is new Observer with record Control : Control_Type (D_Clock'Access); Meridian : Meridian_Type; end record;

procedure Update (Clock : access Digital_Clock);

end Digital_Clocks;

Page 101: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 101

Observing Multiple Subjects

• We introduce yet another variation of the observer pattern, this time allowing an observer to observe multiple subjects.

• Now the digital clock simultaneously observes both a timer and a battery. The battery subject notifies its observer when it is drained or charged.

Page 102: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 102

package Batteries is

type Battery_Type is new Subject with private;

procedure Charge (...);

procedure Drain (...);

function Is_Low (...) return Boolean;

private

type Battery_Type is new Subject with record State : Positive := 1; end record;

Page 103: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 103

Battery Subject (spec)

• The battery is observable, asserting this by publicly deriving from Subject.

• Modifier operations Charge and Drain adjust the available energy, and then Notify any observers.

• A selector operation, Is_Low, queries whether there is any energy remaining.

Page 104: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 104

package body Batteries is

procedure Charge (Battery : in out Battery_Type) is begin Battery.State := 1; Notify (Battery); end;

procedure Drain (Battery : in out Battery_Type) is begin Battery.State := Battery.State + 1; Notify (Battery); end;

function Is_Low (Battery : in Battery_Type) return Boolean is begin return Battery.State > 3; end;

Page 105: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 105

package Digital_Clocks is

type Digital_Clock (Timer : access Clock_Timer'Class; Battery : access Battery_Type'Class) is limited private;

private ...end Digital_Clocks;

Page 106: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 106

Digital_Clock (public)

• The public part of the observer type Digital_Clock has been modified to accept two access discriminants, one for each subject it observes.

Page 107: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 107

private

type Timer_Obs_Type (Clock : access Digital_Clock) is new Observer with null record;

procedure Update (Observer : access Timer_Obs_Type);

type Battery_Obs_Type (Clock : access Digital_Clock) is new Observer with null record;

procedure Update (Observer : access Battery_Obs_Type);

...

Page 108: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 108

... type Digital_Clock (Timer : access Clock_Timer'Class; Battery : access Battery_Type'Class) is new Limited_Controlled with record

Timer_Obs : aliased Timer_Obs_Type (D_Clock'Access);

Battery_Obs : aliased Battery_Obs_Type (D_Clock'Access);

end record;

procedure Initialize (Clock : in out Digital_Clock);

procedure Finalize (Clock : in out Digital_Clock);

end Digital_Clocks;

Page 109: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 109

Digital_Clock (private)• There has to be some type that derives from

Observer and overrides Update to process Clock_Timer notifications.

• There has to be some type that derives from Observer and overrides Update to process Battery_Type notifications.

• The same type can’t do both, because we don’t have multiple inheritance in Ada95. No problem, we just use the “multiple views” idiom.

Page 110: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 110

• An internal type, Timer_Obs_Type, observes just the Clock_Timer.

• Another internal type, Battery_Obs_Type, observes just the Battery_Type.

• Each type is bound to its enclosing record, the Digital_Clock, via an access discriminant.

• These internal types will be used to declare the observer components of the Digital_Clock type, which itself already derives from Limited_Controlled.

Page 111: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 111

package body Digital_Clocks is

procedure Update (Observer : access Timer_Obs_Type) is …;

procedure Update (Observer : access Battery_Obs_Type) is

Clock : Digital_Clock renames Observer.Clock.all;

Battery : Battery_Type'Class renames Clock.Battery.all; begin if Is_Low (Battery) then ... end Update;

Page 112: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 112

procedure Initialize (Clock : in out Digital_Clock) is

begin Attach (Obs => Clock.Timer_Obs'Access, To => Clock.Timer.all);

Attach (Obs => Clock.Battery_Obs'Access, To => Clock.Battery.all);end Initialize;

Page 113: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 113

Observing Multiple Attributes

• One issue is that when a subject notifies an observer that a state change has occurred, the observer has no way of knowing which specific attribute has changed.

• This may require the observer to redo all her processing (say, redraw a window), which may be inefficient.

Page 114: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 114

• One solution is to make observation more fine-grained; that is, to be able to observe individual attributes of a object, instead of just one monolithic object.

• When an object being observed changes the value of an attribute, he can notify the observers of that one attribute.

• It’s analogous to observing multiple subjects, but here, all the subjects are part of a single object.

Page 115: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 115

package Clock_Timers is

type Clock_Timer is limited private; ... subtype Hour_Number is Natural range 0 .. 23;

function Get_Hour (Timer : access Clock_Timer) return Hour_Number;

function Get_Hour_Subject (Timer : access Clock_Timer) return Subject_Access;

Page 116: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 116

...private type Clock_Timer is limited record Hour : Integer := -1; Hour_Subject : aliased Subject; Minute : Integer := -1; Minute_Subject : aliased Subject; Second : Integer := -1; Second_Subject : aliased Subject; end record;

end Clock_Timers;

Page 117: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 117

package body Clock_Timers is

procedure Tick (Timer : in out Clock_Timer) is begin

<update time>

if Timer.Hour /= Hour then

Timer.Hour := Hour;

Notify (Timer.Hour_Subject);

end if; ... end Tick;

Page 118: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 118

function Get_Hour_Subject (Timer : access Clock_Timer) return Subject_Access is begin return Timer.Hour_Subject'Access; end;

Page 119: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 119

package Digital_Clocks is

type Digital_Clock (Timer : access Clock_Timer) is limited private;

private

type H_Obs_Type (Timer : access Clock_Timer) is new Observer with null record;

procedure Update (H_Obs : access H_Obs_Type); ... type Digital_Clock (Timer : access Clock_Timer) is new Limited_Controlled with record H_Obs : aliased H_Obs_Type (Timer); ... end record;

...

Page 120: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 120

package body Digital_Clocks is

procedure Update (H_Obs : access H_Obs_Type) is

Image : constant String := Integer'Image (Get_Hour (H_Obs.Timer) + 100); begin <display hour> end;

procedure Initialize (Clock : in out Digital_Clock) is begin

Attach (Obs => Clock.H_Obs'Access, To => Get_Hour_Subject (Clock.Timer));

Page 121: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 121

Factory Method

Page 122: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 122

Motivation

• Suppose we have a family of stack types, and we want to provide a class-wide operation to print a stack.

• We plan on using an active iterator to implement the operation. Each type in the class has its own iterator.

• Here’s the problem: if the stack parameter has a class-wide type, then how do we get an iterator that works for this stack object?

Page 123: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 123

procedure Stacks.Put (Stack : in Root_Stack_Type’Class) is

Iterator : <what’s its type?> := <how do we get one for Stack’s type?>begin while not Is_Done (Iterator) loop ...

Page 124: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 124

What's A Factory Method?

• If you need an iterator for this type of stack, then just ask the stack for one.

• A “factory method” is a constructor that dispatches on one type, and returns a value of some other type.

• In Ada95, the return type has to be class–wide, since an operation can only be primitive for one type.

Page 125: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 125

generic type Item_Type is private;package Stacks is

type Root_Stack_Type is abstract tagged limited null record;

type Root_Iterator_Type is abstract tagged null record;

-- Here’s the factory method: -- function Start_At_Top (Stack : Root_Stack_Type) return Root_Iterator_Type'Class is abstract;

Page 126: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 126

procedure Stacks.Put (Stack : in Root_Stack_Type’Class) is

Iterator : Root_Iterator_Type’Class := Start_At_Top (Stack);begin while not Is_Done (Iterator) loop

… Get_Item (Iterator) …

Advance (Iterator);

end loop;

New_Line;end Stacks.Put;

Page 127: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 127

generic

Max_Depth : in Positive;

package Stacks.Bounded_G is

type Stack_Type is new Root_Stack_Type with private;

type Iterator_Type is new Root_Iterator_Type with private;

function Start_At_Top (Stack : Stack_Type) return Root_Iterator_Type'Class;

Page 128: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 128

Copying A Stack• Requires care, because it’s easy to populate the

target stack in reverse order.• You can either (1) traverse the items in the source

stack in bottom-to-top order, and populate the target stack in the normal way (using Push); or,

• You can (2) traverse the items in the source stack in top-to-bottom order, and populate the target stack in reverse order, using a special operation (like Copy).

Page 129: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 129

procedure Copy_That_Does_Not_Work (From : in Root_Stack_Type’Class; To : in out Root_Stack_Type’Class) is Iter : Root_Iterator_Type’Class := Start_At_Top (From);begin if <From and To are the same stack> then return; end if;

Clear (To); while not Is_Done (Iter) loop Push (Get_Item (Iter), On => To); Advance (Iter); end loop;end Copy_That_Does_Not_Work;

Page 130: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 130

procedure Stacks.Copy -- technique (1) (From : in Root_Stack_Type’Class; To : in out Root_Stack_Type’Class) is

Iterator : Root_Iterator_Type’Class := Start_At_Bottom (From);begin if From’Address = To’Address then -- per RM95 3.10 (9) and 13.3 (16) return; end if;

Clear (To); while not Is_Done (Iterator) loop Push (Get_Item (Iterator), On => To); Backup (Iterator); end loop;end Stacks.Copy;

Page 131: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 131

package body Stacks.Bounded_G is

procedure Copy -- technique (2) (From : in Root_Stack_Type'Class; To : in out Stack_Type) is

Depth : constant Natural := Get_Depth (From);

Iterator : Root_Iterator_Type'Class := Start_At_Top (From);

use type System.Address; begin ...

Page 132: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 132

... if From'Address = To'Address then return; end if;

if Depth > Max_Depth then raise Storage_Error; end if;

To.Top := Depth;

for I in reverse 1 .. Depth loop To.Items (I) := Get_Item (Iterator); Advance (Iterator); end loop; end Copy;

Page 133: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 133

Summary of Stack Copying

• Technique (1) requires that you be able to traverse stacks in reverse order.

• Technique (1) can be implemented as a class-wide operation, or as a primitive operation with a default implementation.

• Technique (2) must be implemented as a primitive operation, for each type, because it needs to know the type’s representation.

Page 134: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 134

Singleton

Page 135: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 135

Using a State Machine Package

• The package itself is the object – an instance of an anonymous type.

• State data is declared in the package body and manipulated via public operations.

• Popular in Ada because static-ness is the default for objects and operations.

Page 136: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 136

with Ownship_Types; use Ownship_Types;

package Ownship is

procedure Update;

function Get_Speed_In_Knots return Speed_In_Knots_Type;

function Get_Heading_In_Deg return Heading_In_Deg_Type;

procedure Set_Heading (Heading : in Heading_In_Deg_Type);

... end Ownship;

Page 137: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 137

with Ownship;

package body P is

… procedure Op is

Ownship_Speed : constant Speed_In_Knots_Type := Ownship.Get_Speed_In_Knots; begin

<do something with speed>

end Op;

Page 138: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 138

Using a Named Type

• Instance creation can be controlled by the abstraction by declaring the type as limited and indefinite.

• The (single) instance of the type is declared in the package body.

• A public operation returns an access object designating the singleton instance.

• All operations of the type take access parameters (so no explicit deref is req’d).

Page 139: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 139

package Ownships is

type Ownship_Type (<>) is limited private;

function Get_Speed_In_Knots (Ownship : access Ownship_Type) return Speed_In_Knots_Type; ... type Ownship_Access is access all Ownship_Type;

function Ownship return Ownship_Access;

private

type Ownship_Type is …;

end Ownships;

Page 140: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 140

with Ownships; use Ownships;

package body P is

… procedure Op is

Ownship_Speed : constant Speed_In_Knots_Type := Get_Speed_In_Knots (Ownship); begin

<do something with speed>

end Op;

Page 141: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 141

package body Ownships is

function Get_Speed_In_Knots (Ownship : access Ownship_Type) return Speed_In_Knots_Type is begin return Ownship.Speed; end; ... Singleton : aliased Ownship_Type;

function Ownship return Ownship_Access is begin return Singleton'Access; end;

end Ownships;

Page 142: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 142

Well-Known Objects

• Well-known objects are global abstractions that have a defined cardinality.

• They tend to be passive holders of system–wide state.

• A singleton is a well-known object whose cardinality happens to be 1.

• Use a discrete identifier to refer to a specific instance.

Page 143: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 143

with Rodmeter_Types; use Rodmeter_Types;package Rodmeters is

type Rodmeter_Id is range 1 .. 2;

procedure Update (Rodmeter : in Rodmeter_Id);

function Get_Speed_In_Knots (Rodmeter : Rodmeter_Id) return Speed_In_Knots_Type;

procedure Set_Bias (Rodmeter : in Rodmeter_Id; Bias : in Bias_In_Knots_Type);

function Get_Bias (Rodmeter : Rodmeter_Id) return Bias_In_Knots_Type;

end Rodmeters;

Page 144: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 144

with Ownships, Ownship_Types;

package body Rodmeters is

type Speed_Array_Type is array (Rodmeter_Id) of Speed_In_Knots_Type;

Speed_Array : Speed_Array_Type;

type Bias_Array_Type is array (Rodmeter_Id) of Bias_In_Knots_Type;

Bias_Array : Bias_Array_Type;

...

Page 145: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 145

function Get_Speed_In_Knots (Rodmeter : Rodmeter_Id) return Speed_In_Knots_Type is begin return Speed_Array (Rodmeter); end;

procedure Set_Bias (Rodmeter : in Rodmeter_Id; Bias : in Bias_In_Knots_Type) is begin Bias_Array (Rodmeter) := Bias; end;

function Get_Bias (Rodmeter : Rodmeter_Id) return Bias_In_Knots_Type is begin return Bias_Array (Rodmeter); end;

Page 146: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 146

procedure Update (Rodmeter : in Rodmeter_Id) is

OS_Speed : constant OS_Types.Speed_In_Knots_Type := Get_Speed_In_Knots (Ownship);

Speed : Speed_In_Knots_Type'Base := Speed_In_Knots_Type'Base (OS_Speed) + Speed_In_Knots_Type'Base (Bias_Array (Rodmeter));

begin

if Speed < 0.0 then Speed := 0.0; elsif Speed > Speed_In_Knots_Type'Last then Speed := Speed_In_Knots_Type'Last; end if;

Speed_Array (Rodmeter) := Speed;

end Update;

Page 147: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 147

package TCP.States is

type Root_State_Type (<>) is abstract tagged limited private;

type State_Access is access all Root_State_Type'Class;

type Root_Connection_Type is abstract tagged limited null record;

procedure Set_State (Connection : in out Root_Connection_Type; State : in State_Access) is abstract;

procedure Transmit (State : access Root_State_Type; Connection : in out Root_Connection_Type'Class; Item : in Stream_Element_Array); ...

Page 148: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 148

package TCP.States.Listen is

type Listen_State_Type is new Root_State_Type with private;

procedure Send (State : access Listen_State_Type; Connection : in out Root_Connection_Type'Class);

function State return State_Access;

private

type Listen_State_Type is new Root_State_Type with null record;

end TCP.States.Listen;

Page 149: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 149

with TCP.States.Established;

package body TCP.States.Listen is

Singleton : aliased Listen_State_Type;

procedure Send (State : access Listen_State_Type; Connection : in out Root_Connection_Type'Class) is begin ... Set_State (Connection, Established.State); end Send;

function State return State_Access is begin return Singleton'Access; end;

end TCP.States.Listen;

Page 150: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 150

package TCP.Connections is

type Connection_Type is limited private; ...private

function Get_Default return State_Access;

type Connection_Type is new Root_Connection_Type with record State : State_Access := Get_Default; File : Streams.File_Type; end record;

procedure Set_State (Connection : in out Connection_Type; State : in State_Access);

end TCP.Connections;

Page 151: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 151

Strategy

Page 152: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 152

What’s A Strategy?

• The simple answer: a fancy name for a generic formal subprogram.

• It’s a way to parameterize a component.

• You effect different behavior by plugging in a different algorithm (the “strategy”).

Page 153: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 153

generic

type Item_Type is limited private;

package Storage_Nodes is

type Storage_Node; type Storage_Node_Access is access all Storage_Node;

type Storage_Node is limited record Item : aliased Item_Type; Next : Storage_Node_Access; end record;

procedure Do_Nothing (Node : in out Storage_Node);

end Storage_Nodes;

Page 154: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 154

with Storage_Nodes;

generic

with package Nodes is new Storage_Nodes (<>);

use Nodes;

with procedure Finalize (Node : in out Storage_Node) is Do_Nothing;

package Storage is

function New_Node return Storage_Node_Access;

procedure Free (Node : in out Storage_Node_Access);

end Storage;

Page 155: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 155

package body Storage is

Free_List : Storage_Node_Access;

function New_Node return Storage_Node_Access is ...

procedure Free (Node : in out Storage_Node_Access) is begin if Node = null then return; end if;

Finalize (Node.all);

Node.Next := Free_List; Free_List := Node; Node := null; end Free;

end Storage;

Page 156: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 156

with Storage_Nodes;

generic type Item_Type is private;package Unbounded_Stacks is

type Stack_Type is limited private; ...private

package Nodes is new Storage_Nodes (Item_Type); use Nodes;

type Stack_Type is limited record Top : Storage_Node_Access; end record;

end Unbounded_Stacks;

Page 157: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 157

with Storage;

package body Unbounded_Stacks is

package Stack_Storage is new Storage (Nodes); use Stack_Storage;

procedure Pop (Stack : in out Stack_Type) is

Node : Storage_Node_Access := Stack.Top; begin Stack.Top := Stack.Top.Next; Free (Node); end Pop;

…end Unbounded_Stacks;

Page 158: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 158

with Storage_Nodes;

generic type Item_Type is private;package Lists is

type List_Type is private; … procedure Clear (List : in out List_Type);

private

package Nodes is new Storage_Nodes (Item_Type); use Nodes;

type List_Type is record Head : Storage_Node_Access; end record;

end Lists;

Page 159: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 159

with Storage;

package body Lists is

procedure Finalize (Node : in out Storage_Node);

package List_Storage is new Storage (Nodes, Finalize); use List_Storage;

procedure Finalize (Node : in out Storage_Node) is begin Free (Node.Next); end; ... procedure Clear (List : in out List_Type) is begin Free (List.Head); end;

end Lists;

Page 160: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 160

Generic Dispatching

Page 161: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 161

Motivation

• Suppose we want to import, as generic formal parameters, a tagged type and one of its primitive operations, and we want dynamically dispatch the operation inside the generic.

• The problem is that you can’t dispatch on a formal subprogram, because a formal subprogram isn’t primitive for a formal type.

Page 162: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 162

package P is

type T is tagged limited private;

procedure Op (O : in out T); …end P;

package P.C is

type NT is new T with private;

procedure Op (O : in out NT); …end P.C;

Page 163: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 163

generic

type T (<>) is abstract tagged limited private;

with procedure Op (O : in out T) is <>;

package GQ is

procedure Do_Something (O : in out T'Class);

end GQ;

Page 164: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 164

• Here’s what we want to do:

with P, GQ;

package Q is new GQ (T => P.T, Op => P.Op);

Page 165: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 165

package body GQ is

procedure Do_Something (O : in out T'Class) is begin Op (O); --here’s the offending line end;

end GQ;

gq.adb:6:10: class-wide argument not allowed heregq.adb:6:10: "Op" is not a primitive operation of "T"

Page 166: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 166

• The problem is that the compiler has no way of knowing (at the time of compilation of the generic) that formal procedure Op is really primitive for type T. So it assumes the worst, and doesn’t allow you to dispatch on a formal operation.

• The actual operation we import must be statically bound to T. What we can do is import the class-wide type, T’Class, and import a class-wide operation (that takes an object of type T’Class) that calls the (primitive) dispatching operation.

Page 167: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 167

Changes to Client

• Implement a new, class-wide operation (as a child, if you don’t have it already):

procedure P.Call_Op (O : in out T'Class) isbegin Op (O); -- dispatchesend;

Page 168: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 168

Changes to Server

• Declare the formal type as non-tagged and indefinite. This allows a class-wide type to be used as the generic actual.

• In the generic operations, declare the formal parameters to be of type T instead of T’Class. (This is required anyway, because the formal type isn’t tagged anymore.)

Page 169: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 169

generic

type T (<>) is limited private;

with procedure Op (O : in out T) is <>;

package GQ is

procedure Do_Something (O : in out T);

end GQ;

Page 170: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 170

package body GQ is

procedure Do_Something (O : in out T) is begin ... Op (O); -- legal (static call) ... end;

end GQ;

Page 171: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 171

Changes to Instantiation

• Now let’s instantiate the new version of the generic using T’Class as the actual type, and our special class-wide operation as the actual operation:

with GQ, P.Call_Op;

package Q is new GQ (P.T'Class, P.Call_Op);

Page 172: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 172

with P.C, Q;

procedure Test_Q is

OT : P.T; -- T is root of class

ONT : P.C.NT; -- NT derives from T

begin

Q.Do_Something (OT); -- call T’s Op

Q.Do_Something (ONT); -- call NT’s Op

end Test_Q;

Page 173: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 173

The Rosen Trick

Page 174: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 174

Ada I/O Model

• A “communication path,” such as disk I/O, socket I/O, etc, is modeled as a “file.”

• You “open the file” to establish communication with a device.

• You “close the file” to sever the connection to the device.

Page 175: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 175

• The “communication path” to a device is represented as a handle, which designates connection state. The state may change, but the handle itself does not.

Page 176: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 176

package Files is

type File_Type is limited private;

procedure Open(File : in out File_Type; Name : in String);

procedure Close(File : in out File_Type);

procedure Read(File : in File_Type; Item : out Item_Type);

procedure Write(File : in File_Type; Item : in Item_Type);

Page 177: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 177

Issue

• Read and Write are state-changing operations, yet the File object (the “handle”) is passed as an in-mode parameter.

• How to we implement File_Type in order to implement this model?

Page 178: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 178

Use the Heap

private

type Connection_State;

type File_Type is access all Connection_State;

end Files;

Page 179: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 179

Use the Heap (cont’d)procedure Open (File : in out File_Type; Name : in String) isbegin File := new Connection_State;...end Open;

procedure Write (File : in File_Type ...) isbegin File.all := ...end;

Page 180: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 180

Use the Heap: Consequences

• Well, requires heap use. In general, if given a choice, we’d rather use the stack.

• The declaration of a named access type means you can’t declare the package using pragma Pure.

• In order to prevent memory leaks, you have to implement File_Type as controlled. This adds a certain amount of heaviness.

Page 181: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 181

Use Static Allocation

private

type File_Type is limited record Index : Natural := 0; end record;

end Files;

Page 182: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 182

Use Static Allocation (cont’d)package body Files is

type Descriptor_Type is ... Descriptors : array (1 .. 20) of Descriptor_Type;

procedure Open (File : in out File_Type; ...) is begin File.Index := Get_Descriptor_Index;

Page 183: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 183

Static Allocation: Consequences

• Limits number of file objects (although there’s probably a system-defined limit anyway).

• Having package state means you won’t be able to declare the package using pragma Pure.

Page 184: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 184

Use Chapter 13 Tricks

private

type File_Type is limited record <connection state> end record;

end Files;

Page 185: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 185

Chapter 13 (cont’d)

package body Files is

package Address_To_Access_Conversions is new System.Addr_To_Acc_Conversions...

procedure Write (File : in File_Type; ...) is FA : const Object_Pointer := To_Pointer(File’Address);

F : File_Type renames FA.all; begin

Page 186: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 186

Chapter 13: Consequences

• Using ‘Address turns off type-checking.

Page 187: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 187

The Rosen Trick

• A clean way to modify a limited (by–reference) in-mode subprogram parameter, that doesn’t require any Chap 13 tricks.

• Allocate memory for the handle directly adjacent to the connection state, on the stack. No package state is necessary.

Page 188: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 188

generic type Result_Subtype is (<>);

package Ada.Numerics.Discrete_Random is

type Generator is limited private;

function Random (Gen : Generator) return Result_Subtype;...private

type Handle_Type (Gen : access Generator) is limited null record;

type Generator is limited record Handle : Handle_Type (Generator’Access); Gen_State : State; end record;

Page 189: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 189

function Random (Gen : Generator) return Result_Subtype is

Gen_State : State renames Gen.Handle.Gen.Gen_State;

begin

<modify Gen_State as necessary>

return <random number>;

end Random;

Page 190: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 190

type File_Type is limited private; ... procedure Read (File : in File_Type; Key : in Key_Type; Item : out Item_Type);

procedure Write (File : in File_Type; Item : in Item_Type);

procedure Remove (File : in File_Type; Key : in Key_Type);

Page 191: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 191

private

...

type Handle_Type (File : access File_Type) is limited null record;

type File_Type is limited record Handle : Handle_Type (File_Type'Access); File : Stream_IO.File_Type; File_Index : Stream_IO.Positive_Count; Root_Page : Page_Type; end record;

Page 192: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 192

procedure Read (File : in File_Type; Key : in Key_Type; Item : out Item_Type) is

Index : Natural; Found : Boolean;

File_Index : Stream_IO.Count; Page : Page_Type;

begin

Set_Mode (File.Handle.File.File, In_File);

...

Page 193: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 193

procedure Write (File : in out File_Type; Item : in Item_Type) is

...

begin

Set_Mode (File.Handle.File.File, In_File); ...

Reset (File.Handle.File.File); Count'Write (Stream (File.File), File.File_Index);

Flush (File.Handle.File.File);

end Write;

Page 194: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 194

Collections Of Limited Items

Page 195: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 195

• Generic data structures typically declare the generic formal item type as non-limited:

generic

type Item_Type is private;

Max_Depth : in Positive;

package Stacks is …;

Page 196: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 196

• This gives you assignment, allowing you to implement the insertion operation by copying the formal parameter:

procedure Push (Item : in Item_Type; On : in out Stack_Type) isbegin On.Top := On.Top + 1; On.Items (On.Top) := Item; -- copyend Push;

Page 197: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 197

• Other modifier operations are implemented the same way, by copying the formal parameter:

procedure Set_Top (Stack : in out Stack_Type; Item : in Item_Type) isbegin Stack.Items (Stack.Top) := Item; -- copyend;

Page 198: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 198

• But suppose you need a collection of items whose type is limited?

with Stacks;with Ada.Text_IO; use Ada.Text_IO;

package File_Stacks is new Stacks (Item_Type => File_Type, -- illegal Max_Depth => 10);

Page 199: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 199

• We can solve the problem by rewriting modifier operations as functions that return a reference to the object.

• This works because assignment isn’t necessary for the implementation of these special modifiers.

• It’s the client who does the modify, by manipulating the actual item (instead of the supplier internally replacing it with a copy).

Page 200: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 200

Stack : aliased Stack_Type;...declare File : File_Type renames Push (Stack’Access).all; -- -- There’s now a new File object on -- the top of the Stack.begin Open (File, Out_File, “myfile.dat”); ... Reset (File); ... Close (File);end;

Pop (Stack);

Page 201: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 201

• Now let’s see what Set_Top will look like:

Stack : aliased Stack_Type;…Reset (File => Set_Top (Stack’Access).all);…Close (File => Set_Top (Stack’Access).all);

Page 202: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 202

• Strictly speaking, Push doesn’t have to be implemented by returning an access object, if you have Set_Top too:

Push (Stack); -- note: no Item parameter…Open (File => Set_Top (Stack’Access).all, Mode => Out_File, Name => “myfile.dat”);

Page 203: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 203

How do we do it? (public part)

• Declare the generic formal item type as limited private.

• Declare a general access type that designates the item type.

• Declare the modifier operations as functions that return the access type, and take the stack as an access parameter.

Page 204: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 204

generic type Item_Type is limited private; Max_Depth : in Positive;package Stacks is

type Stack_Type is limited private;

type Item_Access is access all Item_Type;

function Push (Stack : access Stack_Type) return Item_Access;

function Set_Top (Stack : access Stack_Type) return Item_Access; ...

Page 205: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 205

How do we do it? (private part)

• Declare the item array with aliased components.

Page 206: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 206

private

type Item_Array is array (1 .. Max_Depth) of aliased Item_Type;

subtype Top_Range is Natural range 0 .. Max_Depth;

type Stack_Type is limited record Items : Item_Array; Top : Top_Range := 0; end record;

end Stacks;

Page 207: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 207

How do we do it? (body)

• Implement the modifier operations by returning an access object designating the item on the top of the stack.

Page 208: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 208

package body Stacks is

function Push (Stack : access Stack_Type) return Item_Access is begin Stack.Top := Stack.Top + 1; return Stack.Items (Stack.Top)’Access; end;

function Set_Top (Stack : access Stack_Type) return Item_Access is begin return Stack.Items (Stack.Top)’Access; end; ...

Page 209: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 209

How do we do it? (body)

• The alternate version of Push only needs to increment the Top index (you actually modify the item later, using Set_Top):

procedure Push (Stack : in out Stack_Type) isbegin Stack.Top := Stack.Top + 1;end;

Page 210: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 210

For non-limited items too

• This technique isn’t specific to collections of limited items. You could use it for non-limited items too, if you prefer to modify the actual item in place, instead of (internally) replacing the item with a copy.

Set_Top (Stack’Access).all := Value;

Modify (Item => Set_Top (Stack’Access).all);

Page 211: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 211

Applied to active iterators

• You can generalize this idea, allowing you to modify any item in the collection, by using an active iterator:

Set_Item (Iterator).all := Value;

Modify (Item => Set_Item (Iterator).all);

Page 212: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 212

Command

Page 213: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 213

What’s A Command Object?

• An object that manages the processing that occurs when a user issues a command.

• Store the command in a ring and use it to implement undo and redo.

• Store the command on disk to implement record and playback.

• Store the command in a queue and execute it later.

Page 214: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 214

package Commands is

type Root_Command_Type (<>) is abstract tagged limited private;

type Command_Access is access all Root_Command_Type'Class;

procedure Execute (Command : access Root_Command_Type) is abstract;

procedure Free (Command : in out Command_Access); …

Page 215: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 215

…private

type Root_Command_Type is abstract tagged limited null record;

procedure Do_Free (Command : access Root_Command_Type);

end Commands;

Page 216: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 216

package body Commands is

procedure Free (Command : in out Command_Access) is begin if Command /= null then Do_Free (Command); Command := null; end if; end Free;

procedure Do_Free (Command : access Root_Command_Type) is begin null; end;

end Commands;

Page 217: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 217

package Commands.Open_Commands is

type Command_Type is new Root_Command_Type with private;

function New_Command (App : access Application_Type'Class) return Command_Access;

procedure Execute (Command : access Command_Type); …

Page 218: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 218

...private

type Command_Type (App : access Application_Type'Class) is new Root_Command_Type with null record;

procedure Do_Free (Command : access Command_Type);

end Commands.Open_Commands;

Page 219: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 219

package body Commands.Open_Commands is

type Open_Command_Access is access all Command_Type;

procedure Deallocate is new Ada.Unchecked_Deallocation (Command_Type, Open_Command_Access);

function New_Command (App : access Application_Type'Class) return Command_Access is

Command : constant Open_Command_Access := new Command_Type (App); begin return Command_Access (Command); end;

Page 220: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 220

procedure Do_Free (Command : access Command_Type) is

CA : Open_Command_Access := Open_Command_Access (Command); begin Deallocate (CA); end;

procedure Execute (Command : access Command_Type) is

Doc : constant Document_Access := New_Doc (Name => Get_Filename_From_User); begin Add (Doc, To => Command.App); Documents.Open (Doc); end Execute;

end Commands.Open_Commands;

Page 221: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 221

App : aliased Application_Type; ... declare Command : Command_Access := Open_Commands.New_Command (App'Access); begin <install command as value of Open button> end; …

procedure Execute_Menu_Function_CB (W : in Widget) is begin Execute (W.Command); end;

Page 222: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 222

generic

type Receiver_Type (<>) is limited private;

with procedure Action (Receiver : in out Receiver_Type);

package Commands.Simple is

type Command_Type is new Root_Command_Type with private;

function New_Command (Receiver : access Receiver_Type) return Command_Access;

procedure Execute (Command : access Command_Type); ...end Commands.Simple;

Page 223: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 223

package body Commands.Simple is ... procedure Execute (Command : access Command_Type) is begin Action (Command.Receiver.all); end;

end Commands.Simple;

Page 224: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 224

package Applications is

type Application_Type is tagged limited private; ...end Applications;

procedure Applications.Count_Docs (App : in out Application_Type'Class);

Page 225: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 225

with Applications.Count_Docs; use Applications;with Commands.Simple;

package Commands.App_Commands is new Simple (Receiver_Type => Application_Type'Class, Action => Count_Docs);

(This is similar to the “generic dispatching” trick.)

Page 226: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 226

Composite

Page 227: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 227

What’s A Composite?

• Technique for assembling recursive data structures.

• Example: a macro command (a command comprising a set of commands).

• Example: UNIX directory tree (a directory is a file comprising other files).

Page 228: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 228

package Equipment is

type Equipment_Type (<>) is abstract tagged limited private;

type Equipment_Access is access all Equipment_Type'Class;

type Power_In_Watts_Type is delta 0.1 range 0.0 .. 100.0;

type Dollars_Type is delta 0.01 digits 6;

function Get_Power_In_Watts (Equipment : access Equipment_Type) return Power_In_Watts_Type is abstract;

function Get_Price_In_Dollars (Equipment : access Equipment_Type) return Dollars_Type is abstract;

Page 229: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 229

type Composite_Type is abstract new Equipment_Type with private;

type Composite_Access is access all Composite_Type'Class;

function Get_Power_In_Watts (Composite : access Composite_Type) return Power_In_Watts_Type;

function Get_Price_In_Dollars (Composite : access Composite_Type) return Dollars_Type;

procedure Add (Equipment : access Equipment_Type'Class; To : access Composite_Type);

Page 230: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 230

procedure Free (Equipment : in out Equipment_Access);

private

type Equipment_Type is abstract tagged limited record Next : Equipment_Access; end record;

procedure Do_Free (Equipment : access Equipment_Type);

Page 231: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 231

type Composite_Type is abstract new Equipment_Type with record Head : Equipment_Access; end record;

function Do_Get_Price (Composite : Composite_Type) return Dollars_Type;

function Do_Get_Power (Composite : Composite_Type) return Power_In_Watts_Type;

procedure Free_Items (Composite : access Composite_Type'Class);

end Equipment;

Page 232: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 232

package body Equipment is

procedure Do_Free (Equipment : access Equipment_Type) is begin raise Program_Error; end;

procedure Free (Equipment : in out Equipment_Access) is begin if Equipment /= null then Do_Free (Equipment); Equipment := null; end if; end Free;

Page 233: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 233

function Do_Get_Price (Composite : Composite_Type) return Dollars_Type is begin return 0.0; end;

function Do_Get_Power (Composite : Composite_Type) return Power_In_Watts_Type is begin return 0.0; end;

Page 234: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 234

function Get_Power_In_Watts (Composite : access Composite_Type) return Power_In_Watts_Type is

Power : Power_In_Watts_Type := Do_Get_Power (Composite_Type’Class (Composite.all));

Item : Equipment_Access := Composite.Head;

begin

while Item /= null loop Power := Power + Get_Power_In_Watts (Item); Item := Item.Next; end loop;

return Power;

end Get_Power_In_Watts;

Page 235: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 235

function Get_Price_In_Dollars (Composite : access Composite_Type) return Dollars_Type is

Price : Dollars_Type := Do_Get_Price (Composite_Type’Class (Composite.all));

Item : Equipment_Access := Composite.Head;

begin

while Item /= null loop Price := Price + Get_Price_In_Dollars (Item); Item := Item.Next; end loop;

return Price;

end Get_Price_In_Dollars;

Page 236: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 236

procedure Add (Equipment : access Equipment_Type'Class; To : access Composite_Type) is

Item : constant Equipment_Access := Equipment_Access (Equipment);

begin

pragma Assert (Equipment.Next = null, "equipment already on another list");

Item.Next := To.Head; To.Head := Item;

end Add;

Page 237: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 237

procedure Free_Items (Composite : access Composite_Type'Class) is

Item : Equipment_Access; Head : Equipment_Access renames Composite.Head;

begin

while Head /= null loop Item := Head; Head := Head.Next; Do_Free (Item); end loop;

end Free_Items;

end Equipment;

Page 238: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 238

package Equipment.Hard_Disks is

type Hard_Disk_Type is new Equipment_Type with private;

function New_Hard_Disk return Equipment_Access;

function Get_Price_In_Dollars (Hard_Disk : access Hard_Disk_Type) return Dollars_Type;

function Get_Power_In_Watts (Hard_Disk : access Hard_Disk_Type) return Power_In_Watts_Type;

Page 239: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 239

...private

type Hard_Disk_Type is new Equipment_Type with null record;

procedure Do_Free (Hard_Disk : access Hard_Disk_Type);

end Equipment.Hard_Disks;

Page 240: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 240

package body Equipment.Hard_Disks is

type Hard_Disk_Access is access all Hard_Disk_Type;

procedure Free is new Ada.Unchecked_Deallocation (Hard_Disk_Type, Hard_Disk_Access);

...

Page 241: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 241

function New_Hard_Disk return Equipment_Access is

Hard_Disk : constant Hard_Disk_Access := new Hard_Disk_Type; begin return Equipment_Access (Hard_Disk); end;

procedure Do_Free (Hard_Disk : access Hard_Disk_Type) is

Hard_Disk_A : Hard_Disk_Access := Hard_Disk_Access (Hard_Disk); begin Free (Hard_Disk_A); end;

Page 242: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 242

function Get_Price_In_Dollars (Hard_Disk : access Hard_Disk_Type) return Dollars_Type is begin return 350.0; end;

function Get_Power_In_Watts (Hard_Disk : access Hard_Disk_Type) return Power_In_Watts_Type is begin return 20.0; end;

end Equipment.Hard_Disks;

Page 243: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 243

package Equipment.Cabinets is

type Cabinet_Type is new Composite_Type with private;

function New_Cabinet return Composite_Access;

private

type Cabinet_Type is new Composite_Type with null record;

procedure Do_Free (Cabinet : access Cabinet_Type);

function Do_Get_Price (Cabinet : Cabinet_Type) return Dollars_Type;

end Equipment.Cabinets;

Page 244: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 244

package body Equipment.Cabinets is

type Cabinet_Access is access all Cabinet_Type;

procedure Free is new Ada.Unchecked_Deallocation (Cabinet_Type, Cabinet_Access);

function New_Cabinet return Composite_Access is

Cabinet : constant Cabinet_Access := new Cabinet_Type; begin return Composite_Access (Cabinet); end;

Page 245: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 245

procedure Do_Free (Cabinet : access Cabinet_Type) is

Cabinet_A : Cabinet_Access := Cabinet_Access (Cabinet); begin Free_Items (Cabinet); Free (Cabinet_A); end Do_Free;

function Do_Get_Price (Cabinet : Cabinet_Type) return Dollars_Type is begin return Dollars_Type'(125.0); end;

end Equipment.Cabinets;

Page 246: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 246

declare Cabinet : Composite_Access := New_Cabinet; Chassis : Composite_Access := New_Chassis;

Price : Price_In_Dollars_Type; Power : Power_In_Watts_Type;begin Add (New_Floppy_Disk, To => Cabinet); Add (New_CD_ROM, To => Cabinet);

Add (New_Hard_Disk, To => Chassis);

Add (Chassis, To => Cabinet);

Price := Get_Price_In_Dollars (Cabinet); Power := Get_Power_In_Watts (Cabinet);end;

Page 247: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 247

package Commands.Macro_Commands is

type Command_Type is new Root_Command_Type with private;

type Macro_Command_Access is access all Command_Type;

function New_Command return Macro_Command_Access;

procedure Execute (Command : access Command_Type);

procedure Add (Command : access Root_Command_Type'Class; To : access Command_Type);

...

Page 248: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 248

private

type Command_Type is new Root_Command_Type with record Head : Command_Access; -- really, a queue end record;

procedure Do_Free (Command : access Command_Type);

end Commands.Macro_Commands;

Page 249: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 249

package body Commands.Macro_Commands is … procedure Execute (Command : access Command_Type) is

procedure Traverse_Then_Execute (C : in Command_Access) is begin if C /= null then Traverse_Then_Execute (C.Next); Execute (C); end if; end Traverse_Then_Execute;

begin

Traverse_Then_Execute (Command.Head);

end Execute;

Page 250: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 250

procedure Add (Command : access Root_Command_Type'Class; To : access Command_Type) is begin pragma Assert (Command.Next = null, "command already on another list");

Command.Next := To.Head; To.Head := Command_Access (Command); end;

end Commands.Macro_Commands;

Page 251: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 251

declare Command : constant Macro_Command_Access := New_Command;begin Add (New_Open (App’Access), To => Command); Add (New_Paste (Doc), To => Command); … Execute (Command); -- Execute all the commands contained by -- the macro command.end;

Page 252: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 252

Bounded Buffer

Page 253: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 253

What’s A Bounded Buffer?

• Classic way of asynchronously transferring data between producer(s) and consumer(s).

• Consumer gets an item from the buffer; it blocks if there’s nothing to get.

• Producer puts an item in the buffer; it blocks if the buffer is full.

Page 254: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 254

generic

type Item_Type is private;

package Buffers is

type Item_Array is array (Positive range <>) of Item_Type;

protected type Buffer_Type (Size : Positive) is

entry Put (Item : in Item_Type);

entry Get (Item : out Item_Type); ...

Page 255: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 255

...

private

Items : Item_Array (1 .. Size); Get_Index : Positive := 1; Put_Index : Positive := Size; Count : Natural := 0;

end Buffer_Type;

end Buffers;

Page 256: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 256

with Buffers;package Character_Buffers is new Buffers (Character);

Page 257: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 257

with Character_Buffers; use Character_Buffers;

package Consumers is

type Consumer_Type (Buffer : access Buffer_Type) is limited private;

private

task type Consumer_Type (Buffer : access Buffer_Type) is end;

end Consumers;

Page 258: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 258

declare Buffer : aliased Buffer_Type (Size => 5);

Consumer : Consumer_Type (Buffer'Access);begin for C in Character range ‘a’ .. ‘z’ loop Buffer.Put (C); end loop;end;

Page 259: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 259

package body Consumers is

task body Consumer_Type is

C : Character; begin

Main: loop Buffer.Get (C); <do something with character> end loop Main;

end Consumer_Type;

end Consumers;

Page 260: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 260

package body Buffers is

protected body Buffer_Type is

entry Put (Item : in Item_Type) when Count < Size is begin Put_Index := Put_Index mod Size + 1; Items (Put_Index) := Item; Count := Count + 1; end; ...

Page 261: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 261

... entry Get (Item : out Item_Type) when Count > 0 is begin Item := Items (Get_Index); Get_Index := Get_Index mod Size + 1; Count := Count - 1; end Get;

end Buffer_Type;

end Buffers;

Page 262: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 262

Termination Problem

• There’s a slight problem with this implementation: How do you terminate the consumer task?

• One way is to designate a value in the set of Item_Type as a meta-item that means “no more items.”

• This will work for discrete types like Character, but not easily for more complex data, or when there are multiple consumers.

Page 263: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 263

task body Consumer_Type is

C : Character; begin Main: loop

Buffer.Get (C);

exit Main when C = Latin_1.EOT;

<do something with character>

end loop Main; end Consumer_Type;

Page 264: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 264

declare Buffer : aliased Buffer_Type (Size => 5);

Consumer : Consumer_Type (Buffer'Access);begin for C in Character range ‘a’ .. ‘z’ loop Buffer.Put (C); end loop;

Buffer.Put (Latin_1.EOT);end;

Page 265: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 265

Termination Solution

• Give the producer a way to indicate explicitly that there are no more items.

• Pass status back to the consumer, to let her know all the items have been consumed.

• This solution is completely general, and works for any kind of data, and for any number of consumers.

Page 266: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 266

protected type Buffer_Type (Size : Positive) is

entry Put (Item : in Item_Type);

procedure Put_EOF;

entry Get (Item : out Item_Type; EOF : out Boolean); private

Items : Item_Array (1 .. Size); Get_Index : Positive := 1; Put_Index : Positive := Size; Count : Natural := 0; End_Of_File : Boolean := False;

end Buffer_Type;

Page 267: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 267

task body Consumer_Type is

C : Character; EOF : Boolean; begin Main: loop

Buffer.Get (C, EOF);

exit Main when EOF;

<do something with character>

end loop Main; end Consumer_Type;

Page 268: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 268

declare Buffer : aliased Buffer_Type (Size => 5);

Consumer : Consumer_Type (Buffer'Access);begin for C in Character range ‘a’ .. ‘z’ loop Buffer.Put (C); end loop;

Buffer.Put_EOF;end;

Page 269: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 269

protected body Buffer_Type is

entry Put (Item : in Item_Type) …; procedure Put_EOF is begin End_Of_File := True; end;

...

Page 270: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 270

entry Get (Item : out Item_Type; EOF : out Boolean) when Count > 0 or End_Of_File is begin if Count > 0 then

Item := Items (Get_Index); Get_Index := Get_Index mod Size + 1; Count := Count - 1;

EOF := False;

else

EOF := True;

end if; end Get;

Page 271: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 271

Resource Control

Lots O’ Stuff For Task Synchronization

Page 272: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 272

Canonical Form

• A protected object with protected subprograms that set and get the data.

• Simplicity has a price: protected operations cannot make blocking calls, and should only require a short amount of time to execute. These restrictions allow us to solve simple synchronization problems efficiently.

Page 273: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 273

type Time_Type is record Hour, Min, Sec : Natural; end record;

protected Protected_Time is procedure Set_Time (Time : in Time_Type);

function Get_Time return Time_Type;private Time : Time_Type;end Protected_Time;

Page 274: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 274

protected body Protected_Time is

procedure Set (Time : in Time_Type) is begin Protected_Time.Time := Time; end;

function Get_Time return Time_Type is begin return Time; end;

end Protected_Time;

Page 275: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 275

Semaphore

• A semaphore is a low-level primitive used to synchronize concurrent access to a resource.

• Yes, it’s still necessary in Ada95, even though we have protected types now. You’ll need it when you need to make blocking calls in the critical region.

• Use it only if you’re unable to satisfy the constraints of the canonical form.

Page 276: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 276

package Binary_Semaphores is

protected type Semaphore_Type is

procedure Release;

entry Seize;

private

In_Use : Boolean := False;

end Semaphore_Type;

end Binary_Semaphores;

Page 277: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 277

package body Binary_Semaphores is

protected body Semaphore_Type is

procedure Release is begin In_Use := False; end;

entry Seize when not In_Use is begin In_Use := True; end;

end Semaphore_Type;

end Binary_Semaphores;

Page 278: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 278

Semaphore : Semaphore_Type;Resource : Resource_Type;Task_1 : Task_1_Type;Task_2 : Task_2_Type;...task body Task_x_Type is begin … Semaphore.Seize;

<manipulate resource>

Semaphore.Release; …end Task_x_Type;

Page 279: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 279

package Message_IO is

procedure Put_Line (Message : in String);

end Message_IO;

Page 280: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 280

with Ada.Text_IO;

package body Message_IO is

protected Synchronization is

procedure Put_Line (Message : in String);

end Synchronization;

procedure Put_Line (Message : in String) is begin Synchronization.Put_Line (Message); end; ...

Page 281: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 281

... protected body Synchronization is

procedure Put_Line (Message : in String) is begin -- Wrong! Per RM95 9.5.1 (8, 18) Ada.Text_IO.Put_Line (Message); end;

end Synchronization;

end Message_IO;

Page 282: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 282

package body Message_IO is

Semaphore : Semaphore_Type;

procedure Put_Line (…) is begin Semaphore.Seize;

-- OK; not inside a protected op. Ada.Text_IO.Put_Line (Message);

Semaphore.Release; end;

end Message_IO;

Page 283: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 283

Deadlock

X, Y, Z : Integer;Semaphore : Semaphore_Type;...Semaphore.Seize;…X := 0;…Z := Y / X; -- Oops! Raises CE...Semaphore.Release; -- Bigger oops! -- Sema not released.

Page 284: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 284

package Binary_Semaphores.Controls is

type Semaphore_Control (Semaphore : access Semaphore_Type) is limited private;

private

type Semaphore_Control (Semaphore : access Semaphore_Type) is new Limited_Controlled with null record;

procedure Initialize (...); procedure Finalize (...);

end Binary_Semaphores.Controls;

Page 285: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 285

package body Binary_Semaphores.Controls is

procedure Initialize (Control : in out Semaphore_Control) is begin Control.Semaphore.Seize; end;

procedure Finalize (Control : in out Semaphore_Control) is begin Control.Semaphore.Release; end;

end Binary_Semaphores.Controls;

Page 286: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 286

Deadlock AvoidedX, Y, Z : Integer;Sema : aliased Semaphore_Type;…declare Control : Semaphore_Control (Sema’Access); -- Automatically seizes semaphore.begin … X := 0; … Z := Y / X; -- Oops! Raises CE. …end;-- OK; semaphore released automatically.

Page 287: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 287

Concurrent Stackgeneric ...package Stacks is

type Stack_Type is limited private;

procedure Push (Item : in Item_Type; On : in out Stack_Type);

procedure Pop (Stack : in out Stack_Type);

function Get_Top (Stack : Stack_Type) return Item_Type;

Page 288: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 288

...private

type Item_Array is array (1 .. Max_Depth) of Item_Type;

type Stack_Type is limited record Items : Item_Array; Top : Natural := 0; Sema : aliased Semaphore_Type; end record;

end Stacks;

Page 289: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 289

package body Stacks is

procedure Push (Item : in Item_Type; On : in out Stack_Type) is

Control : Semaphore_Control (On.Sema'Access); begin On.Top := On.Top + 1; On.Items (On.Top) := Item; end;

procedure Pop (Stack : in out Stack_Type) is

Control : Semaphore_Control (Stack.Sema'Access); begin Stack.Top := Stack.Top - 1; end;

Page 290: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 290

... function Get_Top (Stack : Stack_Type) return Item_Type is

Control : Semaphore_Control (Stack.Sema'Access); ^^^^^^^^^^^^^^^^^ begin

return SA.Items (SA.Top);

end Get_Top;

end Stacks;

stacks.adb:42:40: access-to-variable designates constant

Page 291: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 291

package body Stacks is

package Addr_To_Acc_Conversions is new System.Address_To_Access_Conversions (Stack_Type); ... function Get_Top (Stack : Stack_Type) return Item_Type is

SA : constant Object_Pointer := To_Pointer (Stack'Address); -- OK, per RM95 13.3 (16), because Stack_Type is -- a by-reference type (full view is limited).

Control : Semaphore_Control (SA.Sema'Access);

begin

return SA.Items (SA.Top);

end Get_Top;

end Stacks;

Page 292: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 292

package Stacks is

type Stack_Type is limited private; ...private ... type Handle_Type (Stack : access Stack_Type) is limited null record;

type Stack_Type is limited record Handle : Handle_Type (Stack_Type'Access); Items : Item_Array; Top : Natural := 0; Sema : aliased Semaphore_Type; end record;

end Stacks;

Page 293: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 293

function Get_Top (Stack : Stack_Type) return Item_Type is

Control : Semaphore_Control (Stack.Handle.Stack.Sema'Access);

begin

return Stack.Items (Stack.Top);

end Get_Top;

Page 294: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 294

Concurrent Stack Note

• To be honest: the example is a bit contrived, and was really designed to show a couple of ways of converting from a constant view of an object to a variable view.

• We don’t really need a semaphore here, because no blocking calls are made. The canonical monitor form satisfies our synchronization needs, and is probably more efficient anyway.

Page 295: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 295

• In general, you want to advertise that your abstraction synchronizes its callers.

• This is especially true if the abstraction has blocking behavior. This allows your callers to make timed or conditional entry calls.

• In practice this will mean wrapping the data inside a protected object. Clients that need access to the resource use the protected interface explicitly.

Page 296: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 296

protected Stack is

procedure Push (I : Integer);

procedure Pop;

function Top return Integer;

private

List : List_Type;

end Stack;

Page 297: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 297

protected body Stack is

procedure Push (I : Integer) is begin Push_Back (List, I); end;

procedure Pop is begin Pop_Back (List); end;

function Top return Integer is begin return Get_Back (List); end;

end Stack;

Page 298: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 298

protected Queue is

entry Remove (Item : out Integer);

procedure Add (Item : in Integer);

private

List : List_Type;

end Queue;

Page 299: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 299

protected body Queue is

entry Remove (Item : out Integer) when not Is_Empty (List) is begin Item := Get_Front (List); Pop_Front (List); end;

procedure Add (Item : in Integer) is begin Push_Back (List, Item); end;

end Queue;

Page 300: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 300

generic ...package Stacks is

type Stack_Type is limited private;

function "=" (L, R : Stack_Type) return Boolean; …end Stacks;

Static Locking Order

Page 301: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 301

package body Stacks is function Do_Equality (L, R : Stack_Type) return Boolean is begin

if L.Top /= R.Top then return False; end if;

for I in Integer range 1 .. L.Top loop if L.Items (I) /= R.Items (I) then return False; end if; end loop;

return True;

end Do_Equality; ...

Page 302: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 302

... function "=" (L, R : Stack_Type) return Boolean is

LA : constant Object_Pointer := To_Pointer (L'Address);

RA : constant Object_Pointer := To_Pointer (R'Address);

L_Control : Semaphore_Control (LA.Sema'Access);

R_Control : Semaphore_Control (RA.Sema'Access);

begin

return Do_Equality (L, R);

end "="; ...end Stacks;

Page 303: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 303

Deadlock Again

• First, if you compare a stack to itself, you’ll deadlock the second time you seize the semaphore (here, when you seize R).

• Second, the seizing of the pair of stacks is not an atomic operation. You’ll still deadlock because of circularity.

Page 304: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 304

S1, S2 : Stack_Type;T1 : Task_1_Type;T2 : Task_2_Type;

task body Task_1_Type isbegin … if S1 = S2 then …end;

task body Task_2_Type isbegin … if S2 = S1 then …end;

Page 305: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 305

A possible ordering of actions is:

Seize (S1.Sema); -- T1Seize (S2.Sema); -- T2Seize (S2.Sema); -- T1Seize (S1.Sema); -- T2

T1 is waiting for S2, which has already been seized by T2.

T2 is waiting for S1, which has already been seized by T1.

Circularity has caused deadlock.

Page 306: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 306

Removing the Circularity

• Define an order over the set of resources.

• Clients needing exclusive access to multiple resources lock them in resource order.

• By-reference resources already have an implicit order -- their address.

• Use RM95 13.3 (6) to get the address, and lock resources in order of their address.

Page 307: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 307

function "=" (L, R : Stack_Type) return Boolean is

LA : constant Object_Pointer := To_Pointer (L'Address);

RA : constant Object_Pointer := To_Pointer (R'Address);

begin

if L'Address < R'Address then

declare L_Control : Semaphore_Control (LA.Sema'Access); R_Control : Semaphore_Control (RA.Sema'Access); begin return Do_Equality (L, R); end;

...

Page 308: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 308

...

elsif L'Address = R'Address then

return True;

else -- R'Address < L'Address

declare R_Control : Semaphore_Control (RA.Sema'Access); L_Control : Semaphore_Control (LA.Sema'Access); begin return Do_Equality (L, R); end;

end if;

end "=";

Page 309: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 309

package Multiple_Reader_Semaphores is

type Seize_Kind is (For_Reading, For_Writing);

protected type Semaphore_Type is

entry Seize (Kind : in Seize_Kind);

procedure Release_For_Reading; procedure Release_For_Writing;

private

entry Waiting_To_Write;

Reader_Count : Natural := 0;

Writing : Boolean := False;

end Semaphore_Type;

end Multiple_Reader_Semaphores;

Page 310: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 310

package body Multiple_Reader_Semaphores is

protected body Semaphore_Type is

entry Seize (Kind : in Seize_Kind) when Waiting_To_Write'Count = 0 and not Writing is begin case Kind is when For_Reading => Reader_Count := Reader_Count + 1;

when For_Writing => requeue Waiting_To_Write with abort;

end case; end Seize;

...

Page 311: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 311

procedure Release_For_Reading is begin Reader_Count := Reader_Count - 1; end;

procedure Release_For_Writing is begin Writing := False; end;

entry Waiting_To_Write when Reader_Count = 0 is begin Writing := True; end;

end Semaphore_Type;

end Multiple_Reader_Semaphores;

Page 312: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 312

task body Reader_Type isbegin … Sema.Seize (For_Reading);

<read resource state> <make blocking calls> <read resource state again>

Sema.Release_For_Reading; …end Reader_Type;

Page 313: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 313

task body Writer_Type isbegin … Sema.Seize (For_Writing);

<change state of resource> <make blocking calls> <change state again>

Sema.Release_For_Writing; …end Writer_Type;

Page 314: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 314

package Counting_Semaphores is

protected type Semaphore_Type (Default : Natural := 1) is

entry Wait;

procedure Signal;

private

Count : Natural := Default;

end Semaphore_Type;

end Counting_Semaphores;

Page 315: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 315

package body Counting_Semaphores is

protected body Semaphore_Type is

entry Wait when Count > 0 is begin Count := Count - 1; end;

procedure Signal is begin Count := Count + 1; end;

end Semaphore_Type;

end Counting_Semaphores;

Page 316: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 316

Maitre_D : Semaphore_Type (Default => 4); -- Not req’d if you pick up chopsticks in order.

task body Philosopher_Task_Type is begin loop Maitre_D.Wait; -- to sit down at table

Pick_Up (Left); Pick_Up (Right);

delay Delay_Time; -- eat

Put_Down (Left); Put_Down (Right);

Maitre_D.Signal; -- leave the table

delay Delay_Time; -- think end loop;

Page 317: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 317

-- No maitre d’ is req’d, since we pick up the -- chopsticks according to their locking order.

task body Philosopher_Task_Type is begin loop Pick_Up (Chopstick_Id’Min (Left, Right)); Pick_Up (Chopstick_Id’Max (Left, Right));

delay Delay_Time; -- eat

Put_Down (Left); Put_Down (Right);

delay Delay_Time; -- think end loop; end Philosopher_Task_Type;

Page 318: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 318

Producer-Consumer

• When a producer of a resource must wait for it to be consumed, and the consumer must wait until the resource is produced.

• You can implement the communication using a task rendevous (the high-level way), or using a pair of semaphores (the low-level way).

Page 319: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 319

task Producer is

entry Get(Item : out Item_Type);end;

task body Producer isbegin

<produce item>

accept Get (Item : out Item_Type) Item := ... end;

...end Producer;

Page 320: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 320

task body Consumer isbegin

... Producer.Get(Item);

...

end Consumer;

Page 321: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 321

package Binary_Semaphores is

protected type Semaphore_Type (Default : Boolean := False) is

entry Wait;

procedure Signal;

private

Signaled : Boolean := Default;

end Semaphore_Type;

end Binary_Semaphores;

Page 322: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 322

Producer_Semaphore : Semaphore_Type (Default => False); Consumer_Semaphore : Semaphore_Type (Default => True);

task body Producer is begin loop -- Wait for consumer to finish consuming. Consumer_Semaphore.Wait;

<produce resource>

-- Let consumer know producer is done producing. Producer_Semaphore.Signal; end loop; end Producer;

Page 323: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 323

task body Consumer is begin loop -- Wait for producer to finish producing. Producer_Semaphore.Wait;

<consume resource> -- Let producer know consumer has consumed. Consumer_Semaphore.Signal; end loop; end Consumer;

Page 324: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 324

Recursive Semaphore

• When a task needs to seize a semaphore more than once prior to releasing it.

• Task that owns semaphore is allowed to call Seize multiple times without blocking.

• Other tasks wait if a task already owns semaphore. A new task assumes ownership when current task has Released every Seize.

Page 325: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 325

package Recursive_Semaphores is

protected type Semaphore_Type is

procedure Release;

entry Seize;

private

entry Waiting;

Owner : Ada.Task_Identification.Task_Id; Count : Natural := 0;

end Semaphore_Type;

end Recursive_Semaphores;

Page 326: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 326

package body Recursive_Semaphores is

protected body Semaphore_Type is

procedure Release is begin Count := Count - 1; end;

entry Seize when True is begin if Seize'Caller = Owner then Count := Count + 1; else requeue Waiting with abort; end if; end;

Page 327: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 327

entry Waiting when Count = 0 is begin Count := 1; Owner := Waiting'Caller; end;

end Semaphore_Type;

end Recursive_Semaphores;

Page 328: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 328

Semaphore : Semaphore_Type;

procedure Op1 isbegin Semaphore.Seize; ... if P then Op2; end if; ... Semaphore.Release;end Op1;

procedure Op2 isbegin Semaphore.Seize; … Semaphore.Release;end Ops;

Page 329: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 329

Persistent Signal

• To permanently record that an event has happened.

• Keep task(s) waiting until the event occurs. If the event has already happened, then task proceeds immediately.

• Useful for removing a task’s Initialize or Start entry that only gets called once.

Page 330: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 330

protected Task_Initialization is

entry Wait;

procedure Start;

private

OK_To_Start : Boolean := False;

end Task_Initialization;

Page 331: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 331

protected body Task_Initialization is

entry Wait when OK_To_Start is begin null; end;

procedure Start is begin OK_To_Start := True; end;

end Task_Initialization;

Page 332: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 332

task Philosopher_Task_Type (…); -- no Init entry req’d

Philosophers : Philosopher_Task_Array; …

task body Philosopher_Task_Type isbegin

Task_Initialization.Wait; -- wait until it’s OK -- to philosophize loop … end loop;

end Philosopher_Task_Type;

Page 333: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 333

Barrier

• Blocks a group of tasks until all have arrived.

Page 334: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 334

protected Task_Finalization is

entry Wait;

private

Release_All : Boolean := False;

end Task_Finalization;

Page 335: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 335

protected body Task_Finalization is

entry Wait when Wait'Count = Philosopher_Id'Last or Release_All is begin Release_All := True; end;

end Task_Finalization;

Page 336: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 336

task Philosopher_Task_Type (…); -- no Init entry req’d

Philosophers : Philosopher_Task_Array; …task body Philosopher_Task_Type isbegin

Task_Initialization.Wait; -- they aren’t necessarily -- born together... loop … end loop;

Task_Finalization.Wait; -- … but they do all -- die togetherend Philosopher_Task_Type;

Page 337: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 337

All-or-Nothing Resource Allocation

• Suppose one philosopher is eating, and the other three philosophers at the table have each picked up their left chopstick.

• The issue is that two philosophers could be eating. Now, one eats and three wait.

• Make a new rule: if both chopsticks are available, then pick them both up; otherwise, wait to eat.

Page 338: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 338

protected Chopsticks is

entry Pick_Up (Id : Philosopher_Id);

procedure Put_Down (Id : Philosopher_Id);

private

entry Waiting (Id : Philosopher_Id);

Available : Boolean_Array := (others => True);

Retrying : Boolean := False;

end Chopsticks;

Page 339: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 339

task body Philosopher_Task_Type is begin loop

Chopsticks.Pick_Up (Id);

delay Delay_Time; -- eat

Chopsticks.Put_Down (Id);

delay Delay_Time; -- think

end loop; end Philosopher_Task_Type;

Page 340: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 340

protected body Chopsticks is

entry Pick_Up (Id : Philosopher_Id) when not Retrying is

Next : constant Philosopher_Id := Id mod 5 + 1;

begin

if Available (Id) and Available (Next) then Available (Id) := False; Available (Next) := False; else requeue Waiting with abort; end if;

end Pick_Up; ...

Page 341: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 341

procedure Put_Down (Id : Philosopher_Id) is

Next : constant Philosopher_Id := Id mod 5 + 1; begin Available (Id) := True; Available (Next) := True;

Retrying := Waiting'Count > 0; end;

entry Waiting (Id : Philosopher_Id) when Retrying is begin if Waiting'Count = 0 then Retrying := False; end if;

requeue Pick_Up with abort; end Waiting;

end Chopsticks;

Page 342: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 342

ATC

• Abort just the processing being done by a task, without aborting the task.

• Done through a protected object. The task doesn’t have to interrupt its own processing to poll a Shutdown or Cancel entry.

• Need to carefully think about whether to requeue with abort, or to just requeue (without abort), if you’re using ATC.

Page 343: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 343

protected type Signal_Type is

entry Wait;

procedure Send;

private

Occurred : Boolean := False;

end Signal_Type;

-- This is a “permanent signal.”

Page 344: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 344

protected body Signal_Type is

entry Wait when Occurred is begin null; end;

procedure Send is begin Occurred := True; end;

end Signal_Type;

Page 345: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 345

Task_Initialization : Signal_Type; Task_Finalization : Signal_Type;

procedure Start (Update : in Update_Access) is begin Task_Initialization.Send; end;

procedure Stop is begin Task_Finalization.Send; end;

Page 346: Matthew Heaney1 Implementing Design Patterns in Ada95 Tips, Tricks,and Idioms by Matthew Heaney.

Matthew Heaney 346

task body Philosopher_Task_Type is begin

Task_Initialization.Wait;

select Task_Finalization.Wait; then abort

loop Chopsticks.Pick_Up (Id); delay Delay_Time; -- eat Chopsticks.Put_Down (Id); delay Delay_Time; -- think end loop;

end select;

end Philosopher_Task_Type;