ColdFrame: Use of Ada Containers

Introduction

The current software architecture of ColdFrame uses Ada 2005 Containers, which provide various ways of handling groups of things in containers.

In ColdFrame, for each non-«singleton» Class, there's a private means of holding all the current Instances of the class, together with public interfaces allowing the creation, finding, accessing and eventual deletion of instances.

As well as this, there is public support for obtaining and handling vectors (collections) of instances.

The Containers

The container types used by generated code are

Hashed_Maps
for the internal storage of all the instances of a class when it's not possible to use an array. The Key is the class's Identifier record.
Vectors
for normal externally-visible aggregate manipulation.

In all cases the actual things contained (the "Element_Type"s) are Handles, ie pointers to instances of the class concerned. Allocation of instances is handled within the class's Create function, and deallocation in Delete.

Stubs use Maps, Sets, and Vectors.

Standard Container instantiations

Given the domain Domain and the class Class, the standard public Component instantiation (in the package Domain.Class) is the package Domain.Class.Vectors, an instantiation of Ada.Containers.Vectors (or Bounded_Vectors) using Domain.Class.Handle.

Vectors

ColdFrame provides three utility functions for dealing with variables of type Domain.Class.Vectors.Vector, as child subprograms of Domain.Class (note, this means they have to be withed individually):

All_Instances
returns all the current instances of Domain.Class:
function Domain.Class.All_Instances
   return Domain.Class.Vectors.Vector;
Filter_Function
given a Vector, returns a new Vector containing just those instances for which Pass returns True:
generic
   with function Pass (This : Handle) return Boolean is <>;
function Domain.Class.Filter_Function
   (The_Vector : Domain.Class.Vectors.Vector)
   return Domain.Class.Vectors.Vector;
Selection_Function
returns all the current instances of Domain.Class for which Pass returns True:
generic
   with function Pass (This : Handle) return Boolean is <>;
function Domain.Class.Selection_Function
   return Domain.Class.Vectors.Vector;

Operations

Iteration

For each Class, ColdFrame generates a generic procedure Domain.Class.Iterate; it can be easier to use this than to use the Containers' iteration facilities directly. Also, direct use means that your code is more closely tied than it need be to the current ColdFrame implementation strategy.

If you do need to use Container iteration, there are two forms: open and closed (Ada 2012 adds a third, much simpler, form).

With open iteration, you create an iterator or Cursor designating the first element of the container; while the cursor designates a valid element you process the element, then advance the cursor to the next element in the container it references. With closed iteration, you supply a procedure which gets called for each element in the container.

The operations on Cursors are all defined in Domain.Class.Vectors.

The examples below are in terms of a pre-existing container The_Vector. You may take exception to the overloading of the name Process!

Open iteration

declare
   It : Class.Vectors.Cursor := The_Vector.First;
   use type Class.Vectors.Cursor;
begin
   while It /= Class.Vectors.No_Element loop
      declare
         H : Class.Handle := Class.Vectors.Element (It);
      begin
         -- do something with H
      end;
      Class.Vectors.Next (It);
   end loop;
end;

Open iteration (Ada 2012)

for H of The_Vector loop
   -- do something with H
end loop;

Closed iteration

declare
   procedure Process (C : Class.Vectors.Cursor) is
   begin
      -- do something with Class.Vectors.Element (C)
   end Process;
begin
    The_Vector.Iterate (Process'Access);
end;

Iteration using ColdFrame's Domain.Class.Iterate

declare
   procedure Process (H : Class.Handle);
   pragma Inline_Always (Process);

   procedure Process
     is new Class.Iterate (Process);

   procedure Process (H : Class.Handle) is
   begin
      -- do something with H
   end Process;
begin
   Process (The_Vector);
end;

Filtering

Most filtering requirements should be met by the generated Filter_Function and Selection_Function generics.

Sorting

The Ada Container Vectors provide a generic sorting facility in Domain.Class.Vectors.Generic_Sort.

The resulting ordering is in terms of the function that instantiates the parameter "<"; the Element that is "less than" all the other Elements in the Vector will end up first. The sort should not be expected to be stable; that is, don't expect Elements which compare "=" to remain in the same order.

An example of sorting by threateningness, assuming the existence of Candidate_Tracks of type Track.Vectors.Vector, is

declare
   function Is_More_Threatening (L, R : Track.Handle) return Boolean;

   package Sorting is new Track.Vectors.Generic_Sort
     ("<" => Is_More_Threatening);

   function Is_More_Threatening (L, R : Track.Handle) return Boolean is
   begin
      return L.Time_Of_CPA < R.Time_Of_CPA;
   end Is_More_Threatening;
begin
   Sorting.Sort (Candidate_Tracks);
end;
after which Candidate_Tracks.First_Element will return the track with the earliest Time_Of_CPA (CPA means Closest Point of Approach).