10 Generics


Overview

Code reuse has been one of the great programming hopes for many years. Although suitable in the area of mathematical routines (where the functions are well defined and stable) trying to develop libraries in other areas has met with very limited success, due to the inevitable intertwining of process and data types in procedures. Ada has attempted to free us from this problem by producing code that does not rely as much on the specific data types used, but on their more general algorithmic properties.

As described by Naiditch, generics are like form letters; mostly they are complete letters with a few blanks requiring substitution (eg name and address information). Form letters aren't sent out until this information is filled in. Similarly generics cannot be used directly, we create a new subprogram or a package by 'instantiating' a generic and then using the instantiated compilation unit. When a generic is instantiated we have to supply information to fill in the blanks, such as type information, values or even subprograms.


Generics

In Ada a program unit (either a subprogram or a package) can be a generic unit.

This generic unit is used to create instances of the code that work with actual data types. The data type required is passed in as a parameter when the generic unit is instantiated. Generics are usually presented in two parts, the generic specification and then the generic package.

Once they are compiled the generics are stored in the Ada library and can be with'ed (but never use'd) by other compilation units. These other units, whether they be subprograms, packages or generics, can be with'ed and instantiated (the process of creating a usable subprogram or package by supplying generic parameters). The instatiated subprogram or package can be stored in the Ada library for later use.

The following is the instantiation of the generic package integer_io. Int_io is now available to be with'ed (and use'd) by any program.

The package can be instantiated with other types as well.


Generic subprograms

The following is a compilation unit. Once it is compiled it is available to be 'with'ed (but not 'use'd) from the Ada library. It includes the keyword generic, a list of generic parameters and the procedure specification.

The body of the generic procedure is presented as a seperate compilation unit. Note that it is identical to a non-generic version.

The code above is simply a template for an actual procedure that can be created. It can't be called. It is equivalent to a type statement - it doesn't allocate any space, it just defines a template for the shape of things to come. To actually create a procedure:

We now have a procedure swap that swaps integers. Here "integer" is called a generic actual parameter. "Element" is called a generic formal parameter.

You can create as many of these as you like. In this case the procedure name is overloaded and as normal the compiler can tell which one you call by the parameter type.

It can be called (and behaves) just as though it had been defined as


Generic packages

Packages can be generics as well.

The following generic package specification is fairly standard:

The accompanying package body would be

Quite simply you replace any instance of the data type to be manipulated with that of the generic type name.

How would you create a generic unit and test it?

- Create it using a specific type and translate it to use generic parameters.


Generic parameters

There are three types of parameters to generics

Type parameters

Value and object parameters

Subprogram parameters

So far we have only seen type parameters.


Type parameters

Despite the alluring appeal of generics we are still restricted by the very nature of the tasks we are attempting to accomplish. In some generics we may wish to provide a facility to sum an array of numbers. Quite clearly this is only appropriate for numbers, we can't add records together. To provide protection from someone instantitating a generic with an inappropriate type, we can specify the category of types that can be used when instantiating.

The compiler can also check that we don't perform any inappropriate actions on a variable inside the code e.g. it wont allow us to find the 'pred of a record.

A list of the various type restrictions is given below.

To understand the rule governing instantiation of array type parameters consider the following generic package

and the types:

then Mix can match vector and Option can match table.


Value parameters

Value parameters allow you to specify a value for a variable inside the generic:

You would instantiate the package thus:

package fred is new stack(element => integer, size => 50);

or

or

Note that if the value parameter does not have a default value then one has to be provided when the generic is instantiated.

Value parameters such as string can also be included.

Note that the file_name parameter type (string) is not constrained. This is identical to string parameters to subprograms.


Subprogram parameters

We can pass a subprogram as a parameter to a generic.

Why? If you have a limited private type you can pass tests for equality and assignment in as subprogram parameters.

E.g.

To instantiate the generic

Other forms allow for a default subprogram if none is given.

Or you can specify the computer makes a default selection of procedure based on the normal subprogram selection rules:

If no function is supplied for the "=" function then the default equal function will be used according to the type of element ( i.e. if element is integer the normal integer "=" will be used).


to the index...