Section 11.1 - Defining Generics

It's often useful to first create a more generic version of a subprogram or package and then use that generic version to create more specific subprograms or packages. Ada's capability to do this is called a generic, and it's the same thing as C++'s templates. Generics are also somewhat similar to C's "#define" preprocessor command, though Ada generics are type-checked and thus much safer.

It's probably easiest to understand this by example. First, let's write a procedure to swap two Integers:

  -- Here's the declaration (specification):
  procedure Swap(Left, Right : in out Integer);

  -- .. and here's the body:
  procedure Swap(Left, Right : in out Integer) is
    Temporary : Integer;
  begin
    Temporary := Left;
    Left := Right;
    Right := Temporary;
  end Swap;

Swap is a perfectly fine procedure, but it's too specialized. We can't use Swap to swap Floats, or Unbounded_Strings, or anything else. What we want is a more generic version of Swap, but one where we can substitute the type "Integer" with a more generic type. A generic version of Swap would look like this:

  -- Here's the declaration (specification):
  generic
    type Element_Type is private;
  procedure Generic_Swap(Left, Right : in out Element_Type);

  -- .. and here's the body:
  procedure Generic_Swap(Left, Right : in out Element_Type) is
    Temporary : Element_Type;
  begin
    Temporary := Left;
    Left := Right;
    Right := Temporary;
  end Generic_Swap;

In general, to create a generic version of a subprogram (or package), write the subprogram using a few generically-named types. Then precede the subprogram or package with the keyword ``generic'' and a list of the information you'd like to make generic. This list is called the generic formal parameters; this list is like the list of parameters in a procedure declaration. We'll explain more later what the phrase ``is private'' means.

To use a generic subprogram (or package), we have to create a real subprogram (or package) from the generic version. This process is called instantiating, and the result is called an instantiation or instance. These are big words for a simple concept. For example, here's how to create three Swap procedure instances from the generic one:

  procedure Swap is new Generic_Swap(Integer);
  procedure Swap is new Generic_Swap(Float);
  procedure Swap is new Generic_Swap(Unbounded_String);

Note that when you instantiate a generic, you ``pass'' types in the same way that you pass parameters to an ordinary procedure call.

From here on, you can call the Swap procedure that takes Integers, the Swap procedure that takes Floats, and the Swap procedure that takes Unbounded_Strings. Thus if A and B are both of type Integer, Swap(A,B) would swap their values. As for any other Ada subprogram, if different subprograms share the same name, Ada will determine at compile time which one to call based on the argument types.

Here's a simple test program for Generic_Swap:

with Generic_Swap;

procedure Tswap is
 procedure Swap is new Generic_Swap(Integer);
 A, B : Integer;
begin
 A := 5;
 B := 7;
 Swap(A, B);
 -- Now A=7 and B=5.
end Tswap;

For brevity I'm showing procedure Swap being instantiated in procedure Tswap, but in most real programs almost everything is enclosed in a package and the instantiations would be inside the package body.


There is no quiz question for this section.

You may go to the next section.


You may also:

PREVIOUS Go back to the previous section

OUTLINE  Go up to the outline of lesson 11

David A. Wheeler (dwheeler@dwheeler.com)

The master copy of this file is at "http://www.adahome.com/Tutorials/Lovelace/s11s1.htm".