Generic Packages & Formal Objects

Write generic packages that implement reusable abstract data types, use formal objects (values such as Capacity), formal subprograms, private types inside generics, per-instance state and exceptions, and understand how instantiation creates independent packages.

A generic package is a template for a whole module: a bounded stack of any element type and capacity, a queue, a matrix package for any numeric type. Each instantiation creates a new, independent package with its own types, state and exceptions. Ada's standard containers (Ada.Containers.Vectors, Hashed_Maps, ...) are generic packages of exactly this kind.

Formal Objects

Besides types and subprograms, generics accept values: 'Capacity : Positive;' is a formal object of mode in (a constant inside the instance). 'Log : in out Log_Type;' is a formal in out object that the instance can modify (a variable of the instantiating unit). Formal objects size arrays, set limits and configure behavior at instantiation time; they may have defaults ('Capacity : Positive := 100;').

• Name : T; — formal in object (a constant in the instance). • Name : in out T; — refers to a variable of the instantiator. • Defaults allowed: Capacity : Positive := 100;

Generic Packages as ADTs

The spec of Bounded_Stacks declares a private Stack type, operations and exceptions; the private part uses the formals (array (1 .. Capacity) of Element). Instantiate: 'package Int_Stacks is new Bounded_Stacks (Element => Integer, Capacity => 3, Image => Integer'Image);'. Int_Stacks.Stack and Char_Stacks.Stack are different types, and Int_Stacks.Stack_Full is different from Char_Stacks.Stack_Full. Attributes such as Integer'Image can be passed as actual subprograms. Generic packages may also be library units in their own files (bounded_stacks.ads/.adb), and generic children (generic package Parent.Child) extend generic parents.

• Each instance = separate package with its own types, state, exceptions. • Attributes (Integer'Image) can be actual subprograms. • Standard containers are generic packages.

Code Examples

Two instances of the same generic: an integer stack of capacity 3 and a character stack of capacity 5 (popping "Ada" reverses it). Integer'Image's leading space plus the separator space produces two spaces before each number. The fourth push overflows the integer stack. Output: Int stack: [ 100 200 300 ] size 3 Popped 300 -> [ 100 200 ] Char stack popped: adA Int_Stacks.Stack_Full raised (each instance has its own exceptions)

S: Bounded_Stacks.Stack; -- using the generic directly package Int_Stacks is new Bounded_Stacks (Integer, 3, Integer'Image); S: Int_Stacks.Stack;

A generic is a template; you must instantiate it before use.

when Stack_Full => -- which instance?

when Int_Stacks.Stack_Full => Each instance declares its own exceptions.

-- instantiating a generic package inside a frequently called subprogram -- instantiate once at library or package level

Instances inside subprograms are re-elaborated at each call and can't be shared.

What is 'Capacity : Positive;' in a generic formal part? A. A variable B. A formal object (value) C. A type D. A subprogram

Are Int_Stacks.Stack and Char_Stacks.Stack the same type? A. Yes B. No, each instance declares its own types C. Only if Capacity matches D. Only for private types

Can an attribute like Integer'Image be an actual for a formal function? A. Yes B. No C. Only in Ada 83 D. Only for procedures A. (verified in the example)

Which standard units are generic packages? A. Ada.Text_IO only B. Ada.Containers.Vectors, Hashed_Maps, etc. C. Ada.Calendar D. None

B. (Ada.Text_IO also contains generic children like Integer_IO) Trace the generic stack example.

Real output: Int stack: [ 100 200 300 ] size 3 Popped 300 -> [ 100 200 ] Char stack popped: adA Int_Stacks.Stack_Full raised (each instance has its own exceptions)

-- file: bounded_stacks.ads
generic
   type Element is private;             --  formal type
   Capacity : Positive;                 --  formal object (a value)
   with function Image (E : Element) return String;   --  formal subprogram
package Bounded_Stacks is
   type Stack is private;

   Stack_Full, Stack_Empty : exception;

   procedure Push (S : in out Stack; E : Element);
   procedure Pop (S : in out Stack; E : out Element);
   function Size (S : Stack) return Natural;
   function Image (S : Stack) return String;

private
   type Element_Array is array (1 .. Capacity) of Element;
   type Stack is record
      Data : Element_Array;
      Top  : Natural := 0;
   end record;
end Bounded_Stacks;

-- file: bounded_stacks.adb
package body Bounded_Stacks is

   procedure Push (S : in out Stack; E : Element) is
   begin
      if S.Top = Capacity then
         raise Stack_Full;
      end if;
      S.Top := S.Top + 1;
      S.Data (S.Top) := E;
   end Push;

   procedure Pop (S : in out Stack; E : out Element) is
   begin
      if S.Top = 0 then
         raise Stack_Empty;
      end if;
      E := S.Data (S.Top);
      S.Top := S.Top - 1;
   end Pop;

   function Size (S : Stack) retur
…