Ada: Generics

Elucian MoiseElucian Moise
9 min read

A generic package in Ada is a template for a package that can be customized for specific types. It allows code reuse by parameterizing the package on one or more types.

Some key points about generic packages:

  1. They are declared using the generic keyword:
generic
   -- Formal parameters here
package Package_Name is
   -- Declarations here   
end Package_Name;
  1. The formal parameters specify the types that the generic package can work with. For example:
generic
   type Element_Type is private;
  1. The body of the generic package contains algorithms that work on the formal parameters. It is independent of any concrete type.

  2. The generic package is instantiated for specific types using:

package Integer_Package is new Package_Name (Element_Type => Integer);
package String_Package is new Package_Name (Element_Type => String);
  1. This instantiates two concrete packages - one working on Integers and one working on Strings.

  2. The initialization section allows initializing objects when a specific instantiation is made.

  3. Generic packages are useful for code reuse by parameterizing on types. They separate the algorithm from the specific types used.

In summary, a generic package is a template that:

  • Is parameterized by one or more types (formal parameters)

  • Contains declarations and bodies that are independent of any concrete type

  • Is instantiated for specific types to create concrete packages

  • Allows initialization when instantiated

  • Enables code reuse by working with any type that meets the formal requirements.


Instantiation

Ada generic packages allow defining parameterized packages that can be instantiated multiple times with different actual parameters. This allows code reuse and flexibility.

Declaring a generic package in Ada is done using the generic keyword before the package keyword:

generic
   type Item is private;
package Generic_Package is
   ... 
end Generic_Package;

Here we have declared a generic formal parameter Item of any private type. We can then define entities in the generic package that depend on this formal parameter.

We instantiate a generic package by providing actual parameters for the formal generic parameters:

package Integer_Package is new Generic_Package (Item => Integer);

package String_Package is new Generic_Package (Item => String);

Here we have instantiated the generic package Generic_Package twice:

  • Once with Item being an Integer type

  • Another time with Item being a String type.

This allows us to reuse the code defined in the generic package Generic_Package for different actual types.


Initialize

The procedure Initialize is a reserved procedure name in Ada generic packages. It has a special meaning - it is used to specify the initialization section of a generic package.

Using any other procedure name in a generic package will not have the same effect - it will just be a normal procedure.

So in summary:

  • procedure Initialize is a reserved procedure name in Ada generic packages

  • It signifies the initialization section that will be called automatically when the generic package is instantiated

  • Any other procedure name will not have the same effect - it will just be a normal procedure.

So Initialize is a reserved procedure name with special meaning, and it should only be used for defining the initialization section of a generic Ada package.

Hope this clarifies! Let me know if you have any other questions.

The initialization section in a generic Ada package allows you to initialize objects or perform some setup code when a generic package is instantiated.

It is defined using the keyword procedure Initialize inside the generic package, after the declaration section:

generic
   type Item is private;

package Generic_Package is

   -- Declaration section

   procedure Initialize;

end Generic_Package;

package body Generic_Package is

   procedure Initialize is
   begin
      -- Initialization code
      ...
   end Initialize;

end Generic_Package;

When you instantiate a generic package, the initialization section is called automatically:

package Integer_Package is new Generic_Package (Item => Integer);
-- Initializes automatically

package String_Package is new Generic_Package (Item => String);
-- Also initializes automatically

This allows you to:

  • Initialize objects that depend on the generic formal parameter Item

  • Register the package with other packages that require it

  • Perform any setup code that is needed when the generic package is instantiated with a specific type.

The initialization section is useful to have initialization logic in one place, instead of copying it for each generic package instantiation.


Using the type

A type can be used as a selector to select the appropriate instantiation of a generic package. This is done using the 'access discriminant' feature in Ada.

Here is how it works:

  1. The generic package has an access discriminant of the appropriate type:
generic
   type Element is private;
   with function "<" (Left, Right : Element) return Boolean;
package Sort is 
   type Selector is access Element;
   discriminant D : Selector;
end Sort;
  1. When the generic package is instantiated, the access discriminant is given a value:
package Integer_Sort is new Sort (
   Element => Integer, 
   "<" => "<" 
) with D => new Integer;

package String_Sort is new Sort (
   Element => String,
   "<" => "<"  
) with D => new String;
  1. The value of the access discriminant (D) selects which instantiation to use.

  2. It is used like this:

procedure Sort_Array is
   type Array is array (Integer range <>) of Selector'Component_Type;
   A : Array(1..10) := ...;  
begin
   if D'Identity = Integer_Sort.D then
      -- Use Integer sorting algorithm
   elsif D'Identity = String_Sort.D then
      -- Use String sorting algorithm    
   end if;
end Sort_Array;
  1. The 'Identity comparison of D selects the appropriate sorting algorithm based on the type - Integer or String.

So in summary, the access discriminant:

  • Acts as a selector for the appropriate generic instantiation

  • Its 'Identity is compared to select the right algorithm

  • It effectively selects based on the type used to instantiate the generic package.


Use-Cases

Here are some use cases for generic packages in Ada:

  1. Code reuse - Generic packages allow reusing the same code for different types. This reduces code duplication.

  2. Separation of algorithms from data types - The generic package defines the algorithm, while the actual type used is provided when instantiating the generic package. This separates the "what" from the "how".

  3. Flexibility - By parameterizing on types, generic packages can be instantiated for different types as needed. This provides flexibility.

  4. Type independence - The code in the generic package is independent of any specific type. It only depends on the generic formal parameters.

  5. Initialization - The initialization section allows performing any setup code when the generic package is instantiated with a specific type.

Some examples of generic packages:

  • Collections like stacks, queues, lists - They can be generic over the element type.

  • Sorting and searching algorithms - They can be generic over the element type being sorted/searched.

  • Mathematical functions - They can be generic over the numeric type (integer, float etc).

In summary, generic packages allow:

  • Reusing the same algorithm for different types

  • Separating the algorithm from the specific types used

  • Providing flexibility by parameterizing on types

  • Performing initialization when the generic is instantiated


Who can make Generics?

Generic packages in Ada must be implemented by the compiler vendor. They are not implemented by the programmer writing the generic package itself.

Here are the reasons why:

  1. Generic packages are templates - They are not concrete implementations. They only become implementations when instantiated for a specific type.

  2. The compiler needs to generate code for the specific instantiation based on the generic package and the types used.

  3. The compiler needs to handle the formal parameters, access discriminants and other generic features.

  4. The programmer writing the generic package only provides the template - the declarations and bodies that are independent of any specific type.

  5. It is the compiler's job to:

  • Parse and check the generic package for correctness

  • Generate code for each instantiation based on the types specified

  • Check that the types used meet the requirements of the formal parameters

  • Handle all references to the formal parameters and discriminants correctly.

So in short:

  • The programmer writes the generic package template, specifying formal parameters and bodies independent of any type.

  • The compiler vendor implements the mechanisms to:

  1. Parse and check the generic package

  2. Generate code for each instantiation based on the types specified

  3. Check the types meet the requirements

  4. Handle references to formal parameters and discriminants

So the compiler implements generic packages, allowing the programmer to write reusable generic templates that can work with any type.


Who uses Generics?

Generic packages in Ada are used by:

  1. Library developers:

They can write generic packages that implement common algorithms like sorting, searching, containers, etc. These can then work with any type that meets the requirements.

This allows library developers to write reusable code that can be used by application developers.

  1. Application developers:

They can instantiate the generic packages provided by library developers for the specific types used in their application. This simplifies their code and makes it reusable.

They can also write their own generic packages to implement functionality that is reusable within their application.

  1. Framework developers:

They can write generic packages that form the basis of their framework. Application developers can then instantiate these generic packages to use the framework for their specific types.

In summary, generic packages are used by:

  • Library developers to create reusable functionality for others

  • Application developers to reuse existing libraries and create internal reusable code

  • Framework developers to create generic frameworks that can be customized for specific needs.

The key benefits are:

  • Code reuse

  • Separating an algorithm from the types it works with

  • Increased flexibility by working with any type that meets the requirements

  • Simplified and reusable code.

So generic packages are useful for developers at all levels - from libraries to applications to frameworks - to create flexible and reusable functionality.


Standard Packages

There are several standard generic packages in the Ada language defined in the Ada Reference Manual. Some of the most useful ones are:

  1. Containers - These implement various container data structures like:
  • Generic_Array_Sort - Implements sorting of arrays

  • Hash_Tables

  • Ordered_Maps

  • Ordered_Sets

  • Unbounded_Stacks

  • Unbounded_Queues

  1. Strings - These implement string handling functionality:
  • Generic_Bounded_Length - For bounded length strings

  • Generic_Unbounded_Length - For unbounded length strings

  1. IO - For input/output:
  • Generic_Elementary_IO - For elementary input/output

  • Generic_Array_IO - For array input/output

  • Generic_Bounded_IO - For bounded input/output

  1. Exceptions - For handling exceptions:
  • Generic_Exceptions - Provides exception handling functionality
  1. Sequential_IO - For sequential file input/output:
  • Generic_Sequential_IO - Provides sequential file I/O
  1. Real_Time - For real-time functionality:
  • Generic_Real_Time - Provides real-time clocks, delays, etc.
  1. Numerics - For numeric computation:
  • Generic_Complex_Types

  • Generic_Real_Arrays

These are some of the most commonly used and important standard generic packages in Ada. They provide functionality for containers, strings, I/O, exceptions, real time and numerics.

The key benefit is that they are standardized, so any Ada compiler is required to implement them. This allows code reuse across compilers and platforms.


Disclaim: I have never implemented or instantiated a generic package. This content was created with Rix AI for me to understand the topic. Once I do finish this series I will try to use some generics.

0
Subscribe to my newsletter

Read articles from Elucian Moise directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Elucian Moise
Elucian Moise

Software engineer instructor, software developer and community leader. Computer enthusiast and experienced programmer. Born in Romania, living in US.