Ada Programming/Generics
Parametric polymorphism (generic units)
The idea of code reusability arises because of the necessity to construct programs on the basis of well established building blocks that can be combined to form an ampler and complex system. The reusability of code improves the productivity and the quality of software. One of the ways in which the Ada language supports this characteristic is by means of generic units. A generic unit is a unit that defines algorithms in terms of types and other operations that are not defined until the user instantiates them. Generic units can be subprograms and packages.
Note to C++ programmers: generic units are similar to C++ templates.
For example, to define a procedure for swaping variables of any (non-limited) type:
generic
type Element_T is private; -- Generic formal type parameter
procedure Swap (X, Y : in out Element_T);
procedure Swap (X, Y : in out Element_T) is
Temporal : constant Element_T := X;
begin
X := Y;
Y := Temporal;
end Swap;
The Swap subprogram is said to be generic. The subprogram specification is preceded by the generic formal part consisting of the reserved word generic followed by a list of generic formal parameters which may be empty. The entities declared as generic are not directly usable, it is necessary to instantiate them.
To be able to use Swap, it is necessary to create an instance for the wanted type. For example:
procedure Swap_Integers is new Swap (Integer);
Now the Swap_Integers procedure can be used for variables of type Integer.
The generic procedure can be instantiated for all the needed types. It can be instantiated with different names or, if the same identifier is used in the instantiation, each declaration overloads the procedure:
procedure Instance_Swap is new Swap (Float);
procedure Instance_Swap is new Swap (Day_T);
procedure Instance_Swap is new Swap (Element_T => Stack_T);
Similarly, generic packages can be used, for example, to implement a stack of any kind of elements:
generic
Max: Positive;
type Element_T is private;
package Generic_Stack is
procedure Push (E: Element_T);
function Pop return Element_T;
end Generic_Stack;
package body Generic_Stack is
Stack : array (1 .. Max) of Element_T;
Top : Integer range 0 .. Max;
-- ...
end Generic_Stack;
A stack of a given size and type could be defined in this way:
declare
package Float_100_Stack is new Generic_Stack (100, Float);
use Float_100_Stack;
begin
Push (45.8);
-- ...
end;
Formal parameters are of mode in by default and can be in out, but never out. In case it is in, the parameter will behave as a constant whose value is provided by the corresponding actual parameter. Of course, an in generic parameter cannot be of a limited type, because the copy is not allowed and the formal parameter takes its value by means of copying. In case the generic parameter is of mode in out, it behaves as a variable that renames the corresponding actual parameter; in this case, the actual parameter must be a variable and its determination is made in the moment of the unit instantiation.
In addition to generic type and object parameters, formal subprograms or package parameters can be used, for example:
generic
type Element_T is private;
with function "*" (X, Y: Element_T) return Element_T;
function Square (X : Element_T) return Element_T;
function Square (X: Element_T) return Element_T is
begin
return X * X; -- The formal operator "*".
end Square;
This generic function could be used, for example, with matrices, having defined the matrix product.
with Square;
with Matrices;
procedure Matrix_Example is
function Square_Matrix is new Square
(Element_T => Matrices.Matrix_T, "*" => Matrices.Product);
A: Matrix_T := Matrix_T.Identity;
begin
A := Square_Matrix (A);
end Matrix_Example;
See also
Wikibook
Wikipedia
Ada Reference Manual
|