PACKAGES


1. OVERVIEW

When a largish program is to be written it is often advantageous to divide the program up into chuncks (modules) and compile the parts seperatly. This option is particularly desirable when several programmers are jointly developping a large program.


2. LIBRARY UNITS

One method whereby different parts of an Ada program can be compiled seperatly is to make use of library units. Here we first create a desired library unit, for example:

------------------------------------------------------------
  -- TOTAL PROCEDURE
      procedure TOTAL(N1, N2 : INTEGER) is sperate
      begin
          PUT(N1+N2);
      end TOTAL;
  ----------------------------------------------------------

and compile it. We now write a program that will use this code as follows:

-- SUBUNIT EXAMPLE
-- 19 December 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO, TOTAL;
use TEXT_IO;

procedure SUBUNIT_EXAMPLE is
    package INTEGER_INOUT is new INTEGER_IO(INTEGER);
    use INTEGER_INOUT;
    NUM_1, NUM_2 : INTEGER ;

-- TOP LEVEL
begin
    PUT_LINE("Input two integers");
    GET(NUM_1);
    GET(NUM_2);
    TOTAL(NUM_1,NUM_2);
end SUBUNIT_EXAMPLE;

Note the use of the with clause to include the library unit. A use caluse is not included as this is intended for use only with predefine packages. The above program can now be run in the normal manner. Generally speaking library units are collected together into packages and not alone as in the above example.


3. PACKAGES

A package (also known as a module or task) is a collection of a number of related declarations of types, variables and routines. Ada packages comprise two parts:

  1. A specification part which informs the programs that want to use it what resources are to be found in the package - for example, procedures - and how they are used.
  2. A package body that details the resources

Note that the body is not visible to the program (at least conceptually). The template for writing an Ada package is as follows:

-- Specification
package PACKAGE_NAME is
        <DECLARATIONS>
end PACKAGE_NAME;

-- Body

-- With and use clauses

package body PACKAGE_NAME is
        <PROGRAM_DETAILS>
end PACKAGE_NAME;

If we consider the random number generator example presented earlier we can wrap the generator part of this program up into a package as follows:

-- RANDOM NUMBER GENERATOR PACKAGE
-- 9 October 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

-- Specification
package RANDOM is
        -- Set seed
        procedure START_VALUE;
        -- Generate number
        function RANDOM_NUMBER return NATURAL;
end RANDOM;

-- Body

with TEXT_IO;
use TEXT_IO;

package body PACKAGE_NAME is
        package INTEGER_INOUT is new INTEGER_IO(INTEGER);
        use INTEGER_INOUT;
        package FLOAT_INOUT is new FLOAT_IO(FLOAT);
        use FLOAT_INOUT;
        M_CONSTANT  : constant := 8192;         -- 2**13
        K_CONSTANT  : constant := 3125;         -- 5**5
        TERM        : NATURAL;

------------------------------------------------------------
-- START VALUE PROCEDURE
        procedure START_VALUE is
                SEED: POSITIVE range 1..M_CONSTANT-1;
        begin
                loop
        -- Output request to user and get SEED
                        PUT("Enter seed (odd number within the range 1 to ");
                        PUT(M_CONSTANT-1, WIDTH=>5);
                        PUT_LINE("): ");
                        GET(SEED);
        -- Check seed (deed must be an odd number
                        if SEED rem 2 = 1 then
                                TERM := SEED;
                                exit;
                        else
                                PUT("Invalid seed: ");
                                PUT(SEED);
                                PUT_LINE(", try again");
                                NEW_LINE;
                        end if;
                end loop;
                NEW_LINE;
        end START_VALUE;
----------------------------------------------------------
----------------------------------------------------------
-- RANDOM NUMBER
        function RANDOM_NUMBER return NATURAL is
        begin
                TERM := TERM * K_CONSTANT mod M_CONSTANT;
                RETURN(TERM);
        end RANDOM_NUMBER;
----------------------------------------------------------

end RANDOM;

Having created the package (and compiled it) we can now use it. The most common way of doing this is as follows:

-- RANDOM NUMBER GENERATOR
-- 16 September 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO, RANDOM;
use TEXT_IO;

procedure RANDOM_GENERATOR is
        package INTEGER_INOUT is new INTEGER_IO(INTEGER);
        use INTEGER_INOUT;
        RAND_NUM  : NATURAL := 0;
begin
        RANDOM.START_VALUE;
        while RAND_NUM /= 50 loop
                RAND_NUM := RANDOM.RANDOM_NUMBER*100/8191;
                PUT(RAND_NUM, WIDTH=>4);
        end loop;
        NEW_LINE;
end RANDOM_GENERATOR;

Here we have used a dot operator to specifically link a procedure/function to a package (e.g. RANDOM.START_VALUE and RANDOM.RANDOM_NUMBER). We could have included a use instead, however this is normally re served for Ada's standard packages (TEXT_IO etc.)


4. EXAMPLE PROBLEM SPOT THE BALL


4.1. Requirements

Design and implement (in Ada) a "spot the ball" computer game. The rules for the game are as follows.

  1. The "ball" is placed randomly within a 256x256 grid and its location is defined by a X and Y coordinate pair (i.e. two integers ranging from 1 to 256 inclusive).
  2. The user may then enter a guess in terms of an X and Y coordinate.
  3. The program should then respond with "bull's-eye" or a phrase indicating whether the ball is to the:
    • North,
    • North-east,
    • East,
    • South-east,
    • South,
    • South-west,
    • West or
    • North_west
    of the guess.
  4. The user should only be allowed a maximum of 8 guesses. If the user fails to find the location of the ball within this number of guesses the program "wins"

Note: make use of the package RANDOM defined above.

4.2. Design

A top-down analysis of the proposed problem is given below.

TOP DOWN ANALYSIS

Where required we will define coordinates as a subtype (called COORDINATE) of the pre-defined subtype POSITIVE with a range of 1 to 256 inclusive. We will address the identified operations using the following procedures/functions:

  1. SPOT_THE_BALL (Top level procedure): Maintain "guess" loop. Calls to LOCATE_BALL and CHECK_GUESS. Output result. Include definition for subtype coordinate
    NAMEUSAGETYPERANGE
    X_COORDGlobal input variableCOORDINATE1..256
    Y_COORDGlobal input variableCOORDINATE1..256
    X_POSNGlobal variableCOORDINATE1..256
    Y_POSNGlobal variableCOORDINATE1..256
    MAX_NUM_GUESSESGlobal constantINTEGER8
    NUM_GUESSESGlobal variableINTEGERDefault
  2. LOCATE_BALL (level 2 function): Generate and return random location for ball using RANDOM package.
    NAMEUSAGETYPERANGE
    X_POSFormal (out) parameterCOORDINATE1..256
    Y_POSFormal (out) parameterCOORDINATE1..256
  3. CHECK_GUESS (Level 2 function) : Check guess against position of ball. Return TRUE if OK, FALSE otherwise. Calls to CORRECT_X_COORD, BALL_TO_WEST and BALL_TO_EAST.
    NAMEUSAGETYPERANGE
    X_CRDFormal parameterCOORDINATE1..256
    X_POSFormal parameterCOORDINATE1..256
    Y_CRDFormal parameterCOORDINATE1..256
    Y_POSFormal parameterCOORDINATE1..256
  4. CORRECT_X_COORD (level 3 function): Check whether Y guess is les than, equal to or greater than Y coordinate of ball where the X coordinate of the ball has been correctly guessed. Return TRUE if equal, and FALSE otherwise.
    NAMEUSAGETYPERANGE
    Y_CRDFormal parameterCOORDINATE1..256
    Y_POSFormal parameterCOORDINATE1..256
  5. BALL_TO_WEST (level 3 procedure): Check whether Y guess is les than, equal to or greater than Y coordinate of ball where it is known that the ball is to the west of the current guess.
    NAMEUSAGETYPERANGE
    Y_CRDFormal parameterCOORDINATE1..256
    Y_POSFormal parameterCOORDINATE1..256
  6. BALL_TO_EAST (level 3 procedure): Check whether Y guess is les than, equal to or greater than Y coordinate of ball where it is known that the ball is to the east of the current guess.
    NAMEUSAGETYPERANGE
    Y_CRDFormal parameterCOORDINATE1..256
    Y_POSFormal parameterCOORDINATE1..256

Complete Nassi-Shneiderman charts for the above are given below:

NASSI_SHNEIDERMAN CHART

NASSI_SHNEIDERMAN CHART

A Control Flow Chart/Diagram indicating the broad flow of control through the above is given below.

DFD

DFD

4.3. Implementation

-- SPOT THE BALL
-- 12 October 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO, RANDOM;
use TEXT_IO;

procedure SPOT_THE_BALL is
    package INTEGER_INOUT is new INTEGER_IO(INTEGER);
    use INTEGER_INOUT;
    subtype COORDINATE is POSITIVE range 1..256;
    X_COORD, Y_COORD : COORDINATE;
    X_POSN, Y_POSN   : COORDINATE;
    MAX_NUM_GUESSES  : constant := 8 ;
    NUM_GUESSES      : INTEGER  := 1;

    ----------------------------------------------------------------
    -- Random location of ball within space
    procedure LOCATE_BALL(X_POS, Y_POS : out COORDINATE) is
    begin
        RANDOM.START_VALUE;
        X_POS := RANDOM.RANDOM_NUMBER*256/8191;
        Y_POS := RANDOM.RANDOM_NUMBER*256/8191;
    end LOCATE_BALL;
    ----------------------------------------------------------------

    ----------------------------------------------------------------
    -- Check guess
    function CHECK_GUESS(X_CRD, Y_CRD, X_POS, Y_POS : COORDINATE) return
            BOOLEAN is

        --------------------------------------------------------
        -- Correct X
        function CORRECT_X_COORD(Y_CRD, Y_POS : COORDINATE) return BOOLEAN is
        begin
            if Y_POS = Y_CRD then
                PUT_LINE("bull's-eye!");
                RETURN(TRUE);
            else
                if Y_POS > Y_CRD then
                    PUT_LINE("Ball is to the North");
                else
                    PUT_LINE("Ball is to the South");
                end if;
                RETURN(FALSE);
            end if;
        end CORRECT_X_COORD;
        --------------------------------------------------------
        -- Ball to west
        procedure BALL_TO_WEST(Y_CRD, Y_POS : COORDINATE) is
        begin
            if Y_POS = Y_CRD then
                PUT_LINE("Ball is to the West");
            else
                if Y_POS > Y_CRD then
                    PUT_LINE("Ball is to the North-west");
                else
                    PUT_LINE("Ball is to the South-west");
                end if;
            end if;
        end BALL_TO_WEST;
        --------------------------------------------------------
        -- Ball to east
        procedure BALL_TO_EAST(Y_CRD, Y_POS : COORDINATE) is
        begin
            if Y_POS = Y_CRD then
                PUT_LINE("Ball is to the East");
            else
                if Y_POS > Y_CRD then
                    PUT_LINE("Ball is to the North-east");
                else
                    PUT_LINE("Ball is to the South-east");
                end if;
            end if;
        end BALL_TO_EAST;

        --------------------------------------------------------

    begin
        if X_POS = X_CRD then
            RETURN(CORRECT_X_COORD(Y_CRD,Y_POS));
        else
            if X_POS < X_CRD then
                BALL_TO_WEST(Y_CRD,Y_POS);
            else
                BALL_TO_EAST(Y_CRD,Y_POS);
            end if;
            RETURN(FALSE);
        end if;
    end CHECK_GUESS;
    ----------------------------------------------------------------

begin
-- Locater ball
    LOCATE_BALL(X_POSN,Y_POSN);
-- Loop through maximum of 8 guesses
    while NUM_GUESSES <= MAX_NUM_GUESSES loop
        PUT_LINE("Input your guess (X and Y coordinates between 1 and 256): ");
        GET(X_COORD);
        GET(Y_COORD);
        exit when (CHECK_GUESS(X_COORD,Y_COORD,X_POSN,Y_POSN));
        NUM_GUESSES := NUM_GUESSES+1;
    end loop;
-- Final output
    if NUM_GUESSES = 9 then
        PUT_LINE("Program has won");
    else
        PUT_LINE("You have won");
    end if;
end SPOT_THE_BALL;

4.4. Testing

TEST CASERESULT
X_COORDY_COORD
0*CONSTRAINT_ERROR
10CONSTRAINT_ERROR
21OK
22OK
255255OK
255256OK
256257CONSTRAINT_ERROR
257*CONSTRAINT_ERROR

First note that we do not need to test the package - it is assumed that this has been designed, implemented and tested separately by the developer and that consequently it is fully operational.

4.4.1. Black Box Testing

BVA and limit Testing: The input coordinates should be tested as shown in the table to the right using BVA and limit testing techniques.

4.4.2. White Box Testing

Path Testing: The DFD given above identifies the paths through the system. Test cases should be generated to ensure that every path is exercised. A seed of 123 wile produce X_POS = 235 and Y_POS = 109. Using this seed the following sequence of "guesses" will test all the paths in code which may influence the "advice". Note that after 8 iterations the loop is complete (program has won). The final path, where a correct answer is guessed is exercised bo the loop test cases considered next.

"GUESSES"RESULT
X_COORDY_COORD
250250Ball is to the South-west
250109Ball is to the West
25010Ball is to the North-west
235250Ball is to the South
23510Ball is to the North
10250Ball is to the South-east
10109Ball is to the East
1010Ball is to the North-east Program has won

Loop Testing: we should also test that the loop is working appropriately. In this case we should test the loop at 1, 2 4 7 and 8 iterations (the last has already been taken into account as part of the proposed path test cases). Using the same seed and the inputs given above, and by imposing a correct guess where appropriate, we cause the loop to repeat 1, 2, 4 and 7. Thus:

"GUESSES"RESULT
X_COORDY_COORD
235109bull's-eye! You have won
"GUESSES"RESULT
X_COORDY_COORD
250250Ball is to the South-west
235109bull's-eye! You have won
"GUESSES"RESULT
X_COORDY_COORD
250250Ball is to the South-west
250109Ball is to the West
25010Ball is to the North-west
235109bull's-eye! You have won
"GUESSES"RESULT
X_COORDY_COORD
250250Ball is to the South-west
250109Ball is to the West
25010Ball is to the North-west
235250Ball is to the South
23510Ball is to the North
10250Ball is to the South-east
10109Ball is to the East
235109bull's-eye! You have won

Example Problem Spot The Ball.


5. SUBUNITS

Use of packages is one way in which different parts of an Ada program can be compiled seperatly. An alternative is to use subunits. Here the programmer must "tell" the Ada compiler that the body of some procedure/function will be written and compiled separately by including the keywod separate after the subprogram specification:

procedure <NAME(PARAMETERS)> is separate;

or:

function  <NAME (PARAMETERS)> return <RETURN TYPE> is
separate;

When the body of the subunit is later written we must indicate the program in which it is to be used. For example if the subunit is to be used in a program A, then the following must be included in the subunit code:

seperaste (A)
        <SUBPROGRAM BODY>

For example:

-- SUBUNIT EXAMPLE
-- 19 December 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO;
use TEXT_IO;

procedure SUBUNIT_EXAMPLE is
    package INTEGER_INOUT is new INTEGER_IO(INTEGER);
    use INTEGER_INOUT;
    NUM_1, NUM_2 : INTEGER ;
    ------------------------------------------------------------
    -- TOTAL PROCEDURE
    procedure TOTAL(N1, N2 : INTEGER) is separate;
    ----------------------------------------------------------

-- TOP LEVEL
begin
    PUT_LINE("Input two integers");
    GET(NUM_1);
    GET(NUM_2);
    TOTAL(NUM_1,NUM_2);
end SUBUNIT_EXAMPLE;

Note the use of the key word separate. The declaration of TOTAL, in the above example, is known as a stub. The program can now be compiled. However, before it can be run we must write and compile the subunit TOTAL:

-- TOTAL
-- 19 December 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO;
use TEXT_IO;

separate (SUBUNIT_EXAMPLE)

------------------------------------------------------------
-- TOTAL PROCEDURE
    procedure TOTAL(N1, N2 : INTEGER) is saparate;
    begin
        PUT(N1+N2);
    end TOTAL;
----------------------------------------------------------

Since TOTAL is to be inserted into the procedure SUBUNIT_EXAMPLE, this name is written in brackets after the word separate. The initial program can now be executed.

From the above it can be seen that the technique of working with subunits can be used naturally in connection with top down design. The disadvantages of subunits are that they tend to be very specialised and are only applicable to a specific program. Generally speeking this author would recomend the use of user defined packages whereever separate compilation of parts of a programme would seem to be either desirable or appropriate.




Created and maintained by Frans Coenen. Last updated 11 October 1999