Packages
A package lets us group related declarations, procedures, and functions. A
program can with the package and gain access to all of these. However, as
we'll see, packages have many other advantages. Usually library units are
packages rather than individual procedures and functions.
By way of example, consider a very simple procedure and a very simple function:
procedure Double(Number : in Integer; Answer : out Integer);
function Twice(Number : in Integer) return Integer;
We could compile these individually, or we could put them in a package and
compile that instead. Here's the package specification:
package Simple_Math is
procedure Double(Number : in Integer;
Answer : out Integer);
function Twice(Number : in Integer) return Integer;
end Simple_Math;
The package body must contain the bodies of all the procedures and functions
declared in the package specification:
package body Simple_Math is
procedure Double(Number : in Integer;
Answer : out Integer) is
begin
Answer := Number * 2;
end Double;
function Twice(Number : in Integer) return Integer is
begin
return Number * 2;
end Twice;
end Simple_Math;
The package body could optionally declare either or both subprograms to be
separate:
package body Simple_Math is
procedure Double(Number : in Integer;
Answer : out Integer) is separate;
function Twice(Number : in Integer) return Integer
is separate;
end Simple_Math;
Here's an example of a calling program that withs the package and makes use of
the two subprograms declared in the package specification:
with Simple_Math; use Simple_Math;
procedure Package_Demo is
I, J : Integer := 10;
begin
Double(Number => I, Answer => J); -- This line sets J to 20.
J := Twice(I); -- This line also sets J to 20.
end Package_Demo;
The package specification must be compiled first, but either the calling
program or the package body may be compiled second. The calling program
depends only on the package specification, not the package body. Similarly,
the package body depends on the package specification. If the package body
declares any subprograms to be separate, these must be compiled after the
package body, because any separate subprogram depends on its "parent."
A package specification that declares no subprograms (only objects, types,
etc.) doesn't need a package body. In Ada 95, a package specification that
doesn't need a body may not have one, unless we include pragma Elaborate_Body;
in the package specification. We'll learn more about pragmas later.
The main program may never be inside a package. It must be a procedure or
function compiled directly into the library, such as Hello or Add. In most
implementations of Ada, the main program can't have any parameters, and must be
a procedure rather than a function.
A package body may optionally have initialization code, introduced by the word
begin. This code is executed only once, the first time another program
elaborates the package by withing it. For example, this package uses
initialization code to initialize the array B:
package P is
function F return Float;
end P;
package body P is
A : array(1 .. 10) of Float := (others => 0.0);
B : array(1 .. 10, 1 .. 10) of Integer;
function F return Float is
...
end F;
begin
for I in 1 .. 10 loop
for J in 1 .. 10 loop
B(I, J) := I*J*J;
end loop;
end loop;
end P;
Declarations made inside a package specification are said to be exported and
are available to programs outside the package that with the package. However,
declarations made inside the package body are available only inside the
package. The package body doesn't even have to be written before outside
programs that with the package. In this example, programs outside and inside
the package may use type Answer, array A, and procedure R, but only procedures
P, Q and R may use type Question and array B. Procedure P may be called only
by Q and R (and itself), and procedure Q is available only to R (and itself).
package X is
type Answer is (Yes, No, Maybe);
A : array(1 .. 10) of Float;
procedure R;
end X;
package body X is
type Question is (Why, Who, How);
B : array(1 .. 10) of Integer;
procedure P is separate;
procedure Q is separate;
procedure R is separate;
end X;
Question
True or
False? In the above example,
porcedure P may call R.
You're right! R is declared in the package specification, so any program
inside X (and any program outside X that withs the package) may call R.
True. R is declared in the package specification, so any program
inside X (and any program outside X that withs the package) may call R.
Functions with Infix Notation
+ - * / ** & = < > <= >=
and or xor abs not mod rem
Ada allows us to overload any of the above operators by enclosing the operator
in quotes following the word function. For example, having defined type Date,
we may want to define what it means for one date to be "less than" another. We
could write function "<"(Left, Right : in Date) return Boolean;. Then, our
program could declare D1, D2 : Date; and test D1 < D2. This test would call
our function "<", because < is used between two objects of type Date.
Similarly, we can overload "*" to give the dot product of two vectors:
type Vector is array(Integer range <>) of Float;
V1, V2 : Vector(1 .. 100);
X : Float;
function "*"(Left, Right : in Vector) return Float is
...
end "*";
...
X := V1 * V2; -- This calls our function "*".
There are some restrictions when using infix notation. First, all of the
operators above (except abs, not, +, and -) must be used between two
expressions, and thus the function specification must have exactly two
parameters. Traditionally, the two parameters are called Left and Right. The
operators abs and not must have just one expression on the right, and + and -
may have one expression on the right, or come between two expressions. These
restrictions come from the way the compiler handles operators. For example,
the compiler can handle X := - X;, but it's not designed to handle X := * X;.
Second, in Ada 83 the function "=" must return type Boolean, its two parameters
must be of the same type, and that type must be a limited private type, to be
discussed later. For all the types discussed so far, function "=" is already defined by Ada. Two records are equal if all of their corresponding fields are
equal, and two arrays are equal if they have the same length and all of their
corresponding elements are equal.
Note that in Ada 83 we can't redefine function "/=". However, if we redefine
"=" for some limited private type, we can use /=. Ada will call our function
"=" and negate the result. For example,
type Text is limited private; -- to be discussed later
T1, T2 : Text;
function "="(Left, Right : in Text) return Boolean is
...
end "=";
...
if T1 /= T2 then -- Calls our "=" and reverses the result.
...
end if;
Also, in Ada 83 and Ada 95, we can't redefine in, not in, and then, or else, or
:=. Technically, these aren't operators.
In Ada 95, we may redefine =to return any type, not just Boolean. However, if
we do redefine = to return type Boolean, then we have automatically also
redefined /= to call our = and reverse the result.
If, on the other hand, we redefine = to return some type other than Boolean,
then /= is not automatically redefined.&mnsp; However, in this case Ada 95 lets us
separately redefine /= if we want to, and it may return any type.
Functions using infix notation can't be compiled directly into the library.
They must either be declared locally inside a procedure or function, or placed
inside a package. This is done because many implementations of Ada create
files with names based on the name of the function, procedure, or package being
compiled. Since many of the operators are punctuation marks, they would create
file names that are illegal on most systems.
For the same reason, two functions or procedures with the same name can't be
compiled directly into the library; on many systems that would attempt to give
the same name to two different files. For example, the package specification
below is legal. However, the package body can't declare both Display's
separate, and it can't declare either "*" separate.
package P is
type Complex is ...
type Vector is ...
procedure Display(C : in Complex);
procedure Display(V : in Vector);
function "*"(Left, Right : in Complex) return Complex;
function "*"(Left, Right : in Vector) return Float;
end P;
Question
package K is
type Complex is record
Re, Im : Float;
end record;
type Vector is array(Integer range <>) of Float;
function "+"(Left, Right : in Complex) return Complex;
function "*"(Left, Right : in Complex) return Complex;
function "*"(Left, Right : in Vector) return Float;
function Conjugate(C : in Complex) return Complex;
procedure Display(C : in Complex);
procedure Display(V : in Vector);
end K;
The above is a package specification. In the package body, how many
subprograms could be made
separate:
0,
1,
2,
3,
4,
5, or
6?
No, the function Conjugate can be made separate, because its name isn't
overloaded and it doesn't use infix notation. Also, one, but not both, Display
procedures can be made separate.
1. No, the function Conjugate can be made separate, because its name isn't
overloaded and it doesn't use infix notation. Also, one, but not both, Display
procedures can be made separate
2. You're right! Only function Conjugate and at most one Display can be made
separate. Functions using infix notation can't be made separate, and the name
Display is overloaded.
3. No, the function Conjugate can be made separate, because its name isn't
overloaded and it doesn't use infix notation. Also, one, but not both, Display
procedures can be made separate
4. No, the function Conjugate can be made separate, because its name isn't
overloaded and it doesn't use infix notation. Also, one, but not both, Display
procedures can be made separate
5. No, the function Conjugate can be made separate, because its name isn't
overloaded and it doesn't use infix notation. Also, one, but not both, Display
procedures can be made separate
6. No, the function Conjugate can be made separate, because its name isn't
overloaded and it doesn't use infix notation. Also, one, but not both, Display
procedures can be made separate
< prev next >