22.07.2013 Views

Quick Ada - RMIT University

Quick Ada - RMIT University

Quick Ada - RMIT University

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Quick</strong> <strong>Ada</strong><br />

● 1 The history of <strong>Ada</strong><br />

● 2 Sample programs<br />

● 3 Lexical conventions, contents<br />

● 4 Basics types of <strong>Ada</strong>, contents<br />

● 5 Control Structures, contents<br />

● 6 Arrays, contents<br />

● 7 Records, contents<br />

● 8 Subprograms contents<br />

● 9 Packages, contents<br />

● 10 Generics, contents<br />

● 11 Exceptions, contents<br />

● 12 Files, contents<br />

● 13 Access types, contents<br />

● 14 Object Oriented features of <strong>Ada</strong><br />

● 15 Concurrency support, contents<br />

● 16 Language interfaces, contents<br />

● 17 Idioms, contents<br />

● 18 Designing <strong>Ada</strong> programs, contents<br />

● Appendix A Text_IO package<br />

● Appendix B Sequential_IO package<br />

● Appendix C Direct_IO package<br />

<strong>RMIT</strong> specific information<br />

● Appendix D Text_package package<br />

● Appendix E Simple_io package<br />

● Appendix F GNAT <strong>Ada</strong><br />

● Appendix G <strong>RMIT</strong> <strong>Ada</strong> resources<br />

Copyright Dale Stanbrough<br />

They may be used freely, but they must not be resold, either in part or in whole, without permisssion.<br />

Email: dale@rmit.edu.au<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln.html [10/14/2000 12:54:39 PM]


The postscript files for the notes... (and don't i just wish they were in one file!).<br />

Chapter 1<br />

Chapter 2<br />

Chapter 3<br />

Chapter 4<br />

Chapter 5<br />

Chapter 6<br />

Chapter 7<br />

Chapter 8<br />

Chapter 9<br />

Chapter 10<br />

Chapter 11<br />

Chapter 12<br />

Chapter 13<br />

Chapter 14<br />

Chapter 15<br />

Chapter 16<br />

Chapter 17<br />

Chapter 18<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/ps/index.html [10/14/2000 12:54:42 PM]


1 The history of <strong>Ada</strong><br />

Overview<br />

An understanding of the reasons why <strong>Ada</strong> was developed and the history of its development gives an<br />

appreciation of the language and its future.<br />

History of <strong>Ada</strong>.<br />

In 1974 the US Department of Defence (DoD) realised that it was spending too much time, effort and<br />

money developing and maintaining embedded computer systems (systems stuck in hardware e.g. missile<br />

guidance systems).<br />

At this time over 450 different languages or language extensions were in use. This increased the time and<br />

costs for developing new systems and in continually retraining people to become familiar with existing<br />

systems. Maintenance was also hampered by the lack of standardisation of support tools ( editors,<br />

compilers etc). All these factors led to the DoD realising it needed a single powerful language that could<br />

be used by all embedded computer suppliers.<br />

The developement work began in 1975 with the DoD producing a list of language requirements which<br />

was widely circulated; however no existing language specified the criteria so in 1977 DoD requested<br />

proposals for a new language. Unlike committee languages such as COBOL, the new language was the<br />

subject of a competition and extensive industry and academic review.<br />

Of numerous entries four were selected for further refinement. This was later cut down to two competing<br />

entries from which one was finally selected from the company Cii-Honeywell Bull. This language was<br />

christened <strong>Ada</strong>. The design team was led by Jean Ichbiah who had overall control over the language.<br />

In 1983 the language became an ANSI standard ANSI/MIL-STD-1815A. It became an ISO standard the<br />

following year. The language is defined in a reference manual often referred to as the LRM. References<br />

to this manual occur often in books on the language, and in many compiler error messages. This book is<br />

recommended for any <strong>Ada</strong> site; although hard to read it is the final authority for any <strong>Ada</strong> question (an<br />

ongoing group has been formed to clarify any inconsistancies detected in the language).<br />

The language has since undergone revision, with ISO standardisation of the new standard achieved in<br />

early 1995. This new <strong>Ada</strong> fixes many of the flaws in the original language, and extends it in many useful<br />

ways.<br />

To prevent the proliferation of various imcompatable versions of <strong>Ada</strong> the <strong>Ada</strong> Joint Program Office (the<br />

body set up for control of the language) took a very novel position - they trademarked the name <strong>Ada</strong>.<br />

You were not allowed to market "<strong>Ada</strong>" compilers unless they have passed a compliance test. This has<br />

subsequently been relaxed, the protected term now being `Validated <strong>Ada</strong>'.<br />

The resulting <strong>Ada</strong> validation certificate is limited in duration and has an expiry date. Once it expires the<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/1_history.html (1 of 2) [10/14/2000 12:55:02 PM]


compiler can no longer be marketed as a `Validated <strong>Ada</strong>' compiler. In this way the AJPO has ensured<br />

that all currently marketed compilers comply with the current standards.<br />

The aim is to ensure that any <strong>Ada</strong> program can be compiled on any system - in this regard the AJPO has<br />

succeeded better than many other language groups.<br />

Design Goals<br />

From the <strong>Ada</strong> LRM:<br />

"<strong>Ada</strong> was designed with three overriding concerns: program reliability and maintenance, programming as<br />

a human activity, and efficiency"<br />

Of note is the sentence, also from the LRM:<br />

"Hence emphasis was placed on program readability over ease of writing".<br />

These design goals can be seen in the language. It has strong typing and enforcable abstractions which<br />

have shown to increase reliability and ease maintenance.<br />

It eschews cryptic syntax for a more verbose English style for the sake of readability (readability,<br />

programming as a human activity). Also almost all constructs can be efficiently implemented.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/1_history.html (2 of 2) [10/14/2000 12:55:02 PM]


2 Sample programs<br />

Overview<br />

This chapter shows a few simple programs to give the 'feel' of an <strong>Ada</strong> program.<br />

Simple programs<br />

One of <strong>Ada</strong>'s major philosophies is encapsulation. All of the standard I/O routines come presupplied in packages that can be<br />

included in a program. The following examples use the text_io package.<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

-- a package containing the"put_line" procedure<br />

procedure hello is -- candidate for the "main" procedure.<br />

begin<br />

put_line("hello");<br />

end;<br />

Unlike C which has a function main, and Pascal which has a program, any parameterless procedure can be a "main" routine.<br />

The procedure thus designated is chosen at link time.<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

with Hello;<br />

-- include our previous procedure<br />

procedure your_name is<br />

name :string(1..100); -- 100 character array<br />

last :natural; -- can only contain natural integers<br />

begin<br />

put("Hello what is your name? ");<br />

get_line(name,last);<br />

end;<br />

for i in 1..10 loop -- i is implicity declared<br />

Hello;<br />

put_line(" there " & name(1..last));<br />

-- & string concatenation<br />

-- name(1..last)- array slice<br />

end loop; -- control structure labelled<br />

Inputting numbers requires the use of a package devoted to the task. A package to do this exists with all <strong>Ada</strong> implementations.<br />

Other simpler packages are often created within a site (see package simple_io in the appendices).<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

with <strong>Ada</strong>.Integer_Text_IO; use <strong>Ada</strong>.Integer_Text_IO<br />

procedure Age is<br />

Age :integer range 0..120;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/2_sample_programs.html (1 of 2) [10/14/2000 12:55:02 PM]


egin<br />

Put("hello, how old are you ? ");<br />

Get(Age); -- might cause an exception if value entered<br />

--is outside range<br />

if Age < 18 then<br />

put_line("ha! you're just a baby");<br />

elsif Age < 60 then -- note spelling of elsif<br />

put_line("working hard?");<br />

else<br />

put_line("Now take it easy old fella!");<br />

end if;<br />

exception<br />

when constraint_error =><br />

put_line("sorry only ages 0..120 are accepted");<br />

end age; -- procedure name is optional at end;<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/2_sample_programs.html (2 of 2) [10/14/2000 12:55:02 PM]


3 Lexical conventions<br />

Overview<br />

The lexical conventions describe the allowable character sequences that are used to create identifiers,<br />

numbers and the special values used in the language. Implementations must support 200 characters<br />

lexical elements at the least.<br />

Identifiers<br />

Can be of any length (but can be restricted by an implementation).<br />

Can only contain characters, digits and underscores<br />

Must start with a character<br />

Are case insensitive<br />

E.g.<br />

Apple, apple, APPLE -- same identifier<br />

Max_Velocity_Attained<br />

Minor_Number_ -- illegal, trailing underscore<br />

Minor__Revision -- illegal, consecutive underscores<br />

Literals<br />

Literals are the typed representation of the values stored in the program.<br />

Numeric literals can be broken up with non consecutive underscores as desired.<br />

E.g. 5_101_456 is the number 5101456<br />

3.147_252_6<br />

Numeric literals cannot end or start with an underscore.<br />

Exponential notation is available for floats and integers<br />

E.g. 2E6, 9E+4<br />

Numeric literals can be represented in different bases (2-16).<br />

E.g. 2#1011#, 10#45#, 16#Fe23#, 2#11100.11001#<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/3_lexical_conventions.html (1 of 3) [10/14/2000 12:55:04 PM]


Float literals must have a digit either side of the radix point<br />

E.g. 3.14, 100.0<br />

Thus the numbers 100. or .034 are not valid float numbers.<br />

Character literals are surrounded by single quotes<br />

E.g. 'a', 'b'<br />

String literals are surrounded by double quotes<br />

E.g. "<strong>Ada</strong>", "literal", "embedded ""strings""!"<br />

String literals cannot contain the tab character. String values can contain them, this can be achieved by<br />

either concatenating strings and characters together, or directly inserting the character into the string.<br />

Comments<br />

Comments are introduced by the -- symbol and extend to the end of the line.<br />

Reserved Words<br />

abort else new return<br />

abs elsif not reverse<br />

abstract* end null<br />

accept entry select<br />

access exception separate<br />

aliased* exit of subtype<br />

all or<br />

and for others tagged*<br />

array function out task<br />

at terminate<br />

generic package then<br />

begin goto pragma type<br />

body private<br />

if procedure<br />

case in protected* until*<br />

constant is use<br />

raise<br />

declare range when<br />

delay limited record while<br />

delta loop record while<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/3_lexical_conventions.html (2 of 3) [10/14/2000 12:55:04 PM]


digits renames<br />

do mod requeue* xor<br />

Reserved words followed by an asterisk have been introduced in <strong>Ada</strong>95.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/3_lexical_conventions.html (3 of 3) [10/14/2000 12:55:04 PM]


1.<br />

3 Lexical conventions<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

to the index...<br />

Overview<br />

Identifiers<br />

Literals<br />

Comments<br />

Reserved Words<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/3_lexical_conventions_ToC.html [10/14/2000 12:55:04 PM]


4 Basics types of <strong>Ada</strong><br />

Overview<br />

This chapter introduces some of the types available in <strong>Ada</strong> as well as the operations available on and attributes of those types.<br />

Types and subtypes<br />

All types<br />

Elementary<br />

Scalar<br />

literals<br />

Scalar types<br />

Real<br />

Discrete<br />

Universal_integer -- all integer<br />

Root_integer -- <strong>Ada</strong>95 only<br />

Signed integer<br />

Modular integer -- <strong>Ada</strong>95 nsigned types<br />

Enumeration<br />

User defined<br />

Character<br />

Boolean<br />

Universal_real -- all real literals<br />

Root_real -- <strong>Ada</strong>95 only<br />

Floating point<br />

Fixed point<br />

ordinary fixed point<br />

decimal fixed point -- <strong>Ada</strong> 95 only<br />

Access<br />

Access-to-object<br />

Access-to-subprogram -- <strong>Ada</strong> 95 only<br />

Composite<br />

Array<br />

String<br />

Other array<br />

Untagged record<br />

Tagged record -- <strong>Ada</strong>95<br />

Task<br />

Protected -- <strong>Ada</strong>95<br />

The predefined package Standard contains declarations for the standard types such as integer, float, character and boolean, as well as<br />

(notionally) defining the operations available on them.<br />

All numeric literals belong to the class universal_integer or universal_float. Many of the attributes of the language (discussed later)<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types.html (1 of 7) [10/14/2000 12:55:07 PM]


also return a universal value. These universal types are compatable with any corresponding integer, float or fixed type.<br />

E.g.<br />

Max_Customers : constant := 10_000; -- 10_000 is a "universal integer"<br />

-- It is not of type "integer"<br />

Subtypes can be created in <strong>Ada</strong> by restricting an existing type, by defining a new type based on an existing type or by enumerating<br />

the possible values of the type. A discussion of how to create these new types follows a look at the predefined types and their<br />

attributes.<br />

The following operations are defined for all scalar types.<br />

=, /= Equality, inequality<br />

=<br />

in, not in Range membership text<br />

Integer types<br />

The following are examples of integer declarations. Here the standard predefined integer type is used.<br />

Count : Integer;<br />

X,Y,Z : Integer;<br />

Amount : Integer := 0;<br />

Unity : constant Integer := 1;<br />

Speed_Of_Light : constant := 300_000; -- type universal_integer<br />

A_Month : Integer range 1..12;<br />

subtype Months is Integer range 1..12; -- a restricted integer<br />

-- subtypes are compatable with their base type (here integer)<br />

-- i.e. variables of type month can be mixed with integer variables<br />

type File_Id is new Integer; -- a new integer family derived<br />

-- from type integer;<br />

type result_range is new Integer range 1..20_000;<br />

-- a derived type with a constraint<br />

type other_result_range is range 1..100_000;<br />

-- a type derived from root_integer<br />

-- the compiler chooses an appropriate sized integer to suit the range.<br />

The following operators are also defined for all integer types.<br />

+,-,*,/<br />

** Exponentiation (integer exponent only)<br />

mod Modulus<br />

rem Remainder<br />

abs Absolute value<br />

Floating point types<br />

The following are examples of floating point declarations. Floating point numbers have a relative error.<br />

x : float;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types.html (2 of 7) [10/14/2000 12:55:07 PM]


a,b,c : float;<br />

pi : constant float := 3.14_2;<br />

Avogadro : constant := 6.027E23; -- type universal_float<br />

subtype temperatures is float range 0.0..100.0;<br />

type result is new float range 0.0..20_000.0;<br />

type Velocity is new Float;<br />

type Height is new Float;<br />

-- can't accidentally mix velocities and heights without an explicit<br />

-- type conversion.<br />

type Time is digits 6 range 0.0..10_000.0;<br />

-- six decimal digits of accuracy required, in this range.<br />

type Degrees is digits 2 range -20.00..100.00;<br />

-- two decimal digits of accuracy required.<br />

The following operators are also defined for all float types.<br />

+,*,/,-<br />

** Exponentiation (integer exponent only)<br />

abs Absolute value<br />

Fixed point types<br />

The following are examples of fixed point declarations. Fixed point numbers have a bounded error, the absolute value of which is<br />

called the delta of the type.<br />

type Volt is delta 0.125 range 0.0 .. 255.0;<br />

type Fraction is delta System.Fine_Delta range -1.0..1.0; -- <strong>Ada</strong>95<br />

-- Fraction'last = 1.0 - System.Fine_Delta<br />

type Money is delta 0.01 digits 15; -- decimal fixed point<br />

subtype Salary is Money digits 10;<br />

The last example shows the usefulness of fixed point types - the ability to specify exactly how accurate the type should be. This<br />

allows control over facilities such as errors in rounding expressions, for example.<br />

Enumeration types<br />

An enumeration type is defined by listing all the possible values of the type.<br />

type Computer_Language is (Assembler, Cobol, Lisp, Pascal, <strong>Ada</strong>);<br />

type C_Letter_Languages is (Cobol, C);<br />

Values of this type can be defined as follows:<br />

a_language : computer_language;<br />

early_language : computer_language := cobol;<br />

first_language : constant computer_language := assembler;<br />

example : c_letter_language := cobol;<br />

Note that <strong>Ada</strong> can distinguish between enumeration literals from different types in most cases by examining the context. If this is not<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types.html (3 of 7) [10/14/2000 12:55:07 PM]


possible then type qualification must be used.<br />

Enumeration types are useful to encode simple control codes used internally in a program.<br />

There are two predefined enumerated types in the package STANDARD, the type character and the type boolean.<br />

Booleans<br />

The two values of boolean variables is true and false.<br />

The following opeartors can be used with boolean types<br />

and or not xor /= = 'and then' 'or else'<br />

<strong>Ada</strong> will not allow an unparenthesied expression to contain both and's and or's. This decreases the likelihood of misreading the intent<br />

of a complicated boolean expression.<br />

E.g.<br />

(a < b) and (b > c) or (d < e) -- illegal<br />

((a < b) and (b > c)) or (d < e) -- ok<br />

Usually when evaluating a boolean expression, the compiler is free to rearrange the evaluation of the terms as it sees fit. Both terms<br />

will be evaluated. For example in the following either term may be evaluated first.<br />

if a < b and c > d then ...<br />

However in some instances we wish to evaluate the terms in a defined order, and stop evaluations as soon as the value of the<br />

expression can be determined.<br />

For example<br />

if a /= 0 and then b/a > 5.0 then . . .<br />

Here we see if a is non zero before further evaluation.<br />

The 'or else' statement is similar, only evaluation stops as soon as a term evaluates to true. This can be useful, for example, in a<br />

recursive search of a tree.<br />

E.g.<br />

return Present(Node.Left, Key) or else Present(Node.Right, Key);<br />

Character<br />

<strong>Ada</strong>83 initially had 7 bit characters. This restriction was eased before <strong>Ada</strong>95 arrived, but is still enforced by older compilers such as<br />

the Meridian <strong>Ada</strong> compiler. This creates problems when attempting to display graphic characters on a PC; generally you have to use<br />

integers to display characters above Ascii 127, using special routines supplied by the compiler vendor.<br />

<strong>Ada</strong>95's Character type is based on Latin-1 and provides for 256 character positions. <strong>Ada</strong>95 also supports wide characters (ISO<br />

10646 Basic Multilingual Plane (BMP)) and so all modern compilers can cope with 8 bit characters.<br />

The 7 bit character set is described in the obsolecent package Standard.Ascii. The 8 bit character set is described in the package<br />

Standard. The package <strong>Ada</strong>.Characters.Latin_1 provides usable names for the characters.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types.html (4 of 7) [10/14/2000 12:55:07 PM]


Subtypes<br />

We can restrict the range of values a variable can take by declaring a subtype with a restricted range of values (this corresponds to<br />

Pascal's user defined types). Any attempt to place an out-of-range value into a variable of a subtype results in an exception (program<br />

error). In this way program errors can be discovered. The syntax for a subtype declaration is<br />

subtype Name is Base_Type;<br />

subtype Name is Base_Type range lowerbound . . upperbound;<br />

Examples of declaring subtypes are given below.<br />

type Processors is (M68000, i8086, i80386, M68030, Pentium, PowerPC);<br />

subtype Old_Processors is Processors range M68000..i8086;<br />

subtype New_Processors is Processors range Pentium..PowerPC;<br />

subtype Data is Integer;<br />

subtype Age is Data range 0 . . 140;<br />

subtype Temperatures is Float range -50.0 .. 200.0;<br />

subtype Upper_Chars is Character range 'A' .. 'Z';<br />

Subtypes are compatable with their base types . They can be placed in the same place as any variable of the base type can. Also<br />

variables of different subtypes that are derived from the same base type are compatable.<br />

My_Age : Age;<br />

Height : Integer;<br />

Height := My_Age; -- silly, but never causes a problem.<br />

My_Age := Height; -- will cause a problem if height's<br />

-- value is outside the range of<br />

-- my_age (0..140), but still<br />

-- compilable.<br />

Derived types<br />

When subtypes are created they are still compatable with their base type. Sometimes we may wish to create distinctly new types that<br />

are not associated with the original type at all. This concept of type is very different to that provided by Pascal.<br />

To do this we create a derived type from a parent type using the following syntax<br />

type Name is new Parent_Type;<br />

type Name is new Parent_Type range lower bound . . upper bound;<br />

A derived type is a completely new type and is incompatable with any other type, even those derived from the same parent type.<br />

Derived types should be used when the modelling of a particular object suggests that the parent type is inappropriate, or you wish to<br />

partition the objects into distinct and unmixable classes.<br />

type Employee_No is new Integer;<br />

type Account_No is new Integer range 0..999_999;<br />

Here employee_no's and account_no's are distinct and unmixable, they cannot be combined together without using explicit type<br />

conversion. Derived types inherit any operation defined on the base type. For example if a record was declared that had procedures<br />

push and pop, a derived type could be declared that would automatically have inherit the procedures.<br />

Another important use of derived types is to produce portable code. <strong>Ada</strong> allows us to create a new level of abstraction, one level<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types.html (5 of 7) [10/14/2000 12:55:07 PM]


higher than, for example, the abstraction of Integer over a series of bits.<br />

This is specified by using derived types, without a parent type.<br />

type Name is range ;<br />

For example,<br />

type Data is range 0..2_000_000;<br />

Here the compiler is responsible for choosing an appropriately sized integer type. On a PC, it would be a 32 bit size, equivalent to<br />

long_integer. On a Unix workstation it would still be a 32 bit integer, but this would be equivalent to an integer. Letting the compiler<br />

choose frees the programmer from having to choose. Compiling it on a new host does not require changing the source code.<br />

Type conversion<br />

Despite the usefullness of being able to create distinct types, there are still occasions where we wish to convert from one type to<br />

another. One typical instance is to convert from one integer to float, or vice versa.<br />

X : Integer:= 4;<br />

Y : Float;<br />

Y := float(X);<br />

. . .<br />

X := Integer(Y);<br />

This causes the compiler to insert the appropriate code for type conversion (if needed) as part of the translation.<br />

Do not confuse this with unchecked conversions (covered later) which often perform no internal representation transformation.<br />

It needs to be stressed however that types are created distinct for a reason and that attempts to subvert the compiler's checks by<br />

performing type conversions should be either discouraged or performed only when semantically meaningfull.<br />

Type Qualification<br />

In some situations an expression's or value's type can be ambiguous.<br />

For example,<br />

type primary is (red, green, blue);<br />

type rainbow is (red, yellow, green, blue, violet);<br />

...<br />

for i in red..blue loop -- this is ambiguous<br />

Here we need to specify precisely what type is required. This is done with type qualification.<br />

for i in rainbow'(red)..rainbow'(blue) loop<br />

for i in rainbow'(red)..blue loop -- only one qualification needed<br />

for i in primary'(red)..blue loop<br />

Type qualification does not change a value's type. It merely informs the compiler of what type the programmer thinks it should be.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types.html (6 of 7) [10/14/2000 12:55:07 PM]


Attributes<br />

<strong>Ada</strong> also provides the ability to enquire about a type or object from within the code by using attributes. Some of the attributes for<br />

discrete types are<br />

Integer'first -- the smallest Integer<br />

Integer'last -- the largest integer<br />

Processors'succ(M68000) -- successor of the M68000<br />

Upper_Chars'pred('C') -- the predecessor of 'C' ('B')<br />

Integer'image(67) -- the string " 67" -- space for a '-'<br />

Integer'value("67") -- the integer 67.<br />

Processors'pos(M68030) -- the position of M68030 in the type.<br />

-- (3, position 0 is first).<br />

An example of the use of an attribute is<br />

subtype Positive is Integer range 1..Integer'last;<br />

Here we achieve a maximal positive integer range without introducing any system dependent features.<br />

In <strong>Ada</strong>83 non discrete types such as float, fixed and all their subtypes and derived types, the concepts of pred, succ and pos do not<br />

have meaning. In <strong>Ada</strong>95 they do. All other scalar attributes apply to the real types.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types.html (7 of 7) [10/14/2000 12:55:07 PM]


1.<br />

4 Basics types of <strong>Ada</strong><br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

9.<br />

10.<br />

11.<br />

12.<br />

13.<br />

14.<br />

to the index...<br />

Overview<br />

Types and subtypes<br />

Scalar types<br />

Integer types<br />

Floating point types<br />

Fixed point types<br />

Enumeration types<br />

Booleans<br />

Character<br />

Subtypes<br />

Derived types<br />

Type conversion<br />

Type Qualification<br />

Attributes<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/4_basic_types_ToC.html [10/14/2000 12:55:08 PM]


5 Control Structures<br />

Overview<br />

The control structures of <strong>Ada</strong> are similar in style to most conventional languages. However some differences<br />

remain.<br />

As usual <strong>Ada</strong> control structures are designed for maximum readability, all control structures are clearly ended<br />

with an 'end something'.<br />

If statements<br />

All if statements end with an end if statement.<br />

if boolean expression then<br />

statements<br />

end if;<br />

if boolean expression then<br />

statements<br />

else<br />

other statements<br />

end if;<br />

To prevent the common sight of if's marching across the page there is the elsif structure. As many elsifs as<br />

required can used. Note the spelling of elsif carefully.<br />

if boolean expression then<br />

statements<br />

elsif boolean expression then<br />

other statements<br />

elsif boolean expression then<br />

more other statements<br />

else<br />

even more other statements<br />

end if;<br />

The final else is optional in this form of the if.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/5_control_structures.html (1 of 5) [10/14/2000 12:55:09 PM]


Case statements<br />

The case statement must have an action for every possible value of the case item. The compiler checks that this<br />

is the case. In situations where it is impractical to list every possible value the others case label should be used.<br />

Each choice's value can be either a single value (e.g. 5), a range (1..20) or a combination of any of these,<br />

separated by the character '|' .<br />

Each of the case values must be a static value i.e. it must be able to be computed at compile time.<br />

case expression is<br />

when choices => statements<br />

when choices => statements<br />

. . .<br />

when others => statements<br />

end case;<br />

Important The others option is mandatory in a case statement unless all possible values of the case selector have<br />

been enumerated in the when statements.<br />

case letter is<br />

when 'a'..'z'| 'A'..'Z' => put ("letter");<br />

when '0'..'9' => put ("digit! value is"); put (letter);<br />

when ''' | '"' | '`' => put ("quote mark");<br />

when '&' => put ("ampersand");<br />

when others => put ("something else");<br />

end case;<br />

Each of the case values must be a static value i.e. it must be able to be computed at compile time.<br />

Loops<br />

All <strong>Ada</strong> looping constructs use the loop/ end loop form. Several variations exist. The exit statement can be used<br />

to break out of loops.<br />

Simple Loops<br />

The simple loop is an infinite loop. It is usually used in conjuction with the exit statement.<br />

loop<br />

statements<br />

end loop;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/5_control_structures.html (2 of 5) [10/14/2000 12:55:09 PM]


While Loops<br />

The while loop is identical to the Pascal while loop. The test is performed before the loop is entered.<br />

while boolean expression loop<br />

statements<br />

end loop;<br />

For Loops<br />

The for looping contructs are similar to those in Pascal.<br />

There are several rules that apply to the use of for statements.<br />

l The index in the for loop must be a discrete type - floats are not allowable.<br />

l The index is not explicity declared.<br />

l The index cannot be modified by any statements (read only)<br />

Note that the statements will not be executed if the lower value of the range is higher than the upper value.<br />

Important The index used in the for loop does not need to be declared. It is implicitly declared to be of the same<br />

type as the range.<br />

for index in range loop<br />

statements<br />

end loop;<br />

for i in 1..20 loop<br />

put (i);<br />

end loop;<br />

To count backwards...<br />

for index in reverse range loop<br />

statements<br />

end loop;<br />

for i in reverse 1..20 loop<br />

put(i);<br />

end loop;<br />

A type can be used as a range.<br />

declare<br />

subtype list is integer range 1..10;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/5_control_structures.html (3 of 5) [10/14/2000 12:55:09 PM]


egin<br />

for i in list loop<br />

put(i);<br />

end loop;<br />

end;<br />

Here the type list is being used as a range. In a similar manner an enumerated type can be used.<br />

Exit and exit when<br />

The exit and exit when statements can be used to exit loops prematurely. Execution continues with the first<br />

statement following the loop. The two forms have identical effects. The following code segments are identical.<br />

loop<br />

statements<br />

if boolean expression then<br />

exit;<br />

end if;<br />

end loop;<br />

loop<br />

statements<br />

exit when boolean expression;<br />

end loop;<br />

Labeled loops<br />

An exit statement will normally only exit the inner most loop in which it is enclosed. We can label loops and<br />

modify the exit statement accordingly to allow for an escape from a series of nested loops. In all cases the<br />

instruction next executed is that following the loop exited.<br />

outer_loop:<br />

loop<br />

statements<br />

loop<br />

end loop;<br />

end loop outer_loop;<br />

Note that the end loop statement is also labelled.<br />

statements<br />

exit outer_loop when boolean_expression;<br />

end if;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/5_control_structures.html (4 of 5) [10/14/2000 12:55:09 PM]


Goto statement<br />

The goto statement is provided in <strong>Ada</strong> for use in exceptional situations.<br />

goto label;<br />

<br />

The use of goto's is very restrictive and quite sensible. You cannot jump into if statements, loop statements or,<br />

unlike Pascal, out of procedures.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/5_control_structures.html (5 of 5) [10/14/2000 12:55:09 PM]


1.<br />

5 Control Structures<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

9.<br />

10.<br />

to the index...<br />

Overview<br />

If statements<br />

Case statements<br />

Loops<br />

Simple Loops<br />

While Loops<br />

For Loops<br />

Exit and exit when<br />

Labeled loops<br />

Goto statement<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/5_control_structures_ToC.html [10/14/2000 12:55:10 PM]


6 Arrays<br />

Overview<br />

Most languages provide arrays of one sort of another. Those provided by <strong>Ada</strong> are most similar to Pascal's, with the inclusion of<br />

several very handy features.<br />

Unconstrained arrays, dynamic arrays and array attributes are some of the extras offered.<br />

Simple arrays<br />

Generally when declaring an array, a type definition would be created first, and an array would be declared using this definition.<br />

type Stack is array (1..50) of Integer;<br />

Calculator_Workspace : stack;<br />

type stock_level is Integer range 0..20_000;<br />

type pet is (dog,budgie,rabbit);<br />

type pet_stock is array(pet) of stock_level;<br />

store_1_stock:pet_stock;<br />

store_2_stock:pet_stock;<br />

In general the declaration of an array has this form:<br />

type array_name is array (index specification) of type;<br />

Some points to note:<br />

* The index specification can be a type (e.g. pet).<br />

* The index specification can be a range (e.g. 1..50).<br />

Index values must be of a discrete type.<br />

Anonymous arrays<br />

Arrays can also be declared directly, without using a predefined type.<br />

no_of_desks : array(1..no_of_divisions) of integer;<br />

This is known as an anonymous array (as it has no explicit type) and is incompatable with other arrays - even those declared exactly<br />

the same. Also they cannot be used as parameters to subprograms. In general it is better to avoid them.<br />

Accessing and setting arrays<br />

To access an element of an array<br />

if store_1_stock(dog) > 10 then ...<br />

It is instructive to note that accessing an <strong>Ada</strong> array is indistinguishable from calling a function in all respects.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/6_arrays.html (1 of 5) [10/14/2000 12:55:11 PM]


To store a value into an array...<br />

store_2_stock(rabbit) := 200;<br />

Array aggregates<br />

The values of an array can all be assigned at once, using an aggregate. An aggregate must specify a value for every array element.<br />

store_1_stock := (5,4,300);<br />

The values in the aggregate are assigned in order to the values in the array.<br />

It is also possible to use a named version of the aggregate where the individual elements of the array are named.<br />

store_1_stock := (dog => 5, budgie => 4, rabbit => 300);<br />

It is illegal to combine both notations in the one aggregate.<br />

store_1_stock := (5,4,rabbit=>300); -- illegal<br />

Aggregates can also be used in declarations in exactly the same manner, using either notation.<br />

store_1_stock:pet_stock := (5,4,300);<br />

A discrete range can also be included in the aggregate.<br />

store_1_stock := (dog..rabbit=>0);<br />

The others option is particuarly useful in setting all elements of an array to a given value. In these situations type qualification is<br />

often required.<br />

new_shop_stock:pet_stock := (others := 0);<br />

Consider the following declarations:<br />

declare<br />

type numbers1 is array(1..10) of integer;<br />

type numbers2 is array(1..20) of integer;<br />

a :numbers1;<br />

b :numbers2;<br />

begin<br />

a := (1,2,3,4, others => 5);<br />

end;<br />

The <strong>Ada</strong> language doesn't like this; if you mix the others option with either positional or named association then you have to qualify<br />

it with a type mark:<br />

a: = numbers1'(1,2,3,4, others => 5);<br />

Constant arrays<br />

Constant arrays can be defined. In this case all elements should be initialised through the use of an aggregate when declared.<br />

type months is (jan, feb,mar,....,dec);<br />

subtype month_days is integer range 1..31;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/6_arrays.html (2 of 5) [10/14/2000 12:55:11 PM]


type month_length is array (jan..dec) of month_days;<br />

days_in_month:constant month_length := (31,28,31,30,...,31);<br />

Array attributes<br />

Attributes associated with arrays are as follows.<br />

array_name'first -- lower bound of the array<br />

array_name'last -- upper bound of the array<br />

array_name'length -- the number of elements in the array<br />

-- array_name'last-array_name'first +1<br />

array_name'range -- the subtype defined by<br />

-- array_name'first . . array_name'last<br />

If the array is multi dimensional then the index should be specified e.g. array_name'range(n) supplies the range for the nth index.<br />

This facility is very useful for stepping through arrays<br />

for i in array_name'range loop<br />

...<br />

end loop<br />

This guarantees that every element will be processed.<br />

Unconstrained array types<br />

Unconstrained array types allow us to declare array types that are identical in all respects to normal array types except one - we don't<br />

declare how long they are.<br />

The declaration of an unconstrained array defines a class of arrays that have the same element type, the same index type and the same<br />

number of indices.<br />

subtype positive is integer range 1..integer'last;<br />

type string is array (positive range ) of character;<br />

The represents a range that has to be specified when a variable of type string is declared (filling in the blank when declaring a<br />

variable).<br />

The type string can be used to define a large class of character arrays, identical except in the number of elements in the array.<br />

To create an actual array, we have to provide a index constraint for the type.<br />

My_Name : String (1..20);<br />

Here the index constraint is the range 1..20. The advantage of this is that all strings declared are of the same type, and can thus be<br />

used as parameter to subprograms. This extra level of abstraction allows for more generalised subprograms.<br />

To process every element of an a variable that is derived from an unconstrained array type requires the use of array attributes such as<br />

a'range, a'first etc. as we cannot be sure what index values incoming arrays may have, such as below.<br />

My_Name : String (1..20);<br />

My_Surname : String (21..50);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/6_arrays.html (3 of 5) [10/14/2000 12:55:11 PM]


Unconstrained arrays are typically implemented with an object that stores the bounds, as well as a pointer to the actual array.<br />

Standard array operations<br />

There are several operations that can be applied to arrays as a whole and not just the individual components.<br />

Assignment<br />

An entire array can be assigned the value of another array. Both arrays must be of the same type. If they are of the same<br />

unconstrained type then they both must have the same number of elements.<br />

declare<br />

my_name :string(1..10) :="Dale ";<br />

your_name :string(1..10) :="Russell ";<br />

her_name :string(21..30) :="Liz ";<br />

his_name :string(1..5) :="Tim ";<br />

begin<br />

your_name := my_name;<br />

your_name := her_name; -- legal, both have same number of<br />

-- elements<br />

his_name := your_name; -- will cause an error, same type but<br />

-- different length<br />

end;<br />

Test for equality, inequality<br />

The tests for equality and inequality are available for (almost) every <strong>Ada</strong> type. Two arrays are equal if each element of the array is<br />

equal to the corresponding element of the other array.<br />

if array1 = array2 then....<br />

Concatenation<br />

Two arrays can be concatenated using the & operator.<br />

declare<br />

begin<br />

end;<br />

type vector is array(positive range ) of integer;<br />

a : vector (1..10);<br />

b : vector (1..5):=(1,2,3,4,5);<br />

c : vector (1..5):=(6,7,8,9,10);<br />

a := b & c;<br />

Put_Line("hello" & " " & "world");<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/6_arrays.html (4 of 5) [10/14/2000 12:55:11 PM]


Ordering operations<br />

The operators = can all be applied to one dimensional arrays. They are of most benefit when comparing arrays of characters.<br />

"hello" < "world" -- returns true<br />

Dynamic arrays<br />

The length of an array can be determined at run time, rather than being specified by the programmer when writing the program.<br />

declare<br />

x : Integer := y --y declared somewhere else<br />

a : array (1..x) of integer;<br />

begin<br />

for i in a'range loop<br />

...<br />

end loop;<br />

end;<br />

procedure demo(item :string) is<br />

copy :string(item'first..item'last) := item;<br />

double:string(1..2*item'length) := item & item;<br />

begin<br />

...<br />

Note that this does not easily allow the user's input to decide the size of the array, and should not be thought of as a device for<br />

achieving this. The second example is much more typical of it's use.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/6_arrays.html (5 of 5) [10/14/2000 12:55:11 PM]


1.<br />

6 Arrays<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

9.<br />

10.<br />

11.<br />

12.<br />

13.<br />

14.<br />

to the index...<br />

Overview<br />

Simple arrays<br />

Anonymous arrays<br />

Accessing and setting arrays<br />

Array aggregates<br />

Constant arrays<br />

Array attributes<br />

Unconstrained array types<br />

Standard array operations<br />

Assignment<br />

Test for equality, inequality<br />

Concatenation<br />

Ordering operations<br />

Dynamic arrays<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/6_arrays_ToC.html [10/14/2000 12:55:12 PM]


7 Records<br />

Overview<br />

Once again <strong>Ada</strong> provides the same facilities as other languages, plus a few extra. Records can have aggregates, discriminants which<br />

allow for variant records, variable sized records and initialized variables.<br />

Simple records<br />

In general the declaration of an record has this form:<br />

type record name is<br />

record<br />

field name 1: type A;<br />

field name 2: type B;<br />

...<br />

field name n: type N;<br />

end record;<br />

For example:<br />

type bicycle is<br />

record<br />

frame :construction;<br />

maker :manufacturer;<br />

front_brake :brake_type;<br />

rear_brake :brake_type;<br />

end record;<br />

Accessing and setting fields<br />

Accessing fields of a record is identical to that in pascal and C, i.e. the name of the variable is followed by a dot and then the field<br />

name.<br />

expensive_bike :bicycle;<br />

expensive_bike.frame := aluminium;<br />

expensive_bike.manufacturer := cannondale;<br />

expensive_bike.front_brake := cantilever;<br />

expensive_bike.rear_brake := cantilever;<br />

if expensive_bike.frame = aluminium then ...<br />

As with arrays records can be assigned an aggregate, a complete set of values for all record elements.<br />

expensive_bike:=(aluminium,cannondale,canitlever,cantilever);<br />

Alternatively the aggregate can use positional notation where each element of the record is named.<br />

expensive_bike := (<br />

frame => aluminium,<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/7_records.html (1 of 4) [10/14/2000 12:55:13 PM]


manufacturer => cannondale,<br />

front_brake => cantilever,<br />

rear_brake => cantilever<br />

);<br />

Both notations can be mixed in the one aggregate with the proviso that all positional values precede named values.<br />

Aggregates can also be used in declarations in exactly the same manner, using either notation.<br />

expensive_bike :bicycle := (aluminium,cannondale,cantilever,cantilever);<br />

The same value can be assigned to different fields by using the '|' character.<br />

expensive_bike := (<br />

Default values<br />

);<br />

frame => aluminium,<br />

manufacturer => cannondale,<br />

front_brake | rear_brake => cantilever<br />

Fields in records can be given default values that are used whenever a record of that type is created (unless it is initialised with<br />

different values).<br />

type bicycle is<br />

record<br />

frame:construction := CromeMolyebdenum;<br />

maker :manufacturer;<br />

front_brake :brake_type := cantilever;<br />

rear_brake :brake_type := cantilever;<br />

end record;<br />

Constant records<br />

Just like normal variables constant records can be created. In this case all fields should be initialised through the use of an aggregate<br />

or default field values.<br />

my_bicycle :constant bicycle<br />

:= ( hi_tensile_steel,<br />

unknown,<br />

front_brake => side_pull,<br />

rear_brake => side_pull);<br />

The fields in a constant record cannot be assigned a value, nor can the record be reassigned a new value.<br />

Discriminants<br />

<strong>Ada</strong> allows records to contain discriminants. These extra fields help to customise the record further. They give an extra level of<br />

abstraction when modelling data; records can be of the same type yet still be different in size or the number of fields.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/7_records.html (2 of 4) [10/14/2000 12:55:13 PM]


Variant records<br />

One of the uses of discriminants allows for variant records. In this case the record contains some fields whose existance is dependent<br />

upon the value of the discriminant.<br />

For example the discriminant in the following record is the variable vehicle_type<br />

type vehicle is (bicycle, car, truck, scooter);<br />

type transport (vehicle_type: vehicle:= car) is<br />

record<br />

owner :string(1..10);<br />

description: string(1..10);<br />

case vehicle_type is<br />

when car =><br />

petrol_consumption:float;<br />

when truck =><br />

diesel_consumption:float;<br />

tare:real;<br />

net:real;<br />

when others => null;<br />

end case;<br />

end record;<br />

The discriminant can be supplied when a record is being declared.<br />

my_car:transport(car);<br />

my_bicycle:transport (vehicle_type => bicycle);<br />

When the discriminant has the value car, the record contains the fields owner, description and petrol_consumption. Attempting to<br />

access fields such as tare and net is illegal and will cause a constraint error to be generated (this can be caught and handled using<br />

exceptions).<br />

Constrained records<br />

The records above are said to be constrained, that is the discriminant can never change. The my_bicycle record does not have the<br />

fields tare, net petrol_consumption etc. nor does the compiler allocate room for them.<br />

We can declare the records without setting an initial value for the discriminant, in which case the record is said to be unconstrained.<br />

In this case the discriminant can take on any value throughout its life. Obviously in this case the compiler has to allocate enough<br />

room for the largest record possible.<br />

Aggregates for records with discriminants must include the value of the discriminant as the first value.<br />

begin<br />

my_transport:transport;<br />

my_transport := (car,"dale ","escort ",30.0);<br />

Unconstrained records<br />

We can create records that do not have a fixed discriminant, it can change throughout the life of the record. However to do this the<br />

discriminant of the record must have a default value.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/7_records.html (3 of 4) [10/14/2000 12:55:14 PM]


type accounts is (cheque, savings);<br />

type account (account_type: accounts:= savings) is<br />

record<br />

account_no :positive;<br />

title :string(1..10);<br />

case account_type is<br />

when savings => interest_rate;<br />

when cheque => null;<br />

end case;<br />

end record;<br />

Here the account record discriminant has a default value of savings.<br />

We can declare a record<br />

household_account:account;<br />

which is created as a savings account. We can change the record type later...<br />

household_account:= (cheque,123_456,"household ");<br />

Other uses of discriminants<br />

As well as being used as a case selector in a record, discriminants can also be used to specify the length of arrays that are components<br />

of the record.<br />

type text (length:positive:=20) is<br />

record<br />

value:string(1..length);<br />

end record;<br />

In this case the length of the array is dependent upon the value of the discriminant. As described above the record can be declared<br />

constrained or unconstrained.<br />

This text record is the usual implementation used for variable length strings and text processing.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/7_records.html (4 of 4) [10/14/2000 12:55:14 PM]


1.<br />

7 Records<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

9.<br />

10.<br />

to the index...<br />

Overview<br />

Simple records<br />

Accessing and setting fields<br />

Default values<br />

Constant records<br />

Discriminants<br />

Variant records<br />

Constrained records<br />

Unconstrained records<br />

Other uses of discriminants<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/7_records_ToC.html [10/14/2000 12:55:14 PM]


8 Subprograms<br />

Overview<br />

Sub programs, covering both procedures and functions, are the basis of all programs in <strong>Ada</strong>. <strong>Ada</strong> provides<br />

features which will be new to Pascal and C programmers. Overloading, named parameters, default parameter<br />

values, new parameter modes and return values of any type all make <strong>Ada</strong> sub programs significantly different.<br />

Procedures<br />

Procedures in <strong>Ada</strong> are similar to those in Pascal. A procedure can contain return statements.<br />

procedure Demo(x:integer; y:float) is<br />

declarations;<br />

begin<br />

statements;<br />

end demo;<br />

Procedures are called in the normal Pascal style:<br />

demo(4, 5.0);<br />

Functions<br />

Functions are very similar to procedures except that they also return a value to the calling sub program. The use<br />

of return statements is very similar to C. Functions can have as many return statements as required. A function<br />

returning a variable of a given type can be used anywhere a variable of that type can be used.<br />

function Even( Number : Integer) return boolean is<br />

begin<br />

if Number mod 2 = 0 then<br />

return true;<br />

else<br />

return false;<br />

end if;<br />

end;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (1 of 9) [10/14/2000 12:55:17 PM]


Subprograms in general<br />

<strong>Ada</strong> has been designed with separate compilation very much in mind. To this end we can produce just a<br />

specification of a subprogram and submit this to the compiler. Once compiled and the description stored in an<br />

attribute file, it can be checked for compatibility with other procedures (and packages) when they are compiled.<br />

By producing a large number of procedure stubs we can pre test the design of a system and pick up any design<br />

errors before any more work is done.<br />

A procedure specification for the above procedure would be:<br />

procedure demo( x:integer; y:float);<br />

If we wish to use a seperately compiled subprogram we can with it in another subprogram.<br />

with demo;<br />

procedure use_demo is<br />

....<br />

begin<br />

...<br />

demo(4,5);<br />

end use_demo;<br />

A function specification may appear as:<br />

function Even(Number : Integer) return Boolean;<br />

Like Pascal and unlike C, a subprogram can contain nested subprograms within them that are not visible outside<br />

the subprogram.<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

with <strong>Ada</strong>.Integer_Text_IO; use <strong>Ada</strong>.Integer_Text_IO;<br />

procedure Display_Even_Numbers is<br />

-- declarations<br />

x:integer;<br />

function even (number:integer) return boolean is<br />

begin<br />

return number mod 2 = 0;<br />

end even;<br />

begin<br />

for i in 1..10 loop<br />

if even(i) then<br />

put(i);<br />

new_line;<br />

end if;<br />

end loop;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (2 of 9) [10/14/2000 12:55:17 PM]


end display_even_numbers;<br />

Parameter modes<br />

<strong>Ada</strong> provides three parameter modes for procedures and functions.<br />

o in<br />

o in out<br />

o out<br />

These modes do not correspond directly to any modes in other languages, they will be discussed below. The<br />

following should be noted.<br />

o All parameters to subprograms are by default in.<br />

The parameter passing mechanism is by copy-in, copy-out for in/out scalars. The language specifies that any other<br />

types can be passed by copy-in/copy-out, or by reference.<br />

<strong>Ada</strong>95 mandates that limited private types (see later) are passed by reference, to avoid problems with the breaking<br />

of privacy.<br />

In mode<br />

Parameters supplied with this mode are like value parameters in Pascal, and normal parameters in C with the<br />

exception that they cannot be assigned a value inside the subprogram. The formal parameter (that in the sub<br />

program) is a constant and permits only reading of the value of the associated actual parameter.<br />

with <strong>Ada</strong>.Integer_Text_IO; use <strong>Ada</strong>.Integer_Text_IO;<br />

procedure Demo(x : in integer; y : in integer) is<br />

begin<br />

x := 5; -- illegal, in parameters are read only.<br />

put(y);<br />

get(y); -- also illegal<br />

end demo;<br />

In out mode<br />

This mode corresponds directly to the var parameters of Pascal. Actual parameters can be used on either the left<br />

or the right hand side of statements in procedures. These parameters are effectively read/write.<br />

procedure Demo( x : in out integer;<br />

y : in integer) is<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (3 of 9) [10/14/2000 12:55:17 PM]


z:constant integer:=x;<br />

begin<br />

x := z*y; -- this is ok!<br />

end demo;<br />

Out mode<br />

The formal parameter is a variable and may be assigned values, however it's initial value is not necessarily<br />

defined, and should not be relied upon.<br />

procedure demo( x:out integer;<br />

y:in integer) is<br />

z : integer := x; -- not wise, X not initialised<br />

begin<br />

x := y;<br />

end demo;<br />

Caution - out mode parameters that aren't initialized!<br />

procedure Location(<br />

target : in key;<br />

position : out Small_Integer_Range;<br />

found : out boolean) is<br />

Here if the target is not found, then if position is not assigned a value, the copy out parameter mode causes the<br />

unitialised value to be placed into the awaiting actual parameter, the associated range check may cause a<br />

constraint error.<br />

E.g.<br />

declare<br />

The_Position : Small_Integer_Range;<br />

begin<br />

Location( Some_Key, The_Position, Result);<br />

...<br />

If result = false, a constraint error may be generated when the procedure returns.<br />

Named parameters<br />

Normally the association between the formal (defined in the sub program specification) and actual parameters<br />

(supplied in the sub program call) is on a one to one basis i.e. it is positional. The first formal parameter is<br />

associated with the first actual parameter, etc.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (4 of 9) [10/14/2000 12:55:17 PM]


To enhance the readability of sub program calls (<strong>Ada</strong> is designed to be readable) we can associate the name of the<br />

formal parameter and the actual parameter. This feature makes sub program calls immensely more readable.<br />

procedure demo(x:integer; y:integer); -- procedure specification<br />

...<br />

demo(x=>5, y=> 3*45); -- when calling, associate formal<br />

-- and actual parameters.<br />

Lining up the parameters vertically can also be an aid to readability.<br />

demo( x => 5,<br />

y => 3*45);<br />

Because the association is made explicit (instead of the implicit association with positional parameters) there is no<br />

need to supply them in the same order as in the sub program specification. There is no restriction on the order in<br />

which named parameters are written.<br />

demo( y => 3*45, x => 5); -- the order of named parameters<br />

-- is irrelavent<br />

Mixing positional and named parameters<br />

Positional parameters and named parameters can be mixed with the one proviso:<br />

positional parameters must precede the named parameters.<br />

procedure square(result : out integer;<br />

number :in integer) is<br />

begin<br />

result:=number*number;<br />

end square;<br />

could be called in the following manners:<br />

square(x,4);<br />

square(x,number => 4);<br />

square(result => x,number => 4);<br />

square(number => 4, result => x);<br />

square(number => 4, x) -- illegal as positional follows named.<br />

Default parameter values<br />

A default value can be be given for any in parameters in the procedure specification. The expression syntax is the<br />

same as that for pre initialised variables and is:<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (5 of 9) [10/14/2000 12:55:17 PM]


with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

procedure print_lines(no_of_lines: integer:=1) is<br />

begin<br />

for count in 1 . . no_of_lines loop<br />

new_line;<br />

end loop;<br />

end print_lines;<br />

This assigns a value for no_of_lines if the procedure is called without a corresponding parameter (either<br />

positional or named).<br />

E.g. the procedure could be called as<br />

print_lines; -- this prints 1 line.<br />

print_lines(6); -- overrides the default value of 1.<br />

Similarly if a procedure write_lines was defined as<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

procedure write_lines(letter :in char:='*';<br />

no_of_lines:in integer:=1) is<br />

begin<br />

for i in 1 . . no_of_lines loop<br />

for i in 1 . . 80 loop<br />

put(letter);<br />

end loop;<br />

new_line;<br />

end loop;<br />

end write_lines;<br />

then it could be called as<br />

write_lines; -- default character, default<br />

-- no. of lines.<br />

write_lines('-'); -- default no. of lines.<br />

write_lines(no_of_lines => 5); -- default character<br />

write_lines('-',5) -- specifying both.<br />

Local subprograms<br />

So far the subprograms presented have all been independent compilation units. It is possible to embed<br />

subprograms in another subprogram such that only one compilation unit is constructed. These local subprograms<br />

can only be referenced from the surrounding subprogram. This is identical to the features provided by Pascal.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (6 of 9) [10/14/2000 12:55:17 PM]


with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

with <strong>Ada</strong>.Integer_Text_IO; use <strong>Ada</strong>.Integer_Text_IO;<br />

procedure ive_got_a_procedure is<br />

x :integer:=6;<br />

y :integer:=5;<br />

procedure display_values(number:integer) is<br />

begin<br />

put(number);<br />

new_line;<br />

end display_values;<br />

begin<br />

display_values(x);<br />

display_values(y);<br />

end ive_got_a_procedure;<br />

In this example the scope of procedure display_values is limited to inside the procedure ive_got_a_procedure - it<br />

can't be 'seen' or called from anywhere else.<br />

Separate compilation<br />

In the previous example if any change is made to any of the code then both procedures must be resubmitted to the<br />

compiler (because they are in the one source file). We can separate the two components into seperate files while<br />

still retaining the limited scope of procedure display_values. This is a little like the #include directive in C, but the<br />

files are now independent compilation units.<br />

In the first file...<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

with <strong>Ada</strong>.Integer_Text_IO; use <strong>Ada</strong>.Integer_Text_IO;<br />

procedure Ive_got_a_procedure is<br />

x :integer:=6;<br />

y :integer:=5;<br />

procedure display_values(number:integer) is separate;<br />

begin<br />

display_values(x);<br />

display_values(y);<br />

end ive_got_a_procedure;<br />

In the second file...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (7 of 9) [10/14/2000 12:55:17 PM]


separate(Ive_got_a_procedure) -- note no trailing semicolon<br />

procedure display_values(number:integer) is<br />

begin<br />

put(number);<br />

new_line;<br />

end display_values;<br />

Apart from being in another file (and being a seperate compilation unit) the code is identical in all respects to the<br />

previous version. However if the inner subprograms change then only they have to be submitted to the compiler.<br />

This also allows a program to be broken up into several pieces, which can ease the job of understanding it.<br />

Overloading<br />

Finding new names for functions that do the same thing to variables of different types is always a problem. The<br />

procedure insert is a good example. To give programmers a bit more breathing space, <strong>Ada</strong> allows sub programs to<br />

have the same name, it only insists that they are distinguishable. This is called overloading.<br />

Subprogram overloading<br />

Two sub programs with the same name are distinguishable if their profile is different. The profile consists of the<br />

number of parameters, thier type and, if it is a function, the return type.<br />

So long as the compiler can tell which sub program you requested by matching the profile of the call to the<br />

specifications of the sub programs you have supplied, it's happy; otherwise you'll get an ambiguous reference<br />

error.<br />

procedure Insert(Item : Integer); -- Two procedures with the<br />

procedure Insert(Item : Float); -- same name, but different.<br />

-- profiles.<br />

The procedures Put and Get in the package <strong>Ada</strong>.Text_IO are examples of overloaded subprograms.<br />

Operator overloading<br />

In languages such as Pascal the + operator is overloaded. Sometimes it is used to add integers, sometimes reals,<br />

sometimes strings. It is quite obvious that this one operator is used to represent very different code.<br />

<strong>Ada</strong> allows programmers to overload operators with thier own code. One restriction on this overloading of the<br />

operator name, is as expected, that the functions are distinguishable from the originals supplied, i.e. its profile is<br />

unique. Even this problem can be overcome by specifying the package name (see the next section).<br />

Overloaded operators cannot be seperate compilation units. - they must be contained in another unit such as a<br />

procedure, function or package.<br />

Consider an example where we wish to provide facilities to add two vectors together.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (8 of 9) [10/14/2000 12:55:17 PM]


procedure add_demo is<br />

type Vector is array (positive range ) of Integer;<br />

a : Vector(1..5);<br />

b : Vector(1..5);<br />

c : Vector(1..5);<br />

function "+"(left,right:vector) return vector is<br />

begin<br />

result : Vector(left'first..left'last);<br />

offset : constant Natural := right'first-1;<br />

if left'length /= right'length then<br />

raise program_error; -- an exception,<br />

-- see later<br />

end if;<br />

for i in left'range loop<br />

result(i):=left(i) + right(i - offset);<br />

end loop;<br />

return result;<br />

end "+";<br />

begin<br />

a:=(1,2,3,4,5);<br />

b:=(1,2,3,4,5);<br />

c:= a + b;<br />

end add_demo;<br />

This example uses most features discussed in the last few chapters.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html (9 of 9) [10/14/2000 12:55:17 PM]


1.<br />

8 Subprograms<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

9.<br />

10.<br />

11.<br />

12.<br />

13.<br />

14.<br />

15.<br />

16.<br />

to the index...<br />

Overview<br />

Procedures<br />

Functions<br />

Subprograms in general<br />

Parameter modes<br />

In mode<br />

In out mode<br />

Out mode<br />

Named parameters<br />

Mixing positional and named parameters<br />

Default parameter values<br />

Local subprograms<br />

Separate compilation<br />

Overloading<br />

Subprogram overloading<br />

Operator overloading<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms_ToC.html [10/14/2000 12:55:17 PM]


9 Packages<br />

Overview<br />

Although its roots are in Pascal, <strong>Ada</strong> still borrowed heavily from other languages and was influenced by the<br />

latest in software engineering techniques discussed in the 1970's. Undoubtedly the major innovation from<br />

that period is the concept of packages. Separation of the specifications from the implementation of 'objects'<br />

results in far more flexible code. The containment of the data structures behind a wall of functional interfaces<br />

results in much more maintainable systems. This chapter discusses <strong>Ada</strong>'s implementation of packages.<br />

Packages are not about how a program will run, but about how it is constructed and how it is to be<br />

understood and maintained.<br />

Package specifications<br />

The package specification of an <strong>Ada</strong> package describes all the subprogram specifications, variables, types,<br />

constants etc that are visible to anyone who 'withs' the package into thier own code.<br />

The following is an example of a package specification.<br />

package odd_demo is<br />

end odd_demo;<br />

type string is array (positive range ) of character;<br />

pi :constant float:=3.14;<br />

x: integer;<br />

type a_record is<br />

record<br />

left:boolean;<br />

right:boolean;<br />

end record;<br />

-- note that the following two subprograms are<br />

-- specifications only,the body of the subprograms are<br />

-- in the body of the package<br />

procedure insert(item:in integer; success:out boolean);<br />

function present(item:in integer) return boolean;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (1 of 9) [10/14/2000 12:55:19 PM]


The items described in the package specification are often described as resources. We can access these values<br />

by 'with'ing the package in our code and then using the package name, a dot, and the name of the resource<br />

wanted, whether it is a type declaration, a function or a variable name.<br />

with odd_demo;<br />

procedure odder_demo is<br />

begin<br />

my_name :odd_demo.string;<br />

radius :float;<br />

success :boolean;<br />

radius := 3.0*odd_demo.pi;<br />

odd_demo.insert(4, success);<br />

if odd_demo.present(34) then ...<br />

end odder_demo;<br />

It should be noted that accessing resources in a package is textually identical to accessing the fields in a<br />

record.<br />

As accessing the resources of a package using the full dot notation can be cumbersome, the use clause may<br />

be employed. When a package is 'use'd in this way the resources are available as though they were declared<br />

directly in the code.<br />

with odd_demo; use odd_demo;<br />

procedure odder_demo is<br />

my_name : string(1..10);<br />

radius : float;<br />

success : boolean;<br />

begin<br />

radius:=3.0*pi;<br />

insert(4, success);<br />

if present(34) then ...<br />

end odder_demo;<br />

If two packages are with'ed and use'd in the one compilation unit (e.g. subprogram or another package) then<br />

there is the possibility of a name clash between the resources in the two packages. In this case the ambiguity<br />

can be removed by reverting to dot notation for the affected resources.<br />

--*****************************<br />

package no1 is<br />

a,b,c:integer;<br />

end no1;<br />

--*****************************<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (2 of 9) [10/14/2000 12:55:19 PM]


package no2 is<br />

c,d,e:integer;<br />

end no2;<br />

--*****************************<br />

with no1; use no1;<br />

with no2; use no2;<br />

procedure clash_demo is<br />

begin<br />

a:=1;<br />

b:=2;<br />

c:=3; -- ambiguous, are we referring to no1.c or<br />

-- no2.c?<br />

no1.c:=3; -- remove abiguity by reverting to dot<br />

-- notation<br />

no2.c:=3;<br />

end;<br />

Another problem encountered is when local resources 'hide' the resources in a use'd package. In this case the<br />

ambiguity can still be removed by using dot notation.<br />

package no1 is<br />

a:integer;<br />

end no1;<br />

with no1; use no1;<br />

procedure p is<br />

a:integer;<br />

begin<br />

a:=4; -- once again this is ambiguous<br />

p.a:= 4; -- remove abiguity by using procedure name in<br />

-- dot notation<br />

no1.a:=5; -- dot notation for package<br />

end p;<br />

Package body<br />

The package body is where all the implementation details for the services specified in the package<br />

specification are placed. As in the specification the package body can contain type declarations, object<br />

(variable) declarations, subprograms etc.<br />

The format of a package body is<br />

package body odd_demo is<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (3 of 9) [10/14/2000 12:55:19 PM]


type list is array (1..10) of integer;<br />

storage_list :list;<br />

upto :integer;<br />

procedure insert(item :in integer;<br />

success :out boolean) is<br />

begin<br />

....<br />

end insert;<br />

begin<br />

. . . .<br />

end present;<br />

function present(item:in integer) return boolean is<br />

begin -- initialisation statements for the whole package<br />

-- These are run _before_ the main program!<br />

for i in storage_list'range loop<br />

storage_list(i):=0;<br />

end loop;<br />

upto:=0;<br />

end odd_demo;<br />

The resources in the body of the package are unavailable for use by any other package. Any attempt to<br />

reference them will result in a compiler error.<br />

The variables declared in the package body retain their value between successive calls to the public sub<br />

procedures. As a result we can create packages that store information for later use.<br />

The begin/end at the end of the package contain initialisation statements for the package. These are executed<br />

before the main procedure is run. This is true for all packages. The order in which different package's<br />

initialisation statements are run is not defined.<br />

All the resources specified in the package specification are available in the package body without the use of a<br />

with clause.<br />

Private types<br />

So far all the types declared in the package specification have been completely visible to the user of the<br />

package.<br />

Sometimes when creating packages we want to maintain total control over the manipulation of objects. For<br />

instance in a package that controls accounts in general ledger, we only want to provide the facilities to make<br />

withdrawals, deposits and creation of accounts. No other package needs to know or should have access to the<br />

representation details of the account object.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (4 of 9) [10/14/2000 12:55:19 PM]


In <strong>Ada</strong> packages type declarations (and also constants) can be declared private.<br />

package accounts is<br />

type account is private; -- declaration comes later<br />

procedure withdraw(an_account:in out account;<br />

amount :in money);<br />

procedure deposit( an_account:in out account;<br />

amount :in money);<br />

function create( initial_balance:money) return account;<br />

function balance( an_account:in account) return integer;<br />

private -- this part of the package specification<br />

-- contains the full description.<br />

type account is<br />

record<br />

account_no :positive;<br />

balance :integer;<br />

end record;<br />

end accounts;<br />

Outside the package the only operations that can be performed on a private type are assignment, tests for<br />

equality and those operations defined by subprograms in the package specification.<br />

Full details of the representation details are available in the package body. Any routine in the package body<br />

can access and modify the private type as though it were not private - the privacy of an object applies only<br />

outside the package.<br />

It may seem a contradiction to put the private specifications in the package specifications - the public part of<br />

the package when you are trying to hide the representation details of an object. This is required for programs<br />

that allocate an object of that private type- the compiler then knows how much space to allocate.<br />

Although the reader of the package specifications can see what the representation of the private type really is,<br />

there is no way he or she can make explicit use of the knowledge.<br />

Objects can be created outside a package even if of a private type.<br />

E.g.<br />

with accounts; use accounts;<br />

procedure demo_accounts is<br />

home_account :account;<br />

mortgage :account;<br />

this_account :account;<br />

begin<br />

mortgage := accounts.create(initial_balance => 500.00);<br />

withdraw(home_account,50);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (5 of 9) [10/14/2000 12:55:19 PM]


end;<br />

. . .<br />

this_account:=mortgage; -- can assign private types.<br />

-- comparing private types.<br />

if this_account = home_account then<br />

. . .<br />

Limited private types<br />

A private type can be made even more private by removing the ability to compare and assign the values<br />

outside the package. You generally then have to provide a function to test equality (you can overload the<br />

equality operator =, this implicity overloads /= as well). Also you may have to create a procedure to perform<br />

assignments.<br />

Q Why do we need private types?<br />

A Consider the following:<br />

type text (maximum_length:positive:=20) is<br />

record<br />

length: index:=0;<br />

value :string(1. . maximum_length);<br />

end record;<br />

In this record the length field determines the number of characters in the field value that have meaning. Any<br />

characters from position length+1 to maximum_length are ignored by us when using the record. However if<br />

we ask the computer to compare two records of this type, it does not know the significance of the field<br />

length; it will compare the length field and all of the value field. Clearly in this situation we need to write a<br />

comparison function.<br />

<strong>Ada</strong>95 allows the programmer to override the equality operator for all types.<br />

Deferred constants<br />

In some package specifications we may wish to declare a constant of a private type. In the same manner as<br />

declaring a private type (and most forward references) we give an incomplete declaration of the constant - the<br />

compiler expects the rest to follow in the private section of the package specifications.<br />

package coords is<br />

type coord is private;<br />

home: constant coord; -- the deferred constant!<br />

private<br />

type coord is record<br />

x :integer;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (6 of 9) [10/14/2000 12:55:19 PM]


y :integer;<br />

end record;<br />

home:constant coord:=(0,0);<br />

end coords;<br />

Child units (<strong>Ada</strong>95)<br />

It was found in large system developments that a single package specification could grow extraordinarily<br />

large, with subsequent costs in recompilation of compilation units that depend on it if it were to change. To<br />

remedy this situation, the concept of child compilation units was conceived. Child units allow a logically<br />

single package to be broken into several physically distinct packages and subprograms. As well as solving the<br />

problem of large recompilations, they also provide a convenient tool for providing multiple implementations<br />

for an abstract type and for producing self contained subsystems, by using private child units.<br />

Extending an existing package<br />

A package that consists of a set of declarations may need to be extended at some time. For example a stack<br />

package may need the addition of a peek facility. If this package is heavily "withed" by many other units,<br />

then modifying the specification to include this extra features could result in a large recompilation despite the<br />

fact that most clients would not be using the new feature.<br />

A child package can be declared that logically extends the package, but in a physically separate manner.<br />

For example<br />

package stacks is<br />

type stack is private;<br />

procedure push(onto:in out stack; item:integer);<br />

procedure pop(from :in out stack; item: out integer);<br />

function full(item:stack) return boolean;<br />

function empty(item:stack) return boolean;<br />

private<br />

-- hidden implementation of stack<br />

...<br />

-- point A<br />

end stacks;<br />

package stacks.more_stuff is<br />

function peek(item:stack) return integer;<br />

end stacks.more_stuff;<br />

The package stacks.more_stuff is a child packge of stacks. It has the visibility of all the declarations<br />

preceeding point A, that is the parent package presents the same visibility to a child package as it does to its<br />

body. The child package can see all of its parents private parts. The package bodies would be compiled<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (7 of 9) [10/14/2000 12:55:19 PM]


separately.<br />

The clients who wish to use the function peek can simply have a with clause for the child package,<br />

with stacks.more_stuff;<br />

procedure demo is<br />

x :stacks.stack;<br />

begin<br />

stacks.push(x,5);<br />

if stacks.more_stuff.peek = 5 then<br />

....<br />

end;<br />

'With'ing the child package automatically causes the parent package to be 'with'ed, and its parent package to<br />

be 'with'ed, and so on. However the use clause does not act in this way. Visibiliy can only be gained on a<br />

package by package basis. This is no doubt a pragmatic decision based on what most organisations would<br />

prefer (as indicated by existing use of the 'use' clause).<br />

with stacks.more_stuff; use stacks; use more_stuff;<br />

procedure demo is<br />

x :stack;<br />

begin<br />

push(x,5);<br />

if peek(x) = 5 then<br />

....<br />

end;<br />

A package can have child functions and procedures. Rules for use of these are easily inferred.<br />

Private child units<br />

Private child units allow a child unit to be created that is only visible within the hierachy of the parent<br />

package. In this way facilities for a subsystem can be encapsulated within its a hidden package, with the<br />

compilation and visibility benefits that this entails.<br />

Because they are private, a child package's specification is allowed to advertise, in its specification, private<br />

parts of its parents. This would not normally be allowed as it would allow the breaking of the hidden<br />

implementation of a parent's private parts.<br />

private package stacks.statistics is<br />

procedure increment_push_count;<br />

end;<br />

The procedure stack.statistics.increment_push_count could be called from within the implementation of the<br />

stacks package; this procedure is not available to any clients external to this package hierachy.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (8 of 9) [10/14/2000 12:55:19 PM]


to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages.html (9 of 9) [10/14/2000 12:55:19 PM]


1.<br />

9 Packages<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

9.<br />

to the index...<br />

Overview<br />

Package specifications<br />

Package body<br />

Private types<br />

Limited private types<br />

Deferred constants<br />

Child units (<strong>Ada</strong>95)<br />

Extending an existing package<br />

Private child units<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/9_packages_ToC.html [10/14/2000 12:55:20 PM]


10 Generics<br />

Overview<br />

Code reuse has been one of the great programming hopes for many years. Although suitable in the area of<br />

mathematical routines (where the functions are well defined and stable) trying to develop libraries in other areas<br />

has met with very limited success, due to the inevitable intertwining of process and data types in procedures. <strong>Ada</strong><br />

has attempted to free us from this problem by producing code that does not rely as much on the specific data types<br />

used, but on their more general algorithmic properties.<br />

As described by Naiditch, generics are like form letters; mostly they are complete letters with a few blanks<br />

requiring substitution (eg name and address information). Form letters aren't sent out until this information is<br />

filled in. Similarly generics cannot be used directly, we create a new subprogram or a package by 'instantiating' a<br />

generic and then using the instantiated compilation unit. When a generic is instantiated we have to supply<br />

information to fill in the blanks, such as type information, values or even subprograms.<br />

Generics<br />

In <strong>Ada</strong> a program unit (either a subprogram or a package) can be a generic unit.<br />

This generic unit is used to create instances of the code that work with actual data types. The data type required is<br />

passed in as a parameter when the generic unit is instantiated. Generics are usually presented in two parts, the<br />

generic specification and then the generic package.<br />

Once they are compiled the generics are stored in the <strong>Ada</strong> library and can be with'ed (but never use'd) by other<br />

compilation units. These other units, whether they be subprograms, packages or generics, can be with'ed and<br />

instantiated (the process of creating a usable subprogram or package by supplying generic parameters). The<br />

instatiated subprogram or package can be stored in the <strong>Ada</strong> library for later use.<br />

The following is the instantiation of the generic package integer_io. Int_io is now available to be with'ed (and<br />

use'd) by any program.<br />

with text_io; use text_io;<br />

package int_io is new integer_io(integer);<br />

The package can be instantiated with other types as well.<br />

with text_io; use text_io;<br />

with accounts; use accounts;<br />

package account_no_io is new integer_io(account_no);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/10_generics.html (1 of 6) [10/14/2000 12:55:22 PM]


Generic subprograms<br />

The following is a compilation unit. Once it is compiled it is available to be 'with'ed (but not 'use'd) from the <strong>Ada</strong><br />

library. It includes the keyword generic, a list of generic parameters and the procedure specification.<br />

generic<br />

type element is private; -- caution private here means<br />

-- element is a parameter to<br />

-- the generic sub program<br />

procedure exchange(a,b :in out element);<br />

The body of the generic procedure is presented as a seperate compilation unit. Note that it is identical to a<br />

non-generic version.<br />

procedure exchange(a,b :in out element) is<br />

temp :element;<br />

begin<br />

a:=temp;<br />

a:=b;<br />

b:=temp;<br />

end exchange;<br />

The code above is simply a template for an actual procedure that can be created. It can't be called. It is equivalent<br />

to a type statement - it doesn't allocate any space, it just defines a template for the shape of things to come. To<br />

actually create a procedure:<br />

procedure swap is new exchange(integer);<br />

We now have a procedure swap that swaps integers. Here "integer" is called a generic actual parameter.<br />

"Element" is called a generic formal parameter.<br />

procedure swap is new exchange(character);<br />

procedure swap is new exchange(element => account); -- named association<br />

You can create as many of these as you like. In this case the procedure name is overloaded and as normal the<br />

compiler can tell which one you call by the parameter type.<br />

It can be called (and behaves) just as though it had been defined as<br />

procedure swap(a,b :in out integer) is<br />

temp:integer.<br />

begin<br />

. . .<br />

end;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/10_generics.html (2 of 6) [10/14/2000 12:55:22 PM]


Generic packages<br />

Packages can be generics as well.<br />

The following generic package specification is fairly standard:<br />

generic<br />

type element is private; -- note that this is a<br />

-- parameter to the generic<br />

package stack is<br />

procedure push(e: in element);<br />

procedure pop(e: out element);<br />

function empty return boolean;<br />

end stack;<br />

The accompanying package body would be<br />

package body stack is<br />

the_stack :array(1..200) of element;<br />

top :integer range 0..200:=0;<br />

procedure push(e:in element) is<br />

....<br />

procedure pop(e:out element) is<br />

...<br />

function empty return boolean is<br />

...<br />

end stack;<br />

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

How would you create a generic unit and test it?<br />

- Create it using a specific type and translate it to use generic parameters.<br />

Generic parameters<br />

There are three types of parameters to generics<br />

Type parameters<br />

Value and object parameters<br />

Subprogram parameters<br />

So far we have only seen type parameters.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/10_generics.html (3 of 6) [10/14/2000 12:55:22 PM]


Type parameters<br />

Despite the alluring appeal of generics we are still restricted by the very nature of the tasks we are attempting to<br />

accomplish. In some generics we may wish to provide a facility to sum an array of numbers. Quite clearly this is<br />

only appropriate for numbers, we can't add records together. To provide protection from someone instantitating a<br />

generic with an inappropriate type, we can specify the category of types that can be used when instantiating.<br />

The compiler can also check that we don't perform any inappropriate actions on a variable inside the code e.g. it<br />

wont allow us to find the 'pred of a record.<br />

A list of the various type restrictions is given below.<br />

type T is private -- very few restrictions<br />

type T is limited private -- fewer restrictions,<br />

type T is () -- T has to be a discrete type<br />

type T is range -- T must be an integer type<br />

type T is digits -- T must be a floating point type<br />

type T is delta -- T must be a fixed point - not<br />

-- discussed<br />

type T is array(index_type) of element_type<br />

-- The component type of the actual array must match the<br />

-- formal array type. If it is other than scalar then<br />

-- they must both be either constrained or<br />

-- unconstrained.<br />

type T is access X -- T can point to a type X (X can be<br />

-- a previously defined generic parameter.<br />

To understand the rule governing instantiation of array type parameters consider the following generic package<br />

generic<br />

type item is private;<br />

type index is ();<br />

type vector is array (index range ) of item;<br />

type table is array (index) of item;<br />

package P is . . .<br />

and the types:<br />

type color is (red,green.blue);<br />

type Mix is array (color range ) of boolean;<br />

type Option is array (color) of boolean;<br />

then Mix can match vector and Option can match table.<br />

package R is new P( item => boolean,<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/10_generics.html (4 of 6) [10/14/2000 12:55:22 PM]


Value parameters<br />

index => color,<br />

vector => mix,<br />

table => option);<br />

Value parameters allow you to specify a value for a variable inside the generic:<br />

generic<br />

type element is private;<br />

size: positive := 200;<br />

package stack is<br />

end stack;<br />

procedure push...<br />

procedure pop...<br />

function empty return boolean;<br />

package body stack is<br />

size:integer;<br />

theStack :array(1..size) of element;<br />

. . .<br />

You would instantiate the package thus:<br />

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

or<br />

or<br />

package fred is new stack(integer,1000);<br />

package fred is new stack(integer);<br />

Note that if the value parameter does not have a default value then one has to be provided when the generic is<br />

instantiated.<br />

Value parameters such as string can also be included.<br />

generic<br />

package ....<br />

type element is private;<br />

file_name :string;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/10_generics.html (5 of 6) [10/14/2000 12:55:22 PM]


Note that the file_name parameter type (string) is not constrained. This is identical to string parameters to<br />

subprograms.<br />

Subprogram parameters<br />

We can pass a subprogram as a parameter to a generic.<br />

Why? If you have a limited private type you can pass tests for equality and assignment in as subprogram<br />

parameters.<br />

E.g.<br />

generic<br />

type element is limited private;<br />

with function "="(e1,e2:element) return boolean;<br />

with procedure assign(e1,e2:element);<br />

package stuff is . . .<br />

To instantiate the generic<br />

package things is new stuff(person,text."=",text.assign);<br />

Other forms allow for a default subprogram if none is given.<br />

with procedure assign(e1,e2:element) is myAssign(e1,e2:person);<br />

Or you can specify the computer makes a default selection of procedure based on the normal subprogram<br />

selection rules:<br />

with function "="(e1,e2:element ) return boolean is ;<br />

If no function is supplied for the "=" function then the default equal function will be used according to the type of<br />

element ( i.e. if element is integer the normal integer "=" will be used).<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/10_generics.html (6 of 6) [10/14/2000 12:55:22 PM]


1.<br />

10 Generics<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

Overview<br />

Generics<br />

Generic subprograms<br />

Generic packages<br />

Generic parameters<br />

Type parameters<br />

Value parameters<br />

Subprogram parameters<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/10_generics_ToC.html [10/14/2000 12:55:23 PM]


11 Exceptions<br />

Overview<br />

Exceptions are described in the <strong>Ada</strong> Language Reference Manual as errors or other exceptional conditions that<br />

arise during normal program execution. Exception handling is the process of catching these errors at run time<br />

and executing appropriate code to resolve the error, either by correcting the cause of the problem or by taking<br />

some remedial action.<br />

Predefined exceptions<br />

There are five exceptions predefined in the language. They are described below.<br />

The exception CONSTRAINT_ERROR is raised whenever an attempt is made to violate a range constraint.<br />

procedure constraint_demo is<br />

x :integer range 1..20;<br />

y :integer;<br />

begin<br />

put("enter a number "); get(y);<br />

x:=y;<br />

put("thank you");<br />

end constraint_demo;<br />

If the user enters a number outside the range 1..20, then x's range constraint will be violated, and a<br />

constraint_exception will occur. The exception is said to have been 'raised'. Because we have not included any<br />

code to handle this exception, the program will abort, and the <strong>Ada</strong> run time environment will report the error<br />

back to the user. The line 'put("thank you");' will not be executed either - once an exception occurs the<br />

remainder of the currently executing block is abandoned.<br />

This error also occurs when an array index constraint is violated.<br />

procedure constraint_demo2 is<br />

x :array (1..5) of integer:=(1,2,3,4,5);<br />

y :integer :=6;<br />

begin<br />

x(y):= 37;<br />

end constraint_demo2;<br />

In this example the constraint exception will be raised when we try to access a non existant index in the array.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (1 of 8) [10/14/2000 12:55:24 PM]


The exception NUMERIC_ERROR is raised when a numeric operation cannot deliver a correct result (e.g. for<br />

arithmetic overflow, division by zero, inability to deliver the required accuracy for a float operation).<br />

NUMERIC_ERROR has been redefined in <strong>Ada</strong>95 to be the same as CONSTRAINT_ERROR<br />

procedure numeric_demo is<br />

x :integer;<br />

y :integer;<br />

begin<br />

x:=integer'last;<br />

y:=x+x; -- causes a numeric error<br />

end numeric_demo;<br />

The exception PROGRAM_ERROR is raised whenever the end of a function is reached (this means that no<br />

return statement was encountered). As well it can be raised when an elaboration check fails.<br />

procedure program_demo is<br />

z :integer;<br />

function y(x :integer) return integer is<br />

begin<br />

if x < 10 then<br />

return x;<br />

elsif x < 20 then<br />

return x<br />

end if;<br />

end y; -- if we get here, no return has been<br />

-- encountered<br />

begin<br />

z:= y(30);<br />

end program_demo;<br />

The exception STORAGE_ERROR is raised whenever space is exhausted, whether during a call to create a<br />

dynamic object or when calling a subprocedure (and stack space is exhausted).<br />

The exception TASKING_ERROR is raised when exceptions arise during intertask communication; an<br />

example is task that attempts to rendezvous with a task that has aborted.<br />

Handling exceptions<br />

So far the code seen has not processed the exceptions. In these cases the programs are aborted by the run time<br />

code. To make exceptions useful we have to be able to write code that is run whenever an exception occurs.<br />

The exception handler is placed at the end of a block statement, the body of a subprogram, package, task unit<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (2 of 8) [10/14/2000 12:55:24 PM]


or generic unit.<br />

To handle a constraint error consider the following code.<br />

declare<br />

x:integer range 1..20;<br />

begin<br />

put("please enter a number ");<br />

get(x);<br />

put("thank you");<br />

exception<br />

when constraint_error =><br />

put("that number should be between 1 and 20");<br />

when others =><br />

put("some other error occurred");<br />

end;<br />

If the user enters a number between 1 and 20 then no error occurs and the message "thank you " appears.<br />

Otherwise the message "that number ..." appears and the block terminates.<br />

If we want the user to continue to enter in numbers until there is no constraint error then we can write the<br />

following:<br />

loop<br />

declare<br />

...<br />

begin<br />

...<br />

get(x);<br />

exit;<br />

exception<br />

when constraint_error =><br />

put("that number ...<br />

end;<br />

end loop;<br />

This highlights the point the instruction executed after an exception is that following the block in which the<br />

exception was handled.<br />

Exceptions can be raised by the programmer simply by using the raise statement.<br />

raise numeric_error;<br />

The exception raised is indistinguishable from a genuine numeric_error.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (3 of 8) [10/14/2000 12:55:24 PM]


Exception propagation<br />

If an exception is not handled in the subprocedure in which it was raised, the exception is propagated to the<br />

subprocedure that called it. A handler for the exception is searced for in the calling subprocedure. If no handler<br />

is found there then the exception is propagated again. This continues until either an exception handler is found,<br />

or the highest level of the current task is reached, in which case the task is aborted. If there is only one task<br />

running (typical for student projects) then the <strong>Ada</strong> runtime environment handles the exception and the program<br />

is aborted.<br />

procedure exception_demo is<br />

--------------------------------procedure<br />

level_2 is<br />

-- no excpetion handler here<br />

begin<br />

raise constraint_error;<br />

end level_2;<br />

--------------------------------procedure<br />

level_1 is<br />

begin<br />

level_2;<br />

exception<br />

when constraint_error =><br />

put("exception caught in level_1");<br />

end level_1;<br />

begin<br />

level_1;<br />

exception<br />

when constraint_error =><br />

put("exception caught in exception_demo");<br />

end exception_demo;<br />

If this program was run the only output would be "exception caught in level_1". The exception is handled here<br />

and does not propagate any further. If we want to we can place a raise statement in the exception handler - this<br />

has the effect of propagating the exception up to the calling subprogram. In this way an exception can be<br />

viewed by each subprocedure in the call hierachy, with each performing whatever action it deems necessary.<br />

....<br />

exception<br />

when constraint_error =><br />

package_disabled:=true;<br />

raise; -- re raise the current exception,<br />

-- allow other procedures to have a go<br />

-- at processing the exception.<br />

end;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (4 of 8) [10/14/2000 12:55:24 PM]


The raise statement is very useful when used in the others section of an exception handler. In this case the<br />

appropriate exception is still raised and propagated.<br />

User defined exceptions<br />

<strong>Ada</strong> gives the user the ability to define their own exceptions. These are placed in the declarative part of the<br />

code. They can be placed whereever a normal declaration is placed (e.g. even in package specifications). The<br />

format for the declaration of an exception is<br />

my_very_own_exception :exception;<br />

another_exception :exception;<br />

The use of exceptions is the subject of much debate about whether it is a lazy way of programming, without<br />

thinking too much about the problem and likely error conditions, or whether it is a valid form of control<br />

structure that can be used to some effect.<br />

Problems with scope<br />

Handling user exceptions is identical to that of the predefined exceptions except for the problem of scoping.<br />

Consider the following example.<br />

with text_io; use text_io;<br />

procedure demo is<br />

procedure problem_in_scope is<br />

cant_be_seen :exception;<br />

begin<br />

raise cant__be_seen;<br />

end problem_in_scope;<br />

begin<br />

problem_in_scope;<br />

exception<br />

when cant_be_seen =><br />

put("just handled an_exception");<br />

end demo;<br />

This example is illegal. The problem is that the scope of an_exception is limited to procedure exception_raiser.<br />

It's name is not defined outside of this procedure and thus it cannot be explicity handled in procedure demo.<br />

The solution is to use the others clause in the outer procedure's exception handler.<br />

with text_io; use text_io;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (5 of 8) [10/14/2000 12:55:24 PM]


procedure demo is<br />

procedure problem_in_scope is<br />

cant_be_seen :exception;<br />

begin<br />

raise cant_be_seen;<br />

end problem_in_scope;<br />

begin<br />

problem_in_scope;<br />

exception<br />

when others =><br />

put("just handled some exception");<br />

end demo;<br />

Another problem arises when one procedure's exception hides another exception due to scoping rules.<br />

with text_io; use text_io;<br />

procedure demo is<br />

fred :exception;<br />

-----------------------------------procedure<br />

p1 is<br />

begin<br />

raise fred;<br />

end p1;<br />

-----------------------------------procedure<br />

p2 is<br />

fred :exception; -- a local exception<br />

begin<br />

p1;<br />

exception<br />

when fred =><br />

put("wow, a fred exception");<br />

end p2;<br />

-----------------------------------begin<br />

p2;<br />

exception<br />

when fred =><br />

put("just handled a fred exception");<br />

end demo;<br />

The output of this procedure is "just handled a fred exception". The exception handled in p2 is simply a local<br />

exception. This is similar to the handling of scope with normal variables.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (6 of 8) [10/14/2000 12:55:24 PM]


Procedure p2 could be rewritten as<br />

-----------------------------------procedure<br />

p2 is<br />

fred :exception;<br />

begin<br />

p1;<br />

exception<br />

when fred =><br />

-- the local exception<br />

put("wow, an_exception");<br />

end p2;<br />

when demo.fred =><br />

-- the more 'global' exception<br />

put("handeled demo.fred exception");<br />

Suppression of exception checks<br />

Exceptions are generated because extra code that is inserted inline detects some erroneous condition, or<br />

through some hardware checking mechanisms such as software interrupts or traps.<br />

Accordingly it is possible to suppress the insertion of these checks into the code. The method that <strong>Ada</strong> uses is<br />

the pragma SUPPRESS. However it should be noted that through use of program analysis by the compiler, a<br />

good number of checks can automatically be removed at compile time.<br />

The pragma can be placed in the code where the suppression is required. The suppression extends to the end of<br />

the current block (using normal scope rules).<br />

It has a large range of options to enable suppression of various checks on either a type basis, an object basis or<br />

a functional basis. The versatility of the pragma is dependent upon the implementation. As well various<br />

implementations are free to implement (or ignore) any pragma suppress feature.<br />

The exception CONSTRAINT_ERROR can be raised by failing several suppressable checks<br />

pragma suppress (access_check);<br />

pragma suppress (discriminant_check);<br />

pragma suppress (index_check);<br />

pragma suppress (length_check);<br />

pragma suppress (range_check);<br />

pragma suppress (division_check);<br />

pragma suppress (overflow_check);<br />

The exception PROGRAM_ERROR has only one suppressable check<br />

pragma suppress (elaboration_check);<br />

The exception STORAGE_ERROR has only one suppressable check<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (7 of 8) [10/14/2000 12:55:24 PM]


pragma suppress (storage_check);<br />

Applying suppression of checks<br />

We can suppress the checking of exceptions on individual objects.<br />

pragma suppress (index_check, on => table);<br />

It can also relate to a single type.<br />

type employee_id is new integer;<br />

pragma suppress (range_check, employee_id);<br />

The use of a pragma would be as follows. In this case the scope of the pragma is till the end of the block.<br />

declare<br />

pragma suppress(range_check);<br />

subtype small_integer is integer range 1..10;<br />

a :small_integer;<br />

x :integer:=50;<br />

begin<br />

a:=x;<br />

end;<br />

This code would cause no constraint error to be generated.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions.html (8 of 8) [10/14/2000 12:55:24 PM]


1.<br />

11 Exceptions<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

Overview<br />

Predefined exceptions<br />

Handling exceptions<br />

Exception propagation<br />

User defined exceptions<br />

Problems with scope<br />

Suppression of exception checks<br />

Applying suppression of checks<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/11_exceptions_ToC.html [10/14/2000 12:55:25 PM]


12 Files<br />

Overview<br />

Conceptually similar to most extended Pascals file i/o, <strong>Ada</strong> provides a simple yet effective collection of file packages. These can be<br />

discarded and replaced by any other file package, with of course a subsequent decrease in portability. Most of the facilities provided<br />

would not be used in high performance applications directly, but may form the basis of such a file system.<br />

The I/O packages<br />

So far all the I/O performed has been on directed to the standard input/output. All of the facilities provided have come from the<br />

TEXT_IO package (even the generic packages integer_io and float_io). This package provides facilities for file manipulation of<br />

textual files (files of characters) and provides facilities such as close, delete, reset,open, create etc.<br />

Two other standard I/O packages are for storing files that consist of the one type of fixed length object, such as records, arrays,<br />

floating point numbers etc. These are the generic packages sequential_io and direct_io.<br />

The Text_IO package<br />

The text i/o package's main data type is the FILE_TYPE. This is the internal representation of the file. When a file is opened or<br />

created an association is made between the name and the FILE_TYPE. The object of type FILE_TYPE is used from then on to<br />

reference the file.<br />

The procedure create creates a file and writes some data to it.<br />

with text_io; use text_io;<br />

with int_io; use int_io;<br />

procedure demo_file_io is<br />

my_file :text_io.file_type;<br />

begin<br />

create( file => my_file,<br />

mode => out_file,<br />

name => "data.dat");<br />

put( file => my_file,<br />

item => "numbers and their squares");<br />

for i in 1..10 loop<br />

put(my_file,i);<br />

put(my_file," ");<br />

put(my_file,i*i);<br />

new_line(my_file);<br />

end loop;<br />

close(my_file); -- required, <strong>Ada</strong> may not close your<br />

-- open files for you<br />

end demo_file_io;<br />

The following program reads data from one file and writes it to another, a character at a time. It should be noted that the concept of<br />

'end of line' is different to that provided by Unix and Dos. In those systems there is simply a character that marks the end of the line<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/12_files.html (1 of 5) [10/14/2000 12:55:27 PM]


which is processed as a normal character; in <strong>Ada</strong> and Pascal there is the concept of a line terminator that is not a character in the file.<br />

To read pass this terminator you need to perform a skip_line. Similarly to partition the output file into lines, the command new_line<br />

has to be given.<br />

-- program to read a file a character at a time, and to write<br />

-- it out a character at a time<br />

with text_io; use text_io;<br />

procedure read_write is<br />

input_file :file_type;<br />

output_file :file_type;<br />

char :character;<br />

begin<br />

open(input_file,in_file,"input.dat");<br />

create(output_file,out_file,"output.dat");<br />

while not end_of_file(input_file) loop<br />

while not end_of_line(input_file) loop<br />

get(input_file,char);<br />

put(output_file,char);<br />

end loop;<br />

skip_line(input_file);<br />

new_line(output_file);<br />

end loop;<br />

close(input_file);<br />

close(output_file);<br />

end read_write;<br />

There are various procedures to perform file manipulation. These are<br />

Create - Creates a file with the given name and mode. Note that if the file<br />

- has a null string, then the file is temporary and is deleted later.<br />

Open - Opens an existing file with the given name and mode.<br />

Delete - Deletes the appropriate file. It is an error to delete an open file.<br />

Reset - Returns the read (or write) position to the start of the file.<br />

As well there are functions that report on the status of the file system.<br />

End_of_File - Returns true if we are at the end of the current file.<br />

End_of_Line - Returns true if we are at the end of the current text line.<br />

Is_open - Returns true if the given file is open.<br />

Mode - Returns the mode of the given file.<br />

Name - Returns the name (string) of the current file.<br />

There are various other routines, it is best to examine the specifications of the package text_io (appendix B) to get a clear idea of the<br />

package.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/12_files.html (2 of 5) [10/14/2000 12:55:27 PM]


Use of text files<br />

<strong>Ada</strong>'s text_io facilities rely on the exception mechanism to report errors in the creation and opening of files. For example attempting<br />

to create a file that already exists causes an exception, as does attempting to open a file that does not.<br />

To get around this circularity the following procedure robust_open could be used. This attempts to open the file, if it fails due to the<br />

file not being there, it attempts to create it instead.<br />

The code is, of course, subject to race conditions. This program could be interrupted after an attempt open the file, and before the<br />

create is attempted; a second process could conceivably create the file in this time.<br />

------------------------------------------------------------<br />

-- Attempts to open the specified file.<br />

-- If it doesn't work then it creates it instead<br />

with text_io; use text_io;<br />

procedure robust_open( the_file :in out file_type;<br />

mode :in file_mode;<br />

name :in string) is<br />

begin<br />

open(the_file,mode,name);<br />

exception<br />

when name_error =><br />

create(the_file,mode,name);<br />

end robust_open;<br />

Another utility, the boolean function file_exists, allows students to check if the file exists. An exception (use_error) is raised if the is<br />

already open.<br />

------------------------------------------------------------<br />

-- Returns true if the file specified in 'name' exists.<br />

-- This works by attempting to open the file. If this<br />

-- suceeds then the file is there.<br />

with text_io; use text_io;<br />

function file_exists(name :string) return boolean is<br />

the_file:text_io.file_type;<br />

begin<br />

open(the_file, in_file, name);<br />

-- yes it worked, close the file and return true<br />

close( the_file);<br />

return true;<br />

exception<br />

when name_error =><br />

-- couldn't open the file, assume it's not there.<br />

return false;<br />

end file_exists;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/12_files.html (3 of 5) [10/14/2000 12:55:27 PM]


<strong>Ada</strong>95 Text_io enhancements<br />

The mode append_file has been added to the allowable modes for text files. As well the concept of a standard error file (c.f. Unix/C)<br />

has been added. The procedures flush have been added to enable flushing of text file buffers.<br />

Improvements in character handling include look ahead, and get_immediate, with various options.<br />

Generic packages for the I/O of the new modular and decimal types have been included - their specifications conform to that of the<br />

other numeric generic I/O packages.<br />

The sequential_io package<br />

Most large systems do not utilise text as the basis for their files. The files are usually composed of composite objects, and typically<br />

they are records. The sequential_io generic package allows us to create files whose components are any type (they must however be<br />

constrained).<br />

The basics of the sequential_io generic package are identical to the text_io package, except that the procedures get and put are now<br />

read and write, and the procedures deal in terms of the type the package was instantiated with. Similarly the concepts of line has<br />

dissapeared, so that the function end_of_line and the procedures skip_line and new_line have also gone.<br />

Use of the package is demonstrated below.<br />

with sequential_io; -- generic package<br />

with personnel_details; use person_details;<br />

-- has record type 'personnel'<br />

with produce_retirement_letter;<br />

procedure sequential_demo is<br />

begin<br />

package person_io is new sequential_io(personnel);<br />

data_file :person_io.file_type;<br />

a_person :personnel;<br />

person_io.open(data_file,in_file,"person.dat");<br />

while not person_io.end_of_file(data_file) loop<br />

person_io.read(data_file,a_person);<br />

end loop;<br />

if a_person.age > 100 then<br />

produce_retirement_letter(a_person);<br />

end if;<br />

close(data_file);<br />

end sequential_demo;<br />

No direct access of the file is possible. The file is opened at the start and processed until you get to the end, you reset or close the file.<br />

<strong>Ada</strong>95 has added the file mode append_file to the specifications of sequential_io.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/12_files.html (4 of 5) [10/14/2000 12:55:27 PM]


The direct_io package<br />

The direct io package builds on top of the sequential io package by providing the ability to seek directly to a given record, to<br />

determine the size of the file, to determine the current index and to be able to open the file with a new mode - inout_file (read/write).<br />

These facilities should make it possible, in conjuction with a suitable indexing package, to provide very high level file processing<br />

packages.<br />

The following code demonstrates the use of direct files. For brevity it assumes that the employee records are stored on the basis of<br />

their employee numbers.<br />

with direct_io; -- generic package<br />

with personnel_details; use personnel_details;<br />

-- has record type 'personnel'<br />

-- has procedure display_personnel, etc<br />

with int_io; use int_io;<br />

with display_menu;<br />

procedure direct_demo is<br />

begin<br />

package person_io is new direct_io(personnel);<br />

data_file :person_io.file_type;<br />

a_person :personnel;<br />

option :integer;<br />

employee_no :integer;<br />

person_io.open(data_file,inout_file,"person.dat");<br />

loop<br />

display_menu;<br />

get_option(option);<br />

case option is<br />

when 1 =><br />

get(employee_no);<br />

set_index(positive_count(employee_no));<br />

read(data_file,a_person);<br />

display_person(a_person);<br />

when 2 =><br />

get(employee_no);<br />

set_index(positive_count(employee_no));<br />

read(data_file,a_person);<br />

get_new_details(a_person);<br />

write(data_file,a_person);<br />

when 3 =><br />

exit;<br />

when others =><br />

put("not a great option!");<br />

end case;<br />

end loop;<br />

close(data__file);<br />

end direct_demo;<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/12_files.html (5 of 5) [10/14/2000 12:55:27 PM]


1.<br />

12 Files<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

Overview<br />

The I/O packages<br />

The Text_IO package<br />

Use of text files<br />

<strong>Ada</strong>95 Text_io enhancements<br />

The sequential_io package<br />

The direct_io package<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/12_files_ToC.html [10/14/2000 12:55:28 PM]


13 Access types<br />

Overview<br />

Access types (and the objects they access) are used to create dynamic data structures. It should be noted that access types<br />

are not necessarily implemented as pointers are in other languages (e.g. the address of the object in question), although in<br />

many cases they are. They can be used to create dynamic data structures such as linked lists, trees, graphs etc.<br />

Access types<br />

Access types allow for the dynamic referencing and allocation of objects in <strong>Ada</strong>.<br />

To specify an access type consider the following:<br />

type person is<br />

record<br />

name :string(1..4);<br />

age :0.. 150;<br />

end record;<br />

fred :person<br />

type person_ptr is access person;<br />

someone :person_ptr;<br />

To dynamically create an object that someone will point at:<br />

someone:= new person;<br />

An object referred to by someone can be referenced in exactly the same way as an explicity declared object.<br />

E.g.<br />

fred.name := "fred";<br />

someone.name:= "jim ";<br />

A special value called null is used to indicate that the access object does not reference any valid objects. An access object<br />

is always initialised to null.<br />

Given the following declarations<br />

declare<br />

begin<br />

x :person_ptr := new person;<br />

y :person_ptr := new person;<br />

x := ("fred",27);<br />

y := ("anna",20);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/13_access_types.html (1 of 4) [10/14/2000 12:55:29 PM]


end;<br />

x := y; -- *<br />

y.all := ("sue ",34);<br />

put(x.name);<br />

This will output the string "sue ". When the line highlighted with a star is executed, the value of the access object y is<br />

assigned to x, not the object that y was accesses. Both x and y both refer to the same object, so a change to the object that y<br />

refers to implicity changes the object x refers to.<br />

If we wish to change a field of the object refered to by x<br />

x.name := y.name;<br />

Access objects x and y still point to distinct objects. Both access objects are implicitly dereferenced in this example.<br />

Because of the implicit derefence, we need a special syntax to denote the entire object the access object accesses. <strong>Ada</strong> has<br />

the reserved word all for this purpose.<br />

x.all := y.all; -- all field in object referred to by y now<br />

-- in the object referred to by y. They are<br />

-- still distinct objects.<br />

A comparison of Pascal, C and <strong>Ada</strong> pointer/access syntax.<br />

Pascal C <strong>Ada</strong><br />

Access a^.fieldname *a.fieldname a.fieldname<br />

a->fieldname<br />

Copying pointer b := a; b = a; b := a;<br />

Copying accessed b^ := a^; *b = *a; b.all := a.all<br />

object<br />

<strong>Ada</strong>95. Typically all access objects are allocated from a storage pool (heap) (see below). However it is possible to create<br />

an access type to other objects, so long as they are declared aliased, and the access type is declared to point at 'all' objects.<br />

procedure demo is<br />

type ptr is access all integer;<br />

a :aliased integer;<br />

x, y :ptr;<br />

begin<br />

x := a'access;<br />

y := new integer;<br />

end;<br />

Scope rules ensure that there cannot be any dangling references to objects. The attribute Unchecked_Access can be used to<br />

create access values in an unsafe manner.<br />

Access types to subprograms<br />

A feature not available in <strong>Ada</strong>83, access types to subprograms allow for functional parameterisation such as used in<br />

Fortran mathematical libraries, as well as ad-hoc late binding. As for the rest of <strong>Ada</strong>, this is type safe.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/13_access_types.html (2 of 4) [10/14/2000 12:55:29 PM]


An example from the LRM (3.10).<br />

type Message_Procedure is access procedure (M:in String := "Error!");<br />

procedure Default_Message_Procedure (M :in String);<br />

Give_Message : Message_Procedure := Default_Message_Procedure'Access;<br />

...<br />

procedure Other_Procedure (M :in String);<br />

...<br />

Give_Message := Other_Procedure'Access;<br />

...<br />

Give_Message.("File not found");<br />

Give_Message.all;<br />

Self referencing data structures<br />

To define a structure that refers to itself requires normally requires making references to data structures that don't as yet<br />

exist. <strong>Ada</strong> circumvents this problem by allowing for incomplete type declarations. These simply specify the name of a type<br />

that is yet to be defined. The compiler expects that the full definition will be given before the end of the source file.<br />

type element; -- incomplete type definition.<br />

type ptr is access element;<br />

type element is -- full definition of the type.<br />

record<br />

value:integer;<br />

next:ptr;<br />

end record;<br />

Initialisation of objects<br />

When an object is dynamically allocated its value can be set in the same statement as its creation. This simply uses a<br />

qualified (type name specified) aggregate.<br />

E.g.<br />

head := new element'(40,head); -- insertion at the head<br />

-- of a linked list.<br />

The same thing can be done using named aggregates.<br />

head := new element'(value => 40; next => head);<br />

Garbage collection<br />

There is no language requirement for deallocated memory to be put back into use for later allocation. You should consult<br />

your compiler reference manual for details.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/13_access_types.html (3 of 4) [10/14/2000 12:55:29 PM]


If you want to deallocate memory (similar to free system call in Unix), then you can instantiate the generic procedure<br />

Unchecked_Deallocation (child of package <strong>Ada</strong> in <strong>Ada</strong>95). It is called unchecked because it does no live object analysis<br />

before deallocation.<br />

generic<br />

type Object() is limited private;<br />

type Name is access Object;<br />

procedure <strong>Ada</strong>.Unchecked_Deallocation(X: in out Name);<br />

pragma Convention(Intrinsic, <strong>Ada</strong>.Unchecked_Deallocatoin);<br />

This would be instantiated as follows...<br />

procedure Free is new <strong>Ada</strong>.Unchecked_Deallocation(object => node,<br />

name => ptr);<br />

Free could then be called as follows...<br />

head := new node;<br />

...<br />

free(head);<br />

Storage Pools<br />

<strong>Ada</strong>95 allows a storage pool to be specified from which allocated memory comes from. Different access types can share a<br />

pool, typically most user defined access types share the one program wide pool. By extending the abstract type<br />

Root_Storage_Pool, defined in packge System.Storage_Pools, users can write their own storage pool type, and then<br />

associate it with an access type via the Storage_Pool attribute.<br />

For example (from LRM, 13.11)<br />

Pool_Object : Some_Storage_Pool_Type;<br />

type T is access ;<br />

for T'Storage_Pool use Pool_Object;<br />

Storage pools allow for the possibility access types that are smaller than for a default access type that accesses a default<br />

pool.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/13_access_types.html (4 of 4) [10/14/2000 12:55:29 PM]


1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

13 Access types<br />

Overview<br />

Access types<br />

Access types to subprograms<br />

Self referencing data structures<br />

Initialisation of objects<br />

Garbage collection<br />

Storage Pools<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/13_access_types_ToC.html [10/14/2000 12:55:30 PM]


14 Object Oriented features of <strong>Ada</strong><br />

Overview<br />

The type system of <strong>Ada</strong> that existed in <strong>Ada</strong>83 has been extended to include support for object oriented<br />

programming. Rather than an extension that would be discordant with what existed, the object oriented<br />

facilities extend the existing type system to allow for type extension. These provide all the classical object<br />

oriented facilities such as inheritance, dynamic dispatching, polymorphism, with the usual <strong>Ada</strong> features of<br />

readability and safety.<br />

Types are for many purposes similar to classes.<br />

The following object diagram is implemented using <strong>Ada</strong>.<br />

Inheritance (<strong>Ada</strong>83)<br />

Inheritance can be split into two distinct concepts, inheritance of operations (methods) and inheritance and<br />

further addition of attributes (type extension). <strong>Ada</strong> 83 supported inheritance of operations. If a new type is<br />

derived from a base type, then the derived type inherits the operations available from the parent. For example,<br />

type person is<br />

record<br />

name :string(1..10);<br />

dob :date;<br />

salary :float := 0.0; -- :-(<br />

end record;<br />

procedure employ(someone:in out person);<br />

procedure dismiss(someone:in out person);<br />

type manager is new person;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (1 of 9) [10/14/2000 12:55:32 PM]


-- derived type manager inherits the operations of person,<br />

-- and can add new operations of its own.<br />

procedure allocate_master_key(someone:in out manager);<br />

This model of inheritance does not allow for extension of types, that is we cannot add in extra attributes. The<br />

procedures (or functions) that take a parameter of a type are said to be the operations on the type and are<br />

equivalent to methods in other OO languages.<br />

Inheritance (<strong>Ada</strong>95)<br />

<strong>Ada</strong>95 has extended the notion of type derivation to support conventional object oriented programming. A new<br />

type, the tagged record, has been introduced to represent a type that can be extended (inherited). The syntax for<br />

a tagged type is<br />

type person is tagged<br />

record<br />

name :string(1..10);<br />

dob :date;<br />

salary :float := 0.0;<br />

end record;<br />

A tagged record is treated like a conventional record in most situations. Field accessing, and setting is just as<br />

normal.<br />

jane :person := ("Jane ", (6,10,1978), 210.0);<br />

Operations on the type are written just as before...<br />

procedure employ(someone:in out person);<br />

procedure dismiss(someone:in out person);<br />

procedure pay_rise(someone:in out person);<br />

If we want to extend the type, we follow a notation similar to the type derivation common in <strong>Ada</strong>...<br />

type manager is new person with<br />

record<br />

master_key_code :string(1..10);<br />

end record;<br />

The "with record" part indicates the extension to the base type (in this case "person").<br />

New operations for the manager can be added, just as in <strong>Ada</strong>83...<br />

procedure allocate_master_key(<br />

someone:in out manager;<br />

code :in string);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (2 of 9) [10/14/2000 12:55:32 PM]


We may even want to override an inherited operation...<br />

procedure pay_rise(someone:in out manager);<br />

We can now write a different algortihm for pay_rise's for manager's compared to person's.<br />

How do I set all of this up?<br />

In <strong>Ada</strong>, the package concept is simply used for encapsulating the declaration of a type, and it's operations. The<br />

package is used to hide the implementation details, and as an aid to recompilation (we can split a program up<br />

into smaller chunks, which individually are quick to compile). A very common convention is to place one type<br />

and it's operations into one package.<br />

The above example would thus be placed into two packages...<br />

with dates; use dates; -- definition of type date<br />

package persons is<br />

type person is tagged<br />

record<br />

name :string(1..10);<br />

dob :date;<br />

salary :float := 0.0;<br />

end record;<br />

procedure employ(someone:in out person);<br />

procedure dismiss(someone:in out person);<br />

procedure pay_rise(someone:in out person);<br />

end persons;<br />

The declaration of the derived type would be placed in a second package...<br />

with persons; use persons;<br />

package managers is<br />

type manager is new person with<br />

record<br />

master_key_code :string(1..10);<br />

end record;<br />

procedure allocate_master_key(<br />

someone:in out manager;<br />

code :in string);<br />

procedure pay_rise(someone:in out person);<br />

end managers;<br />

For tagged types, the operations on the type that are included in the same package as the type declaration (as<br />

all of the above are) have a special status - they are called the primitive operations of the type - and are all<br />

potentially dispatching calls. This will be described in further detail later.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (3 of 9) [10/14/2000 12:55:32 PM]


Using tagged types<br />

Using these types is quite simple. You just declare variables as you would have before...<br />

with text_io; use text_io;<br />

with persons; use persons;<br />

with dates; use dates;<br />

with managers; use managers;<br />

procedure demo is<br />

me :person;<br />

him :manager;<br />

begin<br />

end;<br />

me.name := "Santa ";<br />

me.dob := (29,11,1962);<br />

him.name := "Rudolph "; -- inherited field<br />

him.dob := (28, 6, 1962); -- inherited field<br />

employ(him); -- inherited operation<br />

-- new method for type manager<br />

allocate_master_key(him, code => "XYZ398");<br />

pay_rise(him); -- Manager version<br />

pay_rise(me); -- Person version<br />

Null records and extensions<br />

A type may need to be created that has no fields (attributes), only operations (methods). This can be achieved<br />

by specify an empty record when declaring a type.<br />

type root is tagged<br />

record<br />

null;<br />

end record;<br />

<strong>Ada</strong> has a special syntax for null records being used with tagged types:<br />

type root is tagged null record;<br />

This can now be used as the basis of further derivations.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (4 of 9) [10/14/2000 12:55:32 PM]


Operations on the type can be added as normal within a package specification...<br />

procedure do_something(item :root);<br />

More typically a new type may be derived from an existing type without the need for new attributes, but with<br />

the need for new subprograms.<br />

The syntax for such an extension would be...<br />

type director is new manager with null record;<br />

procedure pay_rise(someone :in out director);<br />

A director has no new components added, but can has yet another method for allocating a pay_rise.<br />

Abstract types and subprograms<br />

Sometimes we wish to create an artificial type that we never expect to use, but serves as the "root" of a tree of<br />

types. These are called abstract types. They may also include abstract subprograms , which do not have a body<br />

and must be overridden by a derived type. This forces all descendants of a type to support a common<br />

functionality.<br />

An abstract type has the following syntax...<br />

package Sets is<br />

type Set is abstract tagged null record;<br />

function Empty return Set is abstract;<br />

function Empty(Element:Set) return boolean is abstract;<br />

function Union(left, right:set) return Set is abstract;<br />

function Intersection(left, right:set) return Set is abstract;<br />

procedure Insert(Element:natural; into:Set) is abstract;<br />

end Sets;<br />

This implementation of a set of natural numbers is taken from the <strong>Ada</strong> Language Reference Manual. Note that<br />

you cannot declare a variable of type Set because it is an abstract type. E.g.<br />

with Sets; use Sets;<br />

procedure wont_compile is<br />

my_set :Set; -- illegal, abstract type<br />

begin<br />

null;<br />

end;<br />

However a derived type may or may not be abstract...<br />

with Sets;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (5 of 9) [10/14/2000 12:55:32 PM]


package quick_sets is<br />

type bit_vector is array(0..255) of boolean;<br />

pragma pack(bit_vector);<br />

type quick_set is new Sets.set with<br />

record<br />

bits:bit_vector := (others => false);<br />

end record;<br />

-- advertise concrete implementations<br />

function Empty return quick_set;<br />

function Empty(Element:quick_set) return boolean;<br />

etc. etc.<br />

Class wide types<br />

For any given tagged type, there is an associated class wide type, that encompasses the tagged type and all<br />

types derived from it. <strong>Ada</strong> has used the word "class" differently to most object oriented languages. It is perhaps<br />

used in a more traditional English sense, as a collection of similar types.<br />

For example in the type family (inheritance hierachy)<br />

vehicle<br />

motorised<br />

truck<br />

car<br />

train<br />

unmotorised<br />

bicycle<br />

the type vehicle'class refers to all the types above. Motorised'class refers to the classes<br />

motorised, truck, car and train.<br />

This can be used to allow for dynamic selection of the appropriate operation.<br />

Assume that the following procedure was defined in package persons.<br />

procedure reward_good_work(someone:in out person) is<br />

begin<br />

print_certificate(someone.name);<br />

pay_rise(someone);<br />

end;<br />

If we call this with a manager as a parameter, then the procedure persons.pay_rise will be called, not<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (6 of 9) [10/14/2000 12:55:32 PM]


managers.pay_rise.<br />

A solution to this problem is to accept as a parameter any variable in the class wide type. This is done as<br />

follows...<br />

procedure reward_good_work(someone:in out person'class) is<br />

begin<br />

print_certificate(someone.name);<br />

pay_rise(someone);<br />

end;<br />

If the following declarations are made...<br />

and then...<br />

me :person;<br />

him :manager;<br />

reward_good_work(me); -- calls persons.pay_rise<br />

reward_good_work(him); -- calls managers.pay_rise<br />

Here the pay_rise procedure called depends on the specific type passed in as a parameter. This is called<br />

dynamic dispatching - deciding which procedure to dispatchi to (or call) at run time (dynamically). Passing in<br />

a manager object results in a call to the manager's pay_rise routine. Passing in a person object results in a call<br />

to the person's pay_rise routine.<br />

What pay_rise routine would be called if a Director object was passed in?<br />

In fact all the primitive operations of type are potentially dispatching calls, given the right circumstances. The<br />

example using a class wide type is one example. Another (closely related) example is the use of class wide<br />

access (pointer) types.<br />

What you can't do with a class wide type (such as person'class) is create an array of them. This is because each<br />

object is potentially a different size - arrays require all elements to be the same size. Similar considerations<br />

apply for any place where a declaration must be of a fixed size (constrained, in <strong>Ada</strong> parlance).<br />

Class wide pointers<br />

Most pointers in <strong>Ada</strong> are restricted to pointing at just a single type. For example...<br />

type node;<br />

type ptr is access node;<br />

type node is record<br />

item :integer;<br />

next :ptr;<br />

end record;<br />

Here variables of type ptr are restricted to always pointing at variables of type node.<br />

With the concept of class wide types in <strong>Ada</strong>, comes the concept of a pointer that can point to any item within<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (7 of 9) [10/14/2000 12:55:32 PM]


an inheritance hierachy.<br />

This is achived by...<br />

type person_ptr is access person'class;<br />

who :person_ptr;<br />

who' can now point at any object from the derivation tree rooted at person, i.e. person, manager, director, and<br />

quite importantly, any future types included in the inheritance hierachy.<br />

For example we could make 'who' point at...<br />

who := new person;<br />

who := new manager;<br />

who := new director;<br />

Interestingly, we could create an array of class wide access types...<br />

type person_list is array(1..10) of person_ptr;<br />

people :person_list;<br />

...<br />

people(1) := new person;<br />

people(2) := new manager;<br />

people(3) := new person;<br />

people(4) := new director;<br />

-- initialise the variables appropriately.<br />

...<br />

for i in people'range loop<br />

exit when people(i) = null;<br />

-- now dispatch to the appropriate routine...<br />

pay_rise(people(i).all);<br />

end loop;<br />

Class wide pointers can be used to build hetrogeneous data structures, that is those were the components are<br />

not all the same type (they must however be in the same class).<br />

Private tagged types<br />

A type can be declared private to help enforce information hiding (especially of the exact details of its<br />

representation, e.g. is it an array, or a linked list?). Similarly tagged types can be made private.<br />

type person is tagged private;<br />

The private section of the package must complete the declaration (provide what is called a full view of the<br />

type)...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (8 of 9) [10/14/2000 12:55:32 PM]


type person is tagged record...<br />

A type extension can also have a private extension if desired...<br />

type manager is new person with private;<br />

This gives sufficient flexibility for most purposes.<br />

Generic type parameters<br />

Tagged types can now be specified as generic type parameters (they can still be passed as actuals to the normal<br />

private or limited private type parameters). The syntax is as follows...<br />

generic<br />

-- actual must be a tagged type<br />

type T is tagged private;<br />

-- actual must be direct descendent of S<br />

type T is new S;<br />

-- actual must be derived from S somewhere<br />

type T is new S with private;<br />

package some_interesting_pacakge is...<br />

Multiple Inheritance<br />

<strong>Ada</strong>95 does not directly support MI. It provides features that can be used to build MI "by hand" with a bit of<br />

extra work. The options are generics (to extend a type with a set of new features), access discriminants (to<br />

allow a type to point at it's containing object, and therefore allow delegation) and another one that I can't quite<br />

remember now.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/14_OO.html (9 of 9) [10/14/2000 12:55:32 PM]


15 Concurrency support<br />

Overview<br />

<strong>Ada</strong> is one of the few mainstream languages that supports concurrency from within the language. This has the benefit of<br />

stable semantics, and therefore portability between computers. The task and rendezvous mechanism of <strong>Ada</strong>83 has been<br />

enhanced through the introduction of protected objects and greater considertion for real time systems.<br />

Tasks<br />

An <strong>Ada</strong> program consists of at least one, and possible more tasks, which run concurrently. The tasks run independently of<br />

each other, communication/synchronisation is achieved by high level concepts such as the rendezvous or with protected<br />

objects. With an abstraction level higher than simple semaphores, the rendezvous and protected objects come supplied with a<br />

range of options for guards (as Dijkstra postulated), timeouts and (in <strong>Ada</strong>95) to selectively requeue clients and abort<br />

operations.<br />

The death of a task does not necessarily affect the operation of other tasks, except if they attempt to communicate with it.<br />

There is no specialised task, even the main procedure of the <strong>Ada</strong> program can end, with other tasks continuing.<br />

The rendezvous<br />

As one mechanism for tasks to safely communicate and synchronise with each other, the rendezvous is conceptually simple.<br />

Tasks publish entry points at which they are prepared to wait for other tasks. These entry points have a similar semantics to<br />

procedures; they can have parameters of in, inout and out mode, with default parameter values. A task wishing to rendezvous<br />

with a second task simply makes a procedure-like rendezvous call with an entry; Either is held up until they can both<br />

communicate. The rendezvous is non symmetrical - one task is viewed as a server and cannot initiate a rendezvous.<br />

procedure demo is<br />

task single_entry is<br />

entry handshake;<br />

end task;<br />

begin<br />

end;<br />

task body single_entry is<br />

begin<br />

delay 50.0;<br />

end;<br />

accept handshake;<br />

delay 1.0;<br />

for i in 1..random(100) loop<br />

delay(1.0);<br />

end loop;<br />

handshake;<br />

When this program is run the task single_entry is started. If succeful the parent task is also activated. If 'random' returns a<br />

value less than 50, then after the initial delay the main task waits for the second task, and vice versa if it returns greater than<br />

50. Once the two tasks have rendezvoused, they depart on their separate ways. The main task finishes, the second task<br />

continues until it too finishes. The program then terminates.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/15_concurrency.html (1 of 6) [10/14/2000 12:55:34 PM]


The same program can be modified to include parameters in the entry call. In this example, the two tasks swap duration times<br />

for the delay statements. However they may still not finish together as the quicker task to the rendezvous will still have to<br />

wait for the second task.<br />

procedure demo is<br />

x :duration := duration(random(100));<br />

y :duration;<br />

task single_entry is<br />

entry handshake(me_wait: in duration; you_wait: out duration);<br />

end task;<br />

task body single_entry is<br />

a :duration := duration(random(100));<br />

b :duration;<br />

begin<br />

delay a;<br />

accept handshake(me_wait:in duration; you_wait: out duration) do<br />

b := me_wait;<br />

you_wait := a;<br />

end handshake;<br />

delay b;<br />

end;<br />

begin<br />

delay(x);<br />

handshake(x,y);<br />

delay(y);<br />

end;<br />

A task can have several rendezvous'. If it waits for a rendezvous and there is no possible task that can rendezvous with, then it<br />

will abort with a tasking_error exception.<br />

Rendezvous' can be treated in a manner very similar to procedure calls. They can be in loops, if statements, subprograms etc.<br />

Typically however they are structured in a high level loop; this allows a client architecture in which after servicing a<br />

rendezvous, and performing some processing, it goes back and waits for another rendezvous.<br />

procedure demo is<br />

type service is (increment, decrement, quit);<br />

task single_entry is<br />

entry accept_command(which : service);<br />

end task;<br />

task body single_entry is<br />

command :service;<br />

i :integer;<br />

begin<br />

loop<br />

accept accept_command(which :service) do<br />

command := which;<br />

end accept_command;<br />

case command is<br />

when increment => i := i + 1;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/15_concurrency.html (2 of 6) [10/14/2000 12:55:34 PM]


egin<br />

end;<br />

end;<br />

when decrement => i := i - 1;<br />

when quit => exit;<br />

end case;<br />

end loop;<br />

-- stuff, depending perhaps upon user input.<br />

This situation can be impoved having multiple entry points, and making use of the select statement in conjunction with the<br />

accept statement.<br />

procedure demo is<br />

task multiple_entry is<br />

entry increment;<br />

entry decrement;<br />

end multiple_entry;<br />

task body multiple_entry is<br />

i : integer;<br />

begin<br />

select<br />

accept increment;<br />

i := i + 1;<br />

or<br />

accept decrement do<br />

i := i - 1;<br />

end decrement<br />

or<br />

terminate;<br />

end select;<br />

end;<br />

begin<br />

-- rendezvous'<br />

end;<br />

The select statement allows the task to wait on several different entry points. This example shows the increment entry without<br />

any associated processing. The decrement entry forces the calling task to wait while the decrement takes place. The terminate<br />

alternative causes task multiple_entry to finish if it is uncallable from any other task.<br />

The select statement can have a selective wait, unconditional alternative, a terminate or none of these. The previous example<br />

showed the terminate. The unconditional alternative is shown below.<br />

procedure demo is<br />

task multiple_entry is<br />

entry increment;<br />

entry decrement;<br />

end multiple_entry;<br />

task body multiple_entry is<br />

i : integer;<br />

begin<br />

select<br />

accept increment;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/15_concurrency.html (3 of 6) [10/14/2000 12:55:34 PM]


egin<br />

end;<br />

end;<br />

or<br />

else<br />

-- rendezvous'<br />

i := i + 1;<br />

accept decrement do<br />

i := i - 1;<br />

end decrement<br />

perform_some_processing;<br />

end select;<br />

In this example, the task stops only briefly to inspect if another task is waiting to rendezvous. If not it goes off and does some<br />

processing.<br />

The selective wait allows the task to wait for a given time, after which it gives up and continues on with other actions.<br />

procedure demo is<br />

task multiple_entry is<br />

entry increment;<br />

entry decrement;<br />

end multiple_entry;<br />

task body multiple_entry is<br />

i : integer;<br />

begin<br />

select<br />

accept increment;<br />

i := i + 1;<br />

or<br />

accept decrement do<br />

i := i - 1;<br />

end decrement<br />

or<br />

delay 3.0; -- not a normal delay statement!<br />

perform_some_processing;<br />

end select;<br />

end;<br />

begin<br />

-- rendezvous'<br />

end;<br />

Here the task will wait for at least 3.0 seconds for another task. If none comes along it will call perform_some_processing.<br />

Similarly the client task can either wait unconditionally for a rendezvous, it can not wait at all (impatient), or it can specify a<br />

time out after which it will give up on a rendezvous.<br />

Task types<br />

All of the above tasks are of an anonymous type. <strong>Ada</strong> allows you to create a task type (via a special and non consistent syntax<br />

from all other type declarations) and to declare objects of the type. They can be declared dynamically (through an allocater) as<br />

well as local variables. They can be composed into other objects - you could have an array of tasks, or a record, a component<br />

of which, is a task.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/15_concurrency.html (4 of 6) [10/14/2000 12:55:34 PM]


Problems with the rendezvous mechanism<br />

The rendezvous mechanism is a synchronisation as well as a communication mechanism. Tasks that should normally run<br />

asynchronously and which want to pass data between them are required to stop and "have a chat", instead of simply leaving<br />

the data for the other task. The solution to this problem is usually to creat an intermediary task that manages the data flow,<br />

and, the designer hopes, is always available to respond to a rendezvous from either task. It sits between the two tasks, acting<br />

as an emissary for both.<br />

Protected Objects<br />

A simpler solution can be provided in <strong>Ada</strong>95 which has a concept of a protected object or type. Protected objects provide for<br />

task safe access to data via entries and guards, without the overhead of a separate task being created. The subprograms and<br />

entries inside a protected object are executed in the context of the calling task. As with tasks types you can create arrays of<br />

protected type, and compose other objects from them.<br />

All the examples in this section come from Introducing <strong>Ada</strong>9x, John Barnes.<br />

An example of a protected entry follows<br />

protected Variable is<br />

function Read return item;<br />

procedure Write(New_Value:item);<br />

private<br />

Data :item;<br />

end Variable;<br />

protected body Variable is<br />

function Read return item is<br />

begin<br />

return Item;<br />

end;<br />

procedure Write(New_value :item) is<br />

begin<br />

data := New_Value;<br />

end;<br />

end Variable;<br />

Functions are only allowed read access to the data, procedures can manipulate them in any way they wish. Procedure calls are<br />

exclusive, that is only one task can access a procedure at any one time. Multiple reads can occur concurrently.<br />

Another example is a semaphore, to provide exclusive access to resources.<br />

protected type Counting_Semaphore (Start_count :Integer := 1) is<br />

entry Secure;<br />

procedure Release;<br />

function Count return integer;<br />

private<br />

Current_count :integer := start_count;<br />

end;<br />

protected body counting_semaphore is<br />

entry Secure when Current_count > 0 is<br />

begin<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/15_concurrency.html (5 of 6) [10/14/2000 12:55:34 PM]


end;<br />

Current_Count := Current_count + 1;<br />

procedure Release is<br />

begin<br />

Current_Count := Current_count + 1;<br />

end;<br />

function Count return integer is<br />

begin<br />

return current_count;<br />

end;<br />

end Counting_semaphore;<br />

When called the barrier on the entry is queued. If it is false, the task is queued, pending it becoming true.<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/15_concurrency.html (6 of 6) [10/14/2000 12:55:34 PM]


1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

to the index...<br />

15 Concurrency support<br />

Overview<br />

Tasks<br />

The rendezvous<br />

Task types<br />

Problems with the rendezvous mechanism<br />

Protected Objects<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/15_Concurrency_support.html [10/14/2000 12:55:35 PM]


16 Language interfaces<br />

Overview<br />

No matter how good a new language is, there is usually a great deal of investment in existing programs. The<br />

designers of <strong>Ada</strong> realised this and provide a standard mechanism to interface with other language. The facilites<br />

provided are optional, and just which languages are supported depend on the compiler vendor. Considerably<br />

more support has been built into <strong>Ada</strong>95. Consult your compiler's reference manual for full details.<br />

Interfacing with foreign languages (<strong>Ada</strong>83)<br />

Assume you are on a Unix system and you wish to make use of the kill command. You should perform the<br />

following.<br />

function kill( pid :in integer;<br />

sig :in integer) return integer;<br />

pragma interface(C,kill);<br />

The first parameter to pragma interface is the language of the called routine, the second the name the routine is<br />

known by in <strong>Ada</strong>.<br />

Another example for a package is<br />

package MATHS is<br />

function sqrt(x:float) return float;<br />

function exp (x:float) return float;<br />

private<br />

pragma interface(fortran,sqrt);<br />

pragma interface(fortran,exp);<br />

end MATHS;<br />

The pragma interface cannot be used with generic subprograms.<br />

Interfacing with foreign languages (<strong>Ada</strong>95)<br />

<strong>Ada</strong>95 makes extensive use of the predefined libraries to enable data type translation between <strong>Ada</strong> and foriegn<br />

languages. Together with the pragmas import, export and convention they allow <strong>Ada</strong> systems to be easily used<br />

in a multi-language environment.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/16_interfaces.html (1 of 5) [10/14/2000 12:55:36 PM]


Pragma Import<br />

The pragma Import allows the inclusion of foreign language entities within an <strong>Ada</strong> program, such as variables or<br />

procedures/functions. The code below shows an example of the use of pragma import for the Unix function<br />

read.<br />

Pragma Import consists of three parameters,<br />

- the language convention (only <strong>Ada</strong> and Instrinsic must be supported)<br />

- <strong>Ada</strong><br />

- Instrinsic<br />

- C<br />

- Fortran<br />

- Cobol<br />

- any other implementation defined value<br />

- the <strong>Ada</strong> name for the object<br />

- the foreign langauge name for the object, as a string.<br />

-----------------------------------------------------------procedure<br />

read( file_descriptor :in integer;<br />

buffer :in out string;<br />

no_bytes :in integer;<br />

no_read : out integer) is<br />

begin<br />

end;<br />

function read( file_descriptor :integer;<br />

buffer :system.address;<br />

no_bytes:integer) return integer;<br />

pragma import(C, read, "read");<br />

no_read := read(file_descriptor,<br />

buffer(buffer'first)'address,<br />

no_bytes);<br />

The interface packages<br />

The interface package hierachy consists of packages designed to ease the interfacing of <strong>Ada</strong> with other<br />

langauges. The standard suggests interfaces for C, COBOL and Fortran.<br />

The package hierachy is<br />

package Interfaces<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/16_interfaces.html (2 of 5) [10/14/2000 12:55:36 PM]


package Interfaces.C<br />

package Interfaces.C.Pointers<br />

package Interfaces.C.Strings<br />

package Interfaces.COBOL<br />

package Interfaces.Fortran<br />

The packages give sufficient power to deal with most foreign language interfacing issues.<br />

One area in which <strong>Ada</strong> has problems interfacing to other languages is functions that contain non homogenous<br />

variable length parameter lists, such as printf. Such functions are inherently type unsafe, and there is no<br />

satisfactory way to handle such situations.<br />

<strong>Ada</strong> can, however, handle functions where the argument types are homogenous; this is achieved through the use<br />

of unconstrained array types.<br />

A simple example would be a C function...<br />

void something(*int[]);<br />

it could be accessed as follows...<br />

type vector is array(natural range ) of integer;<br />

procedure something(item :vector) is<br />

function C_Something(address:system.address);<br />

pragma import(C, C_something, "something);<br />

begin<br />

if item'length = 0 then<br />

C_something(system.null_address);<br />

else<br />

C_something(item(item'first)'address);<br />

end if;<br />

end;<br />

A larger and more complex example is given below for the Unix C function execv. The added complication is<br />

caused by the necessity of translation from <strong>Ada</strong> strings to C style character arrays (and is not necessarily as good<br />

as it could be. See the LRM for more information on using interfaces.c child family).<br />

The C function is defined as...<br />

int execv(const char *path, char *const argv[]);<br />

you need to declare a few things...<br />

---------------------------------------------------------type<br />

string_ptr is access all string;<br />

type string_array is array(natural range ) of string_ptr;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/16_interfaces.html (3 of 5) [10/14/2000 12:55:36 PM]


function execv( path :string;<br />

arg_list :string_array) return interfaces.c.int;<br />

---------------------------------------------------------<br />

-- execv replaces the current image with a new one.<br />

-- A list of arguments is passed as the command line<br />

-- parameters for the called program.<br />

--<br />

-- To call this routine you can...<br />

--<br />

-- option2 :aliased string := "-b";<br />

-- option3 :aliased string := "-c";<br />

-- option4 :string := "Cxy";<br />

-- result :interfaces.C.int;<br />

-- ...<br />

-- result := execv(path => "some_program",<br />

-- -- build up an array of string pointers...<br />

-- argv => string_array'(new string'("some_program"),<br />

-- new string'("-a"),<br />

-- option2'Unchecked_access,<br />

-- option3'Unchecked_access,<br />

-- new string'('-' & option4));<br />

--<br />

-- Any mixture of dynamic string allocation and<br />

-- 'Unchecked_access to aliased variables is allowed. Note however that<br />

-- you can't do "some_string"'access, as <strong>Ada</strong> requires a name,<br />

-- not a value, for the 'access attribute to be applied to.<br />

------------------------------------------------------------<br />

This function could be implemented as follows. Note that the address of the first item in the array is passed, not<br />

the address of the array. Arrays declared from unconstrained array types often have a vector which includes<br />

extra information such as the lower and upper bounds of the array.<br />

function execv( path :string;<br />

argv :string_array )return interfaces.C.int is<br />

Package C renames Interfaces.C;<br />

Package Strings renames Interfaces.C.Strings;<br />

C_path :constant Strings.chars_ptr(1..path'length+1)<br />

:= Strings.New_string(path);<br />

type char_star_array is array(1..argv'length + 1) of<br />

Strings.char_array_ptr;<br />

C_argv :char_star_array;<br />

index :integer;<br />

result :C.int;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/16_interfaces.html (4 of 5) [10/14/2000 12:55:36 PM]


egin<br />

-----------------------------------------------------------function<br />

C_execv( path :Strings.Char_ptr;<br />

C_arg_list:Strings.Char_ptr)<br />

return C.int;<br />

pragma import(C, C_execv, "execv");<br />

------------------------------------------------------------<br />

-- set up the array of pointers to the strings<br />

index := 0;<br />

for i in argv'range loop<br />

index := index + 1;<br />

C_argv(index) := Strings.New_String(argv(i).all));<br />

end loop;<br />

-- append C style null to the end of the array of addresses<br />

C_argv(C_argv'last) := Strings.Null_Ptr;<br />

-- pass the address of the first element of each<br />

-- parameter, as C expects.<br />

result := C_execv( C_path(1)'address, C_argv(1)'address));<br />

-- Free up the memory as obviously this didn't work<br />

for i in argv'range loop<br />

Strings.free(argv(i));<br />

end loop;<br />

Strings.free(C_path);<br />

return result;<br />

end execv;<br />

to the index...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/16_interfaces.html (5 of 5) [10/14/2000 12:55:36 PM]


1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

16 Language interfaces<br />

Overview<br />

Interfacing with foreign languages (<strong>Ada</strong>83)<br />

Interfacing with foreign languages (<strong>Ada</strong>95)<br />

Pragma Import<br />

The interface packages<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/16_interfaces_ToC.html [10/14/2000 12:55:37 PM]


17 Idioms<br />

Overview<br />

Using a language effectively requires more than a knowledge of it's syntax and semantics. An acquaintaince with<br />

common problems, and the various methods to solve these problems can simplify the process of writing programs,<br />

and in this respect <strong>Ada</strong> is no exception. This chapter is involved in exposing some of the more common idioms of<br />

programming in <strong>Ada</strong>.<br />

Introduction<br />

Abstraction is one of the most important part of programming, and it should always be considered when<br />

programming solutions. You must always make an effort to distinguish what service a type is providing and how it is<br />

implemented. You expose the what, but try to hide the how.<br />

Similarly a knowledge of how to hide things in <strong>Ada</strong> is important. <strong>Ada</strong> provides for (predominantly) orthogonal<br />

concepts of type/behaviour, and modularity/controlling visibility. The semantics of a program (how it runs, what it<br />

does) are independent of whether you design the program carefully for modularity and robustness in the face of<br />

change. Clearly though it is more often than not worth the effort to design a system competently.<br />

Abstraction<br />

Abstractions allow us to present only that which we want the user to be concerned with, and not giving access to<br />

information which is irrelavent. For example in the all too often used stack example, the user should not be too<br />

concerned with whether the implementation uses arrays or linked lists.<br />

A rough stab at an abstraction could be...<br />

package stacks is<br />

max :constant := 10;<br />

subtype count is integer range 0..max;<br />

subtype index is range 1..max;<br />

type list is array(index) of integer;<br />

type stack is<br />

record<br />

values : list;<br />

top : count;<br />

end record;<br />

overflow :exception;<br />

underflow :exception;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (1 of 23) [10/14/2000 12:55:42 PM]


end;<br />

procedure push(item :in out stack; value :in integer);<br />

procedure pop(item :in out stack; value : out integer);<br />

function full(item :stack) return boolean;<br />

function empty(item :stack) return boolean;<br />

-- return an empty initialized stack<br />

function init return stack;<br />

Here however the details of the implementation are rather obvious; the how tends to get in the way of the what. This<br />

can be changed simply by moving whatever is not relavent into the private section...<br />

package stacks is<br />

private<br />

end;<br />

type stack is private;<br />

-- This is called a partial view of the type stack<br />

-- It makes info about its implementation inaccessable.<br />

overflow :exception;<br />

underflow :exception;<br />

procedure push(item :in out stack; value :in integer);<br />

procedure pop(item :in out stack; value : out integer);<br />

function full(item :stack) return boolean;<br />

function empty(item :stack) return boolean;<br />

-- return an empty initialized stack<br />

function init return stack;<br />

-- full view of the type, along with other private details.<br />

max :constant := 10;<br />

subtype count is integer range 0..max;<br />

subtype index is range 1..max;<br />

type list is array(index) of integer;<br />

type stack is<br />

record<br />

values : list;<br />

top : count;<br />

end record;<br />

Although the programmer who uses your abstraction can see all the details if they have access to the code, they can't<br />

write their programs to rely on this information. Also the separation clearly enforces the notion of what is important<br />

for the abstaction, and what is not.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (2 of 23) [10/14/2000 12:55:42 PM]


Alternatively we could change the private section to reflect a linked list implementation...<br />

package stacks is<br />

private<br />

end;<br />

type stack is private;<br />

-- make info about its implementation inaccessable.<br />

-- this is called a partial view of the type stack<br />

overflow :exception;<br />

underflow :exception;<br />

procedure push(item:in out stack; value:in integer);<br />

procedure pop(item:in out stack; value : out integer);<br />

function full(item:stack) return boolean;<br />

function empty(item:stack) return boolean;<br />

-- return an empty initialized stack<br />

function init return stack;<br />

type stack;<br />

type ptr is access stack;<br />

type stack is record<br />

value :integer;<br />

next :ptr;<br />

end;<br />

A user program could then use either package as follows...<br />

with stacks; use stacks;<br />

procedure demo is<br />

a :stack := init;<br />

b :stack := init;<br />

temp :integer;<br />

begin<br />

for i in 1..10 loop<br />

push(a,i);<br />

end loop;<br />

end;<br />

while not empty(a) loop<br />

pop(a, temp);<br />

push(b, temp);<br />

end loop;<br />

The concept of being able to substitute a different implementation, or more precisely, only relying on the public<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (3 of 23) [10/14/2000 12:55:42 PM]


interface, is a very important design principle for building robust systems.<br />

(Note that these two implementations aren't quite identical. Consider the case where we have<br />

with stacks; use stacks;<br />

procedure not_quite_right is<br />

a, b:stack<br />

begin<br />

push(a,1);<br />

push(a,2);<br />

end;<br />

b := a;<br />

An array implementation will work correctly when copied, but a linked list implementation will only copy the head<br />

pointer. Both a and b will then point to the same linked list. The solution to this is presented later on. For the<br />

examples presented here on, assume that this problem has been fixed).<br />

Creating abstractions from other abstractions (code<br />

reuse)<br />

Private inheritance<br />

It is important to establish the difference between what services a type provides, and how it implements that service.<br />

For example a list abstraction (that has appropriate routines, and itself may be implemented as a linked list, or an<br />

array) can be used to implement a queue abstraction. The queue abstraction will have only a few operations:<br />

add_to_tail<br />

remove_from_head<br />

full<br />

empty<br />

init<br />

We want to ensure there is a clear distinction between the abstraction and the implementation in our program.<br />

Preferably the compiler should check and ensure that no-one makes the mistake of calling routines from the<br />

implementation, rather than the abstraction.<br />

Below is an example package which can lead to problems.<br />

package lists is<br />

type list is private;<br />

procedure add_to_head(item:in out list; value :in integer);<br />

procedure remove_from_head(item:in out list; value :out integer);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (4 of 23) [10/14/2000 12:55:42 PM]


private<br />

end;<br />

procedure add_to_tail(item:in out list; value:in integer);<br />

procedure remove_from_tail(item:in out list; value: out integer);<br />

function full(item: list) return boolean;<br />

function empty(item:list) return boolean;<br />

function init return list;<br />

type list is... -- full view of type.<br />

with lists; use lists; -- abstraction of lists<br />

package queues is<br />

end queues;<br />

type queue is new list;<br />

-- inherits operations from the list type<br />

-- i.e. the following are "automagically" (implicity) declared<br />

--<br />

-- procedure add_to_head(item:in out queue; value :in integer);<br />

-- procedure remove_from_head(<br />

-- item :in out queue;<br />

-- value : out integer);<br />

-- etc.<br />

Here type queue inherits all of the operations of the list type, even those that aren't appropriate, such as<br />

remove_from_tail. With this implementation of queue, clients of the abstraction could easily break the queue, which<br />

should only allow insertion at the tail, and removal from the head of the list.<br />

For example a client of the queues package (something that "with queues"), could easily do the following<br />

with queues; use queues;<br />

procedure break_abstraction is<br />

my_Q :queue;<br />

begin<br />

add_to_head(my_Q, 5);<br />

add_to_tail(my_Q, 5);<br />

end;<br />

-- queues should only add at the tail, remove from the head,<br />

-- or vice-versa<br />

What we need to do is advertise a different abstraction, but reuse the list abstraction privately.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (5 of 23) [10/14/2000 12:55:42 PM]


with lists; -- this is only used in the private section.<br />

package queues is<br />

type queue is private;<br />

private<br />

end;<br />

procedure remove_from_head(item:in out queue; value :out integer);<br />

procedure add_to_tail(item:in out queue; value:integer);<br />

function full(item: queue) return boolean;<br />

function empty(item:queue) return boolean;<br />

function init return queue;<br />

type queue is new lists.list;<br />

package body queues is<br />

-- Perform a type conversion (from queue to list), and then call<br />

-- the appropriate list routine.<br />

use lists;<br />

-------------------------------------------------------------------procedure<br />

remove_from_head(item:in out queue; value :out integer) is<br />

begin<br />

end;<br />

lists.remove_from_head(list(item), value);<br />

-------------------------------------------------------------------function<br />

full(item: queue) return boolean is<br />

begin<br />

end;<br />

...<br />

return lists.full(list(item));<br />

function init return queue is<br />

begin<br />

return queue(lists.init);<br />

end;<br />

end;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (6 of 23) [10/14/2000 12:55:42 PM]


Another example<br />

Let's say we wish to create a stack package, but we don't want to recode all of the routines. We may already have a<br />

linked list implementation that could form the basis of the storage for the stack. The list package stores integers.<br />

Assume the package lists is defined as follows...<br />

package lists is<br />

private<br />

end;<br />

type list is private;<br />

underflow :exception;<br />

procedure insert_at_head(item:in out list; value :in integer);<br />

procedure remove_from_head(item:in out list; value:out integer);<br />

procedure insert_at_tail(item:in out list; value :in integer);<br />

procedure remove_from_tail(item:in out list; value: out integer);<br />

function full(item:list) return boolean;<br />

function empty(item:list) return boolean;<br />

-- returns an empty initialized list<br />

function init return list;<br />

...<br />

We can then make use of <strong>Ada</strong>'s ability to hide the full view of a type in the private section. Here we declare a brand<br />

new type (as far as the client is concerned), which is actually implemented as a derived type. The client can't "see"<br />

this - the "under the hood" details remain well hidden.<br />

We implement the stack as...<br />

with lists;<br />

package stacks is<br />

type stack is private;<br />

underflow :exception;<br />

procedure push(item:in out stack; value:in integer);<br />

procedure pop(item:in out stack; value : out integer);<br />

function full(item:stack) return boolean;<br />

function empty(item:stack) return boolean;<br />

-- return an empty initialized stack<br />

function init return stack;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (7 of 23) [10/14/2000 12:55:42 PM]


private<br />

end;<br />

-- create a new type derived from an existing one.<br />

-- This is hidden from the clients of the type<br />

type stack is new lists.list;<br />

-- stack inherits all the operations of type list.<br />

-- They are _implicity_ declared as<br />

--<br />

-- procedure insert_at_head(item:in out stack; value :in integer);<br />

-- procedure remove_from_head(item :in out stack;<br />

-- value: out integer);<br />

-- ...<br />

-- function full(item:stack) return boolean;<br />

-- function empty(item:stack) return boolean;<br />

-- function init return stack;<br />

We now have to relate the implicity declared routines to those that are advertised publically. This is done by a simple<br />

"call through" mechanism.<br />

package body stacks is<br />

procedure push(item:in out stack; value:in integer) is<br />

begin<br />

insert_at_head(item, value);<br />

end;<br />

-- you can make it more efficient by...<br />

pragma inline(push);<br />

procedure pop(item:in out stack; value : out integer) is<br />

begin<br />

remove_from_head(item, value);<br />

exception =><br />

when lists.underflow =><br />

raise stacks.underflow;<br />

end;<br />

...<br />

end stacks;<br />

This is ok for publically advertised routines that have different names from the implicity declared routines (those<br />

inhertied from type list).<br />

However in the package specification there are two functions full which have the profile<br />

function full(item:stack) return boolean;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (8 of 23) [10/14/2000 12:55:42 PM]


One is explicity declared in the public section, one implicity declared in the private section. What's going on? The<br />

first function specification in the public part of the package is only a promise of things to come. It is an as-yet<br />

unrealised function. It will be completed in the package body. As well we have another (implicity) declared function<br />

which just happens to have the same name and profile. You still have to describe to the <strong>Ada</strong> compiler how you are<br />

going to implement the function declared in the public part of the package. The <strong>Ada</strong> compiler can see that both<br />

functions have the same name and profile, but it does not assume that public function should be implemented by the<br />

private function.<br />

For example imagine that integer_lists.full always return false, to indicate that the linked list was never full, and<br />

could always grow. As the implementor of the stack package you may have decided that you wanted to enforce a<br />

limit on the stack, so that it would only contain 100 elements at most. You would then write the function stacks.full<br />

accordingly. It would be incorrect for the implementation of function full to default back to the linked list<br />

implementation.<br />

So far so good. How do I write the function full?<br />

Because of the visibility rules of <strong>Ada</strong>, the explicit public declaration completely hides the implicity declared private<br />

one, and so is not accessable at all. The only solution is to call on the function in the list package, which you have to<br />

do explicity.<br />

package body stacks is<br />

function full(item:stack) return boolean is<br />

begin<br />

-- call the original full in the other package.<br />

end;<br />

...<br />

end stacks;<br />

return lists.full(lists.list(item));<br />

-- type conversion of parameter<br />

This seems all terribly obtuse and annoying. Why does <strong>Ada</strong> force this upon you? The reason you are encountering<br />

this sort of problem is because of the nature of constructing programs with independent name spaces (different<br />

scopes where identical names do not clash with each other). <strong>Ada</strong> is merely providing a solution to a problem that is<br />

an inevitable consequence of name spaces. The alternative, of having to work with a language in which there can be<br />

no names the same is worse than this solution. So don't shoot the messenger just because you don't like the message<br />

:-).<br />

This is a very important point to note. Don't go on unless you understand this point.<br />

The full package body for stacks would be<br />

use lists;<br />

package body stacks is<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (9 of 23) [10/14/2000 12:55:42 PM]


procedure push(item:in out stack; value:in integer) is<br />

begin<br />

insert_at_head(item, value);<br />

end;<br />

procedure pop(item:in out stack; value : out integer) is<br />

begin<br />

remove_from_head(item, value);<br />

exception =><br />

when lists.underflow =><br />

raise stacks.underflow;<br />

end;<br />

-- note how the exception advertised in the other package<br />

-- is "translated" to an exception defined within this package.<br />

function full(item:stack) return boolean is<br />

begin<br />

return lists.full(list(item));<br />

end;<br />

function empty(item:stack) return boolean is<br />

begin<br />

return lists.empty(list(item));<br />

end;<br />

-- return an empty unitialized stack<br />

function init return stack is<br />

begin<br />

return stack(lists.init);<br />

end;<br />

end stacks;<br />

Creating abstractions by using generic abstractions<br />

Often a generic package that implements a suitable data structure can be used to implement another abstraction. This<br />

is very similar to the section presented previously.<br />

Assume the package generic_lists is defined as...<br />

generic<br />

type element is private;<br />

package generic_lists is<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (10 of 23) [10/14/2000 12:55:42 PM]


private<br />

end;<br />

type list is private;<br />

underflow :exception;<br />

procedure insert_at_head(item:in out list; value :in element);<br />

procedure remove_from_head(item:in out list; value:out element);<br />

procedure insert_at_tail(item:in out list; value :in element);<br />

procedure remove_from_tail(item:in out list; value: out element);<br />

function full(item:list) return boolean;<br />

function empty(item:list) return boolean;<br />

-- returns an empty initialized list<br />

function init return list;<br />

...<br />

We can instantiate this generic to create a new package that will store the integers for us. The question is "where do<br />

we want to instantiate it, such that it won't get in the way of the abstraction, or cause other developement<br />

problems?". The solution to this (as it is to a lot of these type of questions) is in the private section of the package.<br />

We can then use the generic as follows...<br />

with generic_lists;<br />

package stacks is<br />

private<br />

type stack is private;<br />

underflow :exception;<br />

procedure push(item:in out stack; value:in integer);<br />

procedure pop(item:in out stack; value : out integer);<br />

function full(item:stack) return boolean;<br />

function empty(item:stack) return boolean;<br />

-- return an empty initialized stack<br />

function init return stack;<br />

-- Instantiate the generic to create a new package.<br />

package integer_lists is new generic_lists(integer);<br />

type stack is new integer_lists.list;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (11 of 23) [10/14/2000 12:55:42 PM]


end;<br />

-- stack inherits all the operations of type list, as before.<br />

The package body is the same as previously described.<br />

Here we can enforce privacy, without changing what the type does for a client.<br />

Abstractions from composition<br />

Abstractions can be implemented by using composition (composing new abstractions by putting existing abstractions<br />

together, typically in a record) and by forwarding subprogram calls onto the appropriate component object. (This is<br />

all pretty simple stuff!).<br />

with lists; use lists;<br />

package queues is<br />

private<br />

end;<br />

type queue is private;<br />

procedure remove_from_head(item:in out queue; value :out integer);<br />

procedure add_to_tail(item:in out queue; value:integer);<br />

function full(item: queue) return boolean;<br />

function empty(item:queue) return boolean;<br />

function init return queue;<br />

-- a queue is composed from the following items...<br />

type queue is record<br />

values:list;<br />

no_of_elements:integer;<br />

end record;<br />

package body queues is<br />

procedure remove_from_head(item:in out queue; value:out integer) is<br />

begin<br />

remove_from_head(item.values, value);<br />

item.no_of_elements := item.no_of_elements - 1;<br />

end;<br />

procedure add_to_tail(item:in out queue; value:integer) is<br />

begin<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (12 of 23) [10/14/2000 12:55:42 PM]


end;<br />

add_to_tail(item.values, value);<br />

item.no_of_elements := item.no_of_elements + 1;<br />

procedure reset (item: in out queue) is ...<br />

function full(item: queue) return boolean is<br />

begin<br />

return full(item.values);<br />

end;<br />

function empty(item:queue) return boolean is<br />

begin<br />

return item.no_of_elements = 0;<br />

end;<br />

end;<br />

In this example calls on a queue are "forwarded" onto the list component, which is responsible for implementing<br />

some aspect of the queue abstraction (in this case a major part!).<br />

Abstracting common functionality - families of<br />

abstractions<br />

Both implementations of the stack described above provided a common set of routines, that is push, pop etc. In <strong>Ada</strong><br />

we can state the commonalities using a common type. The common type will describe what routines are to be<br />

provided, and their profiles, but not actually describe how to implement them. Type derived from this type are forced<br />

to implement each subprogram in its own way. This is called an abstract type. It is an abstraction abstraction!<br />

The reason we go to all of this trouble is to tell the clients of the stack that no matter what implementation they<br />

choose, they can be sure of getting at least the functionality described by the abstract type.<br />

package stacks is<br />

type stack is abstract tagged null record;<br />

-- An abstract type with no fields, and no "real" operations.<br />

-- <strong>Ada</strong> required the "tagged" description for abstract types<br />

underflow :exception;<br />

overflow :exception;<br />

procedure push(item:in out stack; value:in integer) is abstract;<br />

procedure pop(item:in out stack; value : out integer) is abstract;<br />

function full(item:stack) return boolean is abstract;<br />

function empty(item:stack) return boolean is abstract;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (13 of 23) [10/14/2000 12:55:42 PM]


end;<br />

function init return stack is abstract;<br />

Here we have made the type abstract - this package just provides a series of subprogram that must be implemented<br />

by someone else. We could also have made the type private...<br />

package stacks is<br />

private<br />

end;<br />

type stack is abstract tagged private;<br />

-- subprograms<br />

...<br />

type stack is abstract tagged null record;<br />

Either in the same package (or more typically in another package) we can extend the type, and create a real type that<br />

implements the abstract type...<br />

with lists;<br />

with stacks;<br />

package unbounded_stacks is<br />

private<br />

type bounded_stack is new stacks.stack with private;<br />

-- bounded_stack is derived from the empty stack record,<br />

-- with some extra bits added on that are described<br />

-- in the private section.<br />

procedure push(item:in out unbounded_stack; value:in integer);<br />

procedure pop(item:in out unbounded_stack; value : out integer);<br />

function full(item:unbounded_stack) return boolean;<br />

function empty(item:unbounded_stack) return boolean;<br />

-- return an empty initialized unbounded_stack<br />

function init return unbounded_stack;<br />

-- Extend the empty stacks.stack record with one<br />

-- 'more' field.<br />

-- All calls will have to be forwarded onto the<br />

-- internal linked list.<br />

type unbounded_stack is new stacks.stack with<br />

record<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (14 of 23) [10/14/2000 12:55:42 PM]


end;<br />

values:lists.list;<br />

end record;<br />

As described in the section on composition, you will have to forward calls onto the linked list within the<br />

unbounded_stack. The package body would therefore be...<br />

package body unbounded_stacks is<br />

procedure push(item:in out unbounded_stack; value:in integer) is<br />

begin<br />

lists.insert_at_head(item.values, value);<br />

end;<br />

procedure pop(item:in out unbounded_stack; value : out integer) is<br />

begin<br />

lists.remove_from_head(item.values, value);<br />

exception<br />

when lists.underflow =><br />

raise stacks.underflow;<br />

end;<br />

...<br />

end unbounded_stacks;<br />

Packaging<br />

In <strong>Ada</strong>, types and packages are almost completely independent of each other. This allows you to devise a solution to<br />

a problem in terms of types and operations, and later decide on how these parts should be split into pieces to meet the<br />

software engineering requirements of ease of modification, encapsulation, modularisation, reduced recompilation<br />

costs, namespace management etc.<br />

This has been demonstrated already, in the choice to hide implementation details in the various packages above.<br />

However <strong>Ada</strong> also provides further choices that can be made in this regard.<br />

Namespace management<br />

In language such as C, there is only one namespace. There is no hierachy of names; no ability to 'hide' or qualify a<br />

name. A good analogy is the first version of the Mac OS, which had no directories. All filenames had to be different,<br />

and could not conflict with predefined system filenames. The solution typically adopted in C to resolve this problem<br />

is to prepend some form of semantic information in the name. For example all posix thread routines are prepended<br />

with 'pthread_'.<br />

<strong>Ada</strong>83 solved this problem by allowing packages which contain their own namespace, in which locally defined<br />

names don't conflict with other names (the analogy is for one level of subdirectories (actually not quite accurate, as a<br />

package can contain other entities...)).<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (15 of 23) [10/14/2000 12:55:42 PM]


<strong>Ada</strong>95 however expands the namespace concept with the inclusion of child packages to allow for truly hierachic<br />

namespaces. Child packages are typically used to model some form of hierachy present in the problem domain. For<br />

example a unix binding may implement packages<br />

unix -- can be an empty "place holder" package<br />

unix.file -- file routines<br />

unix.process -- process operations<br />

Alternatively the package hierachy can be used to reflect the inhertiance hierachy of an object oriented type. Note<br />

however the package hierachy does not define the inhertiance hierachy - the type declarations do that.<br />

The example given earlier of an abstract stack, and a concrete implementation of an unbounded stack is a good<br />

example of how we could have chosen a different organisation, without affecting the meaning of the abstraction.<br />

with stacks; use stacks;<br />

with lists;<br />

package stacks.unbounded is<br />

private<br />

end;<br />

type unbounded_stack is new stack with private;<br />

-- partial view of the bounded_stack<br />

-- override the primitive operations with concrete services<br />

procedure push(item:in out bounded_stack; value:in integer);<br />

procedure pop(item:in out bounded_stack; value : out integer);<br />

function full(item:bounded_stack) return boolean;<br />

function empty(item:bounded_stack) return boolean;<br />

function init return unbounded_stack;<br />

...<br />

We could create another type, bounded_stack, and decide to put it into the package stacks.bounded.<br />

package stacks.bounded is<br />

type bounded_stack is new stacks.stack with private;<br />

...<br />

end;<br />

Handling dynamic structures properly<br />

As discussed earlier in the implementation of a stack with a linked list, making a copy of such a type through an<br />

assignment statement doesn't quite work. The problem is that copying often only copies a pointer to the head of a list<br />

(or tree) and does not copy the entire list. Another problem is that the space allocated to a linked list is often not<br />

recovered when an object goes out of scope.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (16 of 23) [10/14/2000 12:55:42 PM]


An example which highlights these problems is...<br />

with lists; use lists;<br />

procedure bad_voodoo is<br />

begin<br />

end;<br />

a, b:list;<br />

result :integer;<br />

procedure loose_memory is<br />

c :list;<br />

begin<br />

insert_at_head(c, 1);<br />

insert_at_head(c, 2);<br />

end; -- when you get to here, c goes out of scope<br />

-- and you lose the memory associated with the nodes!<br />

insert_at_head(a, 1);<br />

insert_at_head(a, 2);<br />

insert_at_head(b, 3);<br />

insert_at_head(b, 4);<br />

b := a;<br />

-- b and a now point to the same list, and all of b's nodes<br />

-- have been lost!<br />

remove_from_tail(a, result);<br />

-- affects both a and b<br />

In <strong>Ada</strong>95 you can solve these problems by providing special routines that are "automagically" called when a values<br />

are being copied, when they go out of scope, or when they are initialized. For this to happen, the object in question<br />

must be derived from a special type called Controlled, defined in package <strong>Ada</strong>.Finalization.<br />

The three routines automagically called are called Initialize, Adjust and Finalize. They are called in the following<br />

contexts...<br />

with lists; use lists;<br />

procedure demo is<br />

a :list; -- Initialize(a);<br />

b :list := Init; -- Initialize not called<br />

begin<br />

insert_at_head(a, 1);<br />

insert_at_head(a, 2);<br />

insert_at_head(b, 3);<br />

insert_at_head(b, 4);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (17 of 23) [10/14/2000 12:55:42 PM]


a --> | 2 |-->| 1 |--> null<br />

b --> | 4 |-->| 3 |--> null<br />

b := a;<br />

------------------------------------------<br />

Finalize(b);<br />

Free b's nodes, before it is overwritten<br />

a --> | 2 |-->| 1 |--> null<br />

b --> null<br />

Copy a to b.<br />

Now they _both_ point at the same list<br />

a --> | 2 |-->| 1 |--> null<br />

b ----^<br />

Adjust(b);<br />

b has to duplicate the list it currently<br />

points at, at then point at the new list.<br />

a --> | 2 |-->| 1 |--> null<br />

b --> | 2 |-->| 1 |--> null<br />

------------------------------------------<br />

end; Finalize(a), Finalize(b).<br />

Delete the memory associated with both a and b.<br />

a --> null<br />

b --> null<br />

Here we finally give the full details for package List. The code for it looks like...<br />

with <strong>Ada</strong>.Finalization;<br />

package lists is<br />

type List is private;<br />

underflow :exception;<br />

procedure insert_at_head(item:in out list; value :in integer);<br />

procedure remove_from_head(item:in out list; value:out integer);<br />

procedure insert_at_tail(item:in out list; value :in integer);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (18 of 23) [10/14/2000 12:55:42 PM]


private<br />

end;<br />

procedure remove_from_tail(item:in out list; value: out integer);<br />

function full(item:list) return boolean;<br />

function empty(item:list) return boolean;<br />

-- returns an empty initialized list<br />

function init return list;<br />

-- Normal stuff for the list<br />

type Node;<br />

type Ptr is access Node;<br />

type Node is record<br />

Value :Integer;<br />

Next :ptr;<br />

end record;<br />

-- Only the head of the list is special...<br />

type List is new <strong>Ada</strong>.Finalization.Controlled with<br />

record<br />

Head :Ptr;<br />

end record;<br />

-- No need for Initialize (pointers are automatically set to null)<br />

-- procedure Initialize(item:in out list);<br />

procedure Adjust(item:in out list);<br />

procedure Finalize(item:in out list);<br />

Note that the routines were declared in the private section. This prevents clients from calling them when it is<br />

inappropriate.<br />

The package body would be implemented as follows...<br />

with unchecked_deallocation;<br />

package body lists is<br />

-- routine for deallocating nodes<br />

procedure free is new unchecked_deallocation(node,ptr);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (19 of 23) [10/14/2000 12:55:42 PM]


------------------------------------------------------------<br />

-- Implement all the other stuff (insert_at_head, remove_from_head...)<br />

...<br />

--------------------------------------------------------------<br />

-- given a ptr to a list, this will allocate a new<br />

-- identical list<br />

function Copy_List(item:ptr) return ptr is<br />

begin<br />

end;<br />

if item = null then<br />

return null;<br />

else<br />

return new node'(item.value,Copy_List(item.next));<br />

end if;<br />

------------------------------------------------------------<br />

-- In the assignment b := a, b has just been<br />

-- overwritten by the contents of a. For a linked<br />

-- list this means that both a and b point at the<br />

-- same object. Now have to make a physical copy<br />

-- of the items pointed at by b, and replace the head<br />

-- ptr with a pointer to the copy just made<br />

procedure Adjust(item:in out list) is<br />

begin<br />

end;<br />

item.head := Copy_List(item.head);<br />

------------------------------------------------------------<br />

-- delete all the memory in the about to be<br />

-- destoyed item<br />

procedure Finalize(item:in out list) is<br />

upto:ptr := item.head;<br />

temp :ptr;<br />

begin<br />

while upto /= null loop<br />

temp := upto;<br />

upto := upto.next;<br />

free(temp);<br />

end loop;<br />

end;<br />

end;<br />

item.head := null;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (20 of 23) [10/14/2000 12:55:42 PM]


The use of controlled types gives a good degree of control over dynamic objects. It simplifies the life of a client of<br />

the type as it relieves them of the worry of having to manage the types memory. It also allows for easy substitution<br />

of dynamic and non dynamic solutions to problems.<br />

Typically once the 3 routines (initialize, adjust, finalize) have been written, the rest of the services can be written<br />

without regard to these features.<br />

Enforcing initialization<br />

In C++ you can enforce the initialization of all objects by supplying a class with a constructor, which is called when<br />

an object is created. The same effect in <strong>Ada</strong> is achieved by assigning a value the result of calling a function.<br />

For example<br />

package stacks is<br />

type stack is tagged private;<br />

private<br />

end;<br />

function init return stack;<br />

-- other operations<br />

...<br />

type list is array(1..10) of integer;<br />

type stack is record<br />

values :list;<br />

top :integer range 0..10;<br />

end record;<br />

with stacks; use stacks;<br />

procedure main is<br />

a : stack := init;<br />

b : stack;<br />

begin<br />

null;<br />

end;<br />

In this example the object a is properly initialized to represent an empty stack. You can only use functions provided<br />

in the package; you can't set it any other way, because the type is private. However as can be seen, the object b has<br />

not been initialized. The language has not enforced an initialization requirement.<br />

The only way to do this is through the use of record discriminants. The partial view of the type is declared to have<br />

discriminants, even if the full view does not. As the number of fields that a record has may depend on a discriminant,<br />

and because <strong>Ada</strong> likes constrained objects (one's whose size is known) the combination of discriminant and private<br />

type causes the compiler to enforce all objects of the type to be initialized.<br />

package stacks is<br />

type stack() is private;<br />

-- declare that stack _may_ have discriminant...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (21 of 23) [10/14/2000 12:55:42 PM]


private<br />

end;<br />

function init return stack;<br />

-- other operations<br />

...<br />

type list is array(1..10) of integer;<br />

-- ...even if you have no intention of it having such<br />

type stack is record<br />

values :list;<br />

top :integer range 0..10;<br />

end record;<br />

with stacks; use stacks;<br />

procedure main is<br />

a : stack := init;<br />

b : stack;<br />

-- now this is illegal. You _must_ call a function<br />

-- to initialize it.<br />

begin<br />

null;<br />

end;<br />

This is all quite non intuitive, and rather counter to the <strong>Ada</strong> notion of readability (IMHO). Certainly C++ manages<br />

this is a more programmer friendly way.<br />

Mutually Recursive types<br />

Sometimes there is a need for two (or more) types that are mutually recursive, that is they have pointers that point at<br />

each other. An example is a doctor and a patient, who wish to maintain pointers at each other. Unless you wish to<br />

place them together in the same package, <strong>Ada</strong> does not properly support you. The best you can do if you want to<br />

retain separate compilation, is to define two base types, then join them together later in other packages.<br />

package doctors_and_patients is<br />

type doctor is abstract tagged null record;<br />

type doctor_ptr is access all doctor'class;<br />

end;<br />

type patient is abstract tagged null record;<br />

type patient_ptr is access all patient'class;<br />

with doctors_and_patients; use doctors_and_patients;<br />

package doctors is<br />

type doc is new doctor with private;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (22 of 23) [10/14/2000 12:55:42 PM]


private<br />

end;<br />

-- subprograms<br />

...<br />

type doc is new doctor with<br />

record<br />

pat:patient_ptr;<br />

...<br />

end record;<br />

-- similarly for patient type<br />

If you can forsee all operations required, or at least those required by each other, you can place them in package<br />

doctors_and_patients. Futher operations can be added along with subsequent type declarations (for example type<br />

doc).<br />

It's probably worth point out here that package bodies can see into package specs, so...<br />

with doctors; use doctors;<br />

package body patients is<br />

-- The patients body can access the doctor's services<br />

-- declared in the doctor's spec<br />

...<br />

end patients;<br />

with patients; use patients;<br />

package body doctors is<br />

end doctors;<br />

to the index...<br />

-- The doctors body can access the patients services<br />

-- declared in the patient's spec<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms.html (23 of 23) [10/14/2000 12:55:42 PM]


1.<br />

17 Idioms<br />

1.<br />

2.<br />

3.<br />

4.<br />

5.<br />

6.<br />

7.<br />

8.<br />

9.<br />

10.<br />

11.<br />

12.<br />

13.<br />

14.<br />

15.<br />

to the index...<br />

Overview<br />

Introduction<br />

Abstraction<br />

Creating abstractions from other abstractions (code reuse)<br />

Private inheritance<br />

Another example<br />

So far so good. How do I write the function full?<br />

Creating abstractions by using generic abstractions<br />

Abstractions from composition<br />

Abstracting common functionality - families of abstractions<br />

Packaging<br />

Namespace management<br />

Handling dynamic structures properly<br />

Enforcing initialization<br />

Mutually Recursive types<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/17_idioms_ToC.html [10/14/2000 12:55:43 PM]


18 Packages and program design<br />

When a program starts to get large it needs to broken up into smaller pieces, to make it easier to understand, distribute the<br />

work, quicker to fix, isolate changes etc.<br />

But how to do this? Typical introductory programming courses teach programming with a large main procedure, and<br />

perhaps a couple of packages.<br />

So how do you progress from this initial stage to a more mature view of system developement? One way is to show a simple<br />

mapping (or transformation) of a program to a functionally equivalent program with a number of packages.<br />

The following attempts to show how an existing <strong>Ada</strong> program may be broken up into smaller pieces.<br />

Consider the following program...<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

with <strong>Ada</strong>.Integer_Text_IO; use <strong>Ada</strong>.Integer_Text_IO;<br />

procedure Main is<br />

-- constants-------------------------------------<br />

Max_Bookings : constant := 20;<br />

Max_Name_Length : constant := 40;<br />

No_Cars : constant := 10;<br />

-- types-----------------------------------------<br />

subtype Name_Array is String(1..Max_Name_Length);<br />

subtype Registration is String(1..6);<br />

type Date is<br />

record<br />

Day : Positive;<br />

Month : Positive;<br />

Year : Positive;<br />

end record;<br />

type Booking is<br />

record<br />

On : Date;<br />

Name : Name_Array;<br />

Car : Registration;<br />

end record;<br />

type Booking_Array is array (1..Max_Bookings) of Booking;<br />

-- Variables------------------------------------<br />

Bookings : Booking_Array;<br />

...other variables...<br />

-- Procedures & Functions-----------------------<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (1 of 14) [10/14/2000 12:55:48 PM]


-- Part of booking system for after a date<br />

function After(A, B : Date) return Boolean is...<br />

-- display a date in the format dd/mm/yy<br />

procedure Display(Item : in Date) is...<br />

procedure Search_For_Empty_Booking(<br />

Bookings : in out Booking_Array;<br />

On : in Date;<br />

Position : out Positive) is...<br />

procedure Make_Booking(<br />

Bookings : in out Booking_Array;<br />

On : in Date;<br />

Name : in Name_Array;<br />

Success : out Boolean) is...<br />

procedure Menu( Option : out positive) is...<br />

begin<br />

loop<br />

...<br />

end;<br />

Figure 1. Sample program.<br />

Here the code makes use of different types, and has numerous procedures. We can imagine, for example, that the procedure<br />

Make_Booking calls Search_For_Empty_Booking, which in turn calls the function After to check if the requested booking<br />

is after another booking.<br />

When we look at the code there does not seem to be much cohesion - declarations of types, constants and subprograms are<br />

in their own little sections. This is one way to split a program up into pieces, but it is not a very good way.<br />

Another way to split it up is to note that we can see that somethings "belong" together. For example the type date is closely<br />

related to the function After and procedure Display. What we can do is group these routines more closely together.<br />

(Imagine that there are others programs being developed, and that they also had a need of these routines. It would be silly to<br />

have programmers recode these routines. Reuse of the code would be cheaper - but it is not easy in this situation. If someone<br />

copies the code by doing "cut and paste" then any bug fixes made to the date routines will only occur in one program. It<br />

would be better if we made these routines available in one place for all programs).<br />

The first thing we should think about is grouping these items together...<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (2 of 14) [10/14/2000 12:55:48 PM]


Figure 2. Like types and procedures grouped together.<br />

This does not change how the program runs, only how it is layed out.<br />

You can see that the bookings require the definition of a date to be available, in fact they have to preceede the definition of<br />

the booking. The date definitions do not require any previous declarations.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (3 of 14) [10/14/2000 12:55:48 PM]


Figure 3. Overview of program structure.<br />

From this we can see that it is possible to take these two sections out, and place them into separate files. However we have<br />

to be careful to maintain Bookings visibility of the Date declarations. Likewise the main program has to be able to "see" all<br />

of the booking declarations, and all of the date declarations.<br />

The bookings section only has to be able to "see" the date declarations.<br />

The date section doesn't have to be able to "see" any other declarations.<br />

We are now in a position to place them into separate packages...<br />

Figure 4. Date package<br />

Note that instead of writing out the subprograms in full, we just write them as specifications. The full description of them<br />

(with all the code) is placed in the package body.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (4 of 14) [10/14/2000 12:55:48 PM]


Figure 5. Bookings package<br />

The main procedure can now be...<br />

with Dates; use Dates;<br />

with Bookings; use Bookings;<br />

with <strong>Ada</strong>.Text_IO; use <strong>Ada</strong>.Text_IO;<br />

with <strong>Ada</strong>.Integer_Text_IO; use <strong>Ada</strong>.Integer_Text_IO;<br />

procedure Main is<br />

Bookings : Booking_Array;<br />

--Procedures-not-easily-classified--------------procedure<br />

Menu( Option : out positive) is...<br />

begin<br />

loop<br />

...<br />

end;<br />

Figure 6. Revised program.<br />

When this program is compiled and run, it will execute exactly the same as the very first example. All that is happened is we<br />

have moved code into different places, so as to make it easier to write and maintain. These are software engineering, or<br />

program construction concerns, not issues relating to how the program runs.<br />

However in separating items out, we have had to be very aware of what declarations are dependent on what other<br />

declarations. If we had dates declared after bookings, bookings would not compile. This is called the dependency<br />

relationship and it is a very useful piece of information that tells us a lot about how a program is constructed.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (5 of 14) [10/14/2000 12:55:48 PM]


For example if we found out that a routine in the date package was incorrect, we would need to look at all the routines that<br />

had something to do with dates to see if they were affected, and should also be fixed. In the first example, this may not have<br />

been easy. With the dependency relationships explicity described, it makes it very easy to search through large programs<br />

and find what may be affected by a bug and what isn't.<br />

Also when a maintenance programmer has to change a program, they always have to be aware of the ripple effect - the<br />

possibility that a change in one part of a program may have consequences in other parts that are not anticipated. Dependency<br />

relationships (which are explicitly stated in the with clause) help the programmer understand how a large program is pieced<br />

together.<br />

A "Layered" view of the program<br />

When we think about the dependency relationships in a system, we can see that some packages don't have any<br />

dependencies. Others depend on one or two other packages, and some depend on many. In general we can depict this in a<br />

layered diagram...<br />

Here the procedure main is in the hightest layer, and it depends on services provided by lower layer packages. An item at a<br />

lower layer however, never depends on higher layer items. Designing systems in layers makes the process of abstraction,<br />

and the notion of providing services a fairly natural one.<br />

Interestingly by turning the diagram upside down, you get the same program strucutre as in Figure 2.<br />

Look at putting the pieces into package specs...<br />

When a function such as<br />

-- Part of booking system for after a date<br />

function After(A, B : Date) return Boolean;<br />

is called from, say, procedure<br />

procedure Make_Booking(<br />

Bookings : in out Booking_Array;<br />

On : in Date;<br />

Name : in Name_Array;<br />

Success : out Boolean);<br />

the procedure doesn't really care how the function is written - what internal variables it has, or the order of if statements etc,<br />

so long as it produces the correct result. All it really cares about is giving two dates, and getting a boolean result. It only<br />

really depends on the specification of the function, and not the body of it.<br />

<strong>Ada</strong> enforces this distinction when we put things into a package, by allowing only subprogram specifications in the pacakge<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (6 of 14) [10/14/2000 12:55:48 PM]


spec, and the full subprogram in the package body.<br />

Look at putting pieces into a package body<br />

When we examine our main program, we may find that subprograms such as<br />

procedure Search_For_Empty_Booking(<br />

Bookings : in out Booking_Array;<br />

On : in Date;<br />

Position : out Positive);<br />

are never called from any routine other than the other booking related subprograms. For this reason there is not much point<br />

in making it publically available. We can place it soley in the package body for bookings, and not have any adverse impact<br />

on the system at all.<br />

Designing programs with Direct_IO/Sequential_IO<br />

Many students desiging programs with Direct_IO, or indeed any generic package, often have trouble figuring out where the<br />

instantiation should be placed.<br />

Generally a program can be structured using child packages; the definition of an item is placed in one package, and child<br />

packages are created to contain the I/O facilities for it.<br />

Consider, for example...<br />

---------------package<br />

Blahs is<br />

type Blah is...<br />

end;<br />

If we want a package to perform I/O on this type we can instantiate direct_io as a child package...<br />

---------------with<br />

<strong>Ada</strong>.Direct_IO;<br />

with Blahs;<br />

package Blahs.Direct_IO is new <strong>Ada</strong>.Direct_IO(Blahs.Blah);<br />

However direct_io (and sequential_io) are extremely low level packages. They offer very little in terms of the functionality<br />

you would really like to have, such as the ability to search for an item, or even delete an item.<br />

If you want to write and retrieve binary data to a file, consider the Direct_IO generic as simply a building block, used to<br />

create more sophisticated services.<br />

(We can make an analogy between direct_io and arrays. Both are very low level concepts, and are generally used to<br />

construct higher level concepts such as hash tables).<br />

In the example below, we create a higher level file abstraction, that supports searching and deletion of items in the file.<br />

---------------with<br />

Keys; use Keys;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (7 of 14) [10/14/2000 12:55:48 PM]


package Blahs is<br />

type Blah is...<br />

end;<br />

-- routines to help "find" records<br />

function "="(Left : Blah; Right : Key_Type) return Boolean;<br />

function "="(Left : Key_Type; Right : Blah) return Boolean;<br />

---------------with<br />

<strong>Ada</strong>.Direct_IO;<br />

package Blahs.IO is<br />

type File_Type is limited private;<br />

private<br />

end;<br />

-------------------------------------------------------<br />

-- Open a file in r/w mode (you may decide to include a<br />

-- mode parameter). Also create the file if needed.<br />

procedure Open(<br />

File : in out File_Type;<br />

Name : in String);<br />

----------------------------------------procedure<br />

Close(File : in out File_Type);<br />

-- build in support for "deleted" cells in the file<br />

type Component is<br />

record<br />

Item : Blah;<br />

Deleted : Boolean := true;<br />

end record;<br />

package Blahs_Direct_IO is new <strong>Ada</strong>.Direct_IO(Component);<br />

-- provide a shorter renaming for the package<br />

package BDIO renames Blahs_Direct_IO;<br />

-- Completion of the private type advertised above<br />

type File_Type is new BDIO.File_Type;<br />

At this point, we have a file type that can be used in further child packages to build several different I/O facilities. The<br />

package has been instantiated in the private section for two reasons.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (8 of 14) [10/14/2000 12:55:48 PM]


1. It prevents packages outside the Blahs.IO hierachy from accessing the low level routines in Blahs_Direct_IO, that are of<br />

no concern to them. We can force them to use the high level routines we will provide.<br />

2 It allows child packages to 'see' the Blahs_Direct_IO package, and therefore to be able to call on these routines.<br />

The package body would look like...<br />

package body Blahs.IO is<br />

end;<br />

-------------------------------------------------------<br />

-- Forward the call to the blah_direct_io package<br />

procedure Open(<br />

File : in out File_Type;<br />

Name : in String) is<br />

begin<br />

BDIO.Open(<br />

File => BDIO.File_Type(File),<br />

Mode => BDIO.inout_file,<br />

Name => Name);<br />

exception<br />

when BDIO.Name_Error =><br />

BDIO.Create(<br />

File => BDIO.File_Type(File),<br />

Mode => BDIO.inout_file,<br />

Name => Name);<br />

end;<br />

----------------------------------------procedure<br />

Close(File : in out File_Type) is<br />

begin<br />

BDIO.Close( BDIO.File_Type(File));<br />

end;<br />

For example you may want to produce a package with facilities for reading, deleting, searching etc, while another package<br />

could be used to consolidate a file.<br />

------------------------------------with<br />

Keys; use Keys;<br />

package Blahs.IO.Advanced_Features is<br />

procedure Read(<br />

File : in out File_Type;<br />

Key : in Key_Type;<br />

Item : out Blah;<br />

Found : out Boolean);<br />

procedure Delete(<br />

File : in out File_Type;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (9 of 14) [10/14/2000 12:55:48 PM]


end;<br />

etc.<br />

Key : in Key_Type;<br />

Item : out Blah);<br />

------------------------------------package<br />

Blahs.IO.Utility_Routines is<br />

end;<br />

-- Rewrite the file to remove empty cells.<br />

-- This presumes that free space is not managed in some<br />

-- more sophisticated manner<br />

procedure Compact_File(File : in out File_Type);<br />

The package body for these packages can be roughly sketched out as follows. Note that it makes a simplifying assumption<br />

as to how it searches for a key value.<br />

------------------------------------with<br />

Keys; use Keys;<br />

package body Blahs.IO.Advanced_Features is<br />

-- Call on the Read facility from package BDIO<br />

procedure Read(<br />

File : in out File_Type;<br />

Key : in Key_Type;<br />

Item : out Blah;<br />

Found : out Boolean) is<br />

Data : Component;<br />

use BDIO;<br />

begin<br />

for i in 1..BDIO.Size(BDIO.File_Type(File)) loop<br />

BDIO.Read(BDIO.File_Type(File), Data);<br />

-- use the "=" operator defined in Blahs to<br />

-- compare what we have read<br />

if (not Data.Deleted) and then<br />

(Data.Item = Key) then<br />

Found := True;<br />

Item := Data.Item;<br />

return;<br />

end if;<br />

end loop;<br />

-- Searched-for data not found.<br />

return False;<br />

end;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (10 of 14) [10/14/2000 12:55:48 PM]


end;<br />

etc.<br />

It is informative to note that if we examine the non private interfaces in this package hierachy, we will see no references to<br />

the generic Direct_IO at all.<br />

---------------package<br />

Blahs is<br />

type Blah is...<br />

end;<br />

---------------package<br />

Blahs.IO is<br />

type File_Type is limited private;<br />

procedure Open(...<br />

procedure Close(...<br />

end<br />

---------------package<br />

Blahs.IO.Advanced_Features is<br />

procedure Read(...<br />

procedure Delete(...<br />

end;<br />

---------------package<br />

Blahs.IO.Utility_Routines is<br />

procedure Compact_File(...<br />

end;<br />

To use these routines you may have code as follows...<br />

with Keys; use Keys;<br />

with Blahs; use Blahs;<br />

with Blahs.IO; use Blahs.IO;<br />

with Blahs.IO.Advanced_Features;use Blahs.IO.Advanced_Features;<br />

procedure Demo is<br />

Data : Blah;<br />

File : Blahs.IO.File_Type;<br />

Key : Key_Type;<br />

Found : Boolean;<br />

begin<br />

-- Read some data from the user..<br />

get(Key);<br />

Open(File, "my_file.dat");<br />

Read(File, Key, Data, Found);<br />

if Found then<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (11 of 14) [10/14/2000 12:55:48 PM]


-- do whatever<br />

else<br />

-- do whatever<br />

end if;<br />

end;<br />

These child packages can also be written as generic packages, so that this structure can be replicated for different file types.<br />

Private packages<br />

When we develop systems we find that new issues appear as we tackle larger and larger programs. Initially splitting a<br />

program into subprograms makes developement easier. We can place variables that are only used by one subprogram inside<br />

it - hiding it from outside view and interference.<br />

As the number of subprograms grows, however, we find a need to split them up into another level of grouping - the package.<br />

This gives us the opportunity to hide entire subprograms inside package bodies - subprograms that are not meant for other<br />

routines to use.<br />

After the number of packages starts to grow, we turn to child packages to create subsystems, in which a family of logically<br />

related types and functionality is packaged together. Each subsystem provides services to all clients by advertising them in<br />

it's client specifications. Once again however, we find at this high level of abstraction some of the services that are offered<br />

should only be available to those within the subsystem.<br />

Private packages allow you to structure your program with local packages, and prevent offering services to anyone who<br />

wants them. Banks are, of course, more secure if the internal procedures they make use of to deliver services to customers,<br />

are not made available to those same clients. We definately want the same level of security for our subsystems!<br />

How do I know when to use private packages?<br />

Armies during war time generally run on a "needs to know" basis. You only tell people what they need to know. Similarly<br />

when you progressed from to each new level in the diagram above, you dealt with the issue of what to hide on a "needs to<br />

know" basis. If a client doesn't need to know the details of a service then they are hidden from view. This split is based on<br />

the difference between what service is offered, and how it is implemented. You simply need to apply the same knowledge,<br />

only at a higher level.<br />

This may come about from a hierachical object decomposition, where the services provided at a high level analysis provide<br />

the subsystem level interfaces. Further elaboration of the design results in objects which may only be needed to implement<br />

the subsystem services already offered.<br />

A case study<br />

A temporal assertions package (used for making assertions such as "this event must happen within 5 seconds of that event",<br />

or "this event must never occur before that event") has been developed at <strong>RMIT</strong>. Several concepts emerged from the<br />

analysis of the requirements.<br />

Events Something that occurs at an instant in time<br />

Intervals A duration, marked by a beginning and end event<br />

Predicates A statement involving events, intervals, boolean<br />

connectives (and, or, not, xor) and special<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (12 of 14) [10/14/2000 12:55:48 PM]


conditions, such a "must occur before", and<br />

"must occur at least once"<br />

TriState Logic<br />

A special form of boolean logic consisting of three<br />

states (false, true, don't know)<br />

As well as these types/concepts discovered at the analysis stage, other types were found at the design stage, such as data<br />

structures to hold the events and predicates that would be declared.<br />

The packages developed for this were...<br />

package Assertion<br />

declared major types<br />

private package Assertion.Events_Table<br />

maintains data structure for storing events<br />

package Tri_State<br />

3 valued boolean logic<br />

Assertion contains most of the code of the program. Package Tri_State, although not involved in any other part of the<br />

system, was felt to be a useful sort of package, and was therefore not declared private.<br />

Package Assertion.Events_Table maintains the data structure for storing the events that clients have declared. As an<br />

alternative it could have been included in the package body of Assertion in a number of different ways.<br />

One technique would be to dump all of the code, data structures and variables in the package body. This would not be<br />

satisfactory as it would make the package body more cluttered.<br />

Another technique would be to place a package inside the package body...<br />

package body Assertion is<br />

-------------------------------package<br />

Events_Table is<br />

type Event_Table is private;<br />

procedure Insert(<br />

Into : in out Event_Table;<br />

Item : in Event);<br />

...<br />

end Events;<br />

package body Events_Table is<br />

procedure Insert(....) is<br />

....<br />

end;<br />

--------------------------------<br />

Stored_Events : Events_Table.Event_Table;<br />

-- rest of Assertion package<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (13 of 14) [10/14/2000 12:55:48 PM]


end Assertion;<br />

Although it fixes the problem of cluttering, it still causes the package to be longer than it needs to be (increased compilation<br />

times), harder to develop (it is harder to make calls on it). As well the structure of the program is harder to understand (the<br />

program structure is easiest to see when we can easily see the packages that make up the program).<br />

For this reason the package was made a private child package of Assertion.<br />

A package because<br />

We need to hide away the low level details of how the table is implemented<br />

A child package because<br />

It needs to see the declarations of type Event in the spec of Assertion<br />

It's name clearly links it into the Assertion subsystem of a program<br />

A Private child package because<br />

No other part of a program, apart from the Assertion subsystem, needs to know the internal details of how events are stored<br />

away.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs.html (14 of 14) [10/14/2000 12:55:48 PM]


1.<br />

2.<br />

3.<br />

4.<br />

18 Packages and program design<br />

A "Layered" view of the program<br />

Designing programs with Direct_IO/Sequential_IO<br />

Private packages<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/18_designs_ToC.html [10/14/2000 12:55:49 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

Appendix A Text_IO package<br />

with <strong>Ada</strong>.IO_Exceptions;<br />

with System;<br />

with System.Parameters;<br />

package <strong>Ada</strong>.Text_IO is<br />

type File_Type is limited private;<br />

type File_Mode is (In_File, Out_File, Append_File);<br />

type Count is range 0 .. System.Parameters.Count_Max;<br />

subtype Positive_Count is Count range 1 .. Count'Last;<br />

Unbounded : constant Count := 0;<br />

-- Line and page length<br />

subtype Field is Integer range 0 .. System.Parameters.Field_Max;<br />

subtype Number_Base is Integer range 2 .. 16;<br />

type Type_Set is (Lower_Case, Upper_Case);<br />

---------------------<br />

-- File Management --<br />

---------------------<br />

procedure Create<br />

(File : in out File_Type;<br />

Mode : in File_Mode := Out_File;<br />

Name : in String := "";<br />

Form : in String := "");<br />

procedure Open<br />

(File : in out File_Type;<br />

Mode : in File_Mode;<br />

Name : in String;<br />

Form : in String := "");<br />

procedure Close (File : in out File_Type);<br />

procedure Delete (File : in out File_Type);<br />

procedure Reset (File : in out File_Type; Mode : in File_Mode);<br />

procedure Reset (File : in out File_Type);<br />

function Mode (File : in File_Type) return File_Mode;<br />

function Name (File : in File_Type) return String;<br />

function Form (File : in File_Type) return String;<br />

function Is_Open (File : in File_Type) return Boolean;<br />

------------------------------------------------------<br />

-- Control of default input, output and error files --<br />

------------------------------------------------------<br />

procedure Set_Input (File : in File_Type);<br />

procedure Set_Output (File : in File_Type);<br />

procedure Set_Error (File : in File_Type);<br />

function Standard_Input return File_Type;<br />

function Standard_Output return File_Type;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (1 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

function Standard_Error return File_Type;<br />

function Current_Input return File_Type;<br />

function Current_Output return File_Type;<br />

function Current_Error return File_Type;<br />

type File_Access is access constant File_Type;<br />

function Standard_Input return File_Access;<br />

function Standard_Output return File_Access;<br />

function Standard_Error return File_Access;<br />

function Current_Input return File_Access;<br />

function Current_Output return File_Access;<br />

function Current_Error return File_Access;<br />

--------------------<br />

-- Buffer control --<br />

--------------------<br />

procedure Flush (File : in out File_Type);<br />

procedure Flush;<br />

--------------------------------------------<br />

-- Specification of line and page lengths --<br />

--------------------------------------------<br />

procedure Set_Line_Length (File : in File_Type; To : in Count);<br />

procedure Set_Line_Length (To : in Count);<br />

procedure Set_Page_Length (File : in File_Type; To : in Count);<br />

procedure Set_Page_Length (To : in Count);<br />

function Line_Length (File : in File_Type) return Count;<br />

function Line_Length return Count;<br />

function Page_Length (File : in File_Type) return Count;<br />

function Page_Length return Count;<br />

------------------------------------<br />

-- Column, Line, and Page Control --<br />

------------------------------------<br />

procedure New_Line (File : in File_Type; Spacing : in<br />

Positive_Count := 1);<br />

procedure New_Line (Spacing : in Positive_Count := 1);<br />

procedure Skip_Line (File : in File_Type; Spacing : in<br />

Positive_Count := 1);<br />

procedure Skip_Line (Spacing : in Positive_Count := 1);<br />

function End_Of_Line (File : in File_Type) return Boolean;<br />

function End_Of_Line return Boolean;<br />

procedure New_Page (File : in File_Type);<br />

procedure New_Page;<br />

procedure Skip_Page (File : in File_Type);<br />

procedure Skip_Page;<br />

function End_Of_Page (File : in File_Type) return Boolean;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (2 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

function End_Of_Page return Boolean;<br />

function End_Of_File (File : in File_Type) return Boolean;<br />

function End_Of_File return Boolean;<br />

procedure Set_Col (File : in File_Type; To : in<br />

Positive_Count);<br />

procedure Set_Col (To : in Positive_Count);<br />

procedure Set_Line (File : in File_Type; To : in<br />

Positive_Count);<br />

procedure Set_Line (To : in Positive_Count);<br />

function Col (File : in File_Type) return Positive_Count;<br />

function Col return Positive_Count;<br />

function Line (File : in File_Type) return Positive_Count;<br />

function Line return Positive_Count;<br />

function Page (File : in File_Type) return Positive_Count;<br />

function Page return Positive_Count;<br />

-----------------------------<br />

-- Characters Input-Output --<br />

-----------------------------<br />

procedure Get (File : in File_Type; Item : out Character);<br />

procedure Get (Item : out Character);<br />

procedure Put (File : in File_Type; Item : in Character);<br />

procedure Put (Item : in Character);<br />

procedure Look_Ahead<br />

(File : in File_Type;<br />

Item : out Character;<br />

End_Of_Line : out Boolean);<br />

procedure Look_Ahead<br />

(Item : out Character;<br />

End_Of_Line : out Boolean);<br />

procedure Get_Immediate<br />

(File : in File_Type;<br />

Item : out Character);<br />

procedure Get_Immediate<br />

(Item : out Character);<br />

procedure Get_Immediate<br />

(File : in File_Type;<br />

Item : out Character;<br />

Available : out Boolean);<br />

procedure Get_Immediate<br />

(Item : out Character;<br />

Available : out Boolean);<br />

--------------------------<br />

-- Strings Input-Output --<br />

--------------------------<br />

procedure Get (File : in File_Type; Item : out String);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (3 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

procedure Get (Item : out String);<br />

procedure Put (File : in File_Type; Item : in String);<br />

procedure Put (Item : in String);<br />

procedure Get_Line<br />

(File : in File_Type;<br />

Item : out String;<br />

Last : out Natural);<br />

procedure Get_Line<br />

(Item : out String;<br />

Last : out Natural);<br />

procedure Put_Line<br />

(File : in File_Type;<br />

Item : in String);<br />

procedure Put_Line<br />

(Item : in String);<br />

--------------------------------------------------------<br />

-- Generic packages for Input-Output of Integer Types --<br />

--------------------------------------------------------<br />

generic<br />

type Num is range ;<br />

package Integer_Io is<br />

Default_Width : Field := Num'Width;<br />

Default_Base : Number_Base := 10;<br />

procedure Get<br />

(File : in File_Type;<br />

Item : out Num;<br />

Width : in Field := 0);<br />

procedure Get<br />

(Item : out Num;<br />

Width : in Field := 0);<br />

procedure Put<br />

(File : in File_Type;<br />

Item : in Num;<br />

Width : in Field := Default_Width;<br />

Base : in Number_Base := Default_Base);<br />

procedure Put<br />

(Item : in Num;<br />

Width : in Field := Default_Width;<br />

Base : in Number_Base := Default_Base);<br />

procedure Get<br />

(From : in String;<br />

Item : out Num;<br />

Last : out Positive);<br />

procedure Put<br />

(To : out String;<br />

Item : in Num;<br />

Base : in Number_Base := Default_Base);<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (4 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

end Integer_Io;<br />

-----------------------------------<br />

-- Input-Output of Modular Types --<br />

-----------------------------------<br />

generic<br />

type Num is mod ;<br />

package Modular_IO is<br />

Default_Width : Field := Num'Width;<br />

Default_Base : Number_Base := 10;<br />

procedure Get<br />

(File : in File_Type;<br />

Item : out Num;<br />

Width : in Field := 0);<br />

procedure Get<br />

(Item : out Num;<br />

Width : in Field := 0);<br />

procedure Put<br />

(File : in File_Type;<br />

Item : in Num;<br />

Width : in Field := Default_Width;<br />

Base : in Number_Base := Default_Base);<br />

procedure Put<br />

(Item : in Num;<br />

Width : in Field := Default_Width;<br />

Base : in Number_Base := Default_Base);<br />

procedure Get<br />

(From : in String;<br />

Item : out Num;<br />

Last : out Positive);<br />

procedure Put<br />

(To : out String;<br />

Item : in Num;<br />

Base : in Number_Base := Default_Base);<br />

end Modular_IO;<br />

--------------------------------<br />

-- Input-Output of Real Types --<br />

--------------------------------<br />

generic<br />

type Num is digits ;<br />

package Float_Io is<br />

Default_Fore : Field := 2;<br />

Default_Aft : Field := Num'Digits - 1;<br />

Default_Exp : Field := 3;<br />

procedure Get<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (5 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

(File : in File_Type;<br />

Item : out Num;<br />

Width : in Field := 0);<br />

procedure Get<br />

(Item : out Num;<br />

Width : in Field := 0);<br />

procedure Put<br />

(File : in File_Type;<br />

Item : in Num;<br />

Fore : in Field := Default_Fore;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

procedure Put<br />

(Item : in Num;<br />

Fore : in Field := Default_Fore;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

procedure Get<br />

(From : in String;<br />

Item : out Num;<br />

Last : out Positive);<br />

procedure Put<br />

(To : out String;<br />

Item : in Num;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

end Float_Io;<br />

generic<br />

type Num is delta ;<br />

package Fixed_Io is<br />

Default_Fore : Field := Num'Fore;<br />

Default_Aft : Field := Num'Aft;<br />

Default_Exp : Field := 0;<br />

procedure Get<br />

(File : in File_Type;<br />

Item : out Num;<br />

Width : in Field := 0);<br />

procedure Get<br />

(Item : out Num;<br />

Width : in Field := 0);<br />

procedure Put<br />

(File : in File_Type;<br />

Item : in Num;<br />

Fore : in Field := Default_Fore;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

procedure Put<br />

(Item : in Num;<br />

Fore : in Field := Default_Fore;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (6 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

procedure Get<br />

(From : in String;<br />

Item : out Num; Last : out Positive);<br />

procedure Put<br />

(To : out String;<br />

Item : in Num;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

end Fixed_Io;<br />

generic<br />

type Num is delta digits ;<br />

package Decimal_IO is<br />

Default_Fore : Field := Num'Fore;<br />

Default_Aft : Field := Num'Aft;<br />

Default_Exp : Field := 0;<br />

procedure Get<br />

(File : in File_Type;<br />

Item : out Num;<br />

Width : in Field := 0);<br />

procedure Get<br />

(Item : out Num;<br />

Width : in Field := 0);<br />

procedure Put<br />

(File : in File_Type;<br />

Item : in Num;<br />

Fore : in Field := Default_Fore;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

procedure Put<br />

(Item : in Num;<br />

Fore : in Field := Default_Fore;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

procedure Get<br />

(From : in String;<br />

Item : out Num;<br />

Last : out Positive);<br />

procedure Put<br />

(To : out String;<br />

Item : in Num;<br />

Aft : in Field := Default_Aft;<br />

Exp : in Field := Default_Exp);<br />

end Decimal_IO;<br />

---------------------------------------<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (7 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a<br />

-- Input-Output of Enumeration Types --<br />

---------------------------------------<br />

generic<br />

type Enum is ();<br />

package Enumeration_Io is<br />

Default_Width : Field := 0;<br />

Default_Setting : Type_Set := Upper_Case;<br />

procedure Get (File : in File_Type; Item : out Enum);<br />

procedure Get (Item : out Enum);<br />

procedure Put<br />

(File : in File_Type;<br />

Item : in Enum;<br />

Width : in Field := Default_Width;<br />

Set : in Type_Set := Default_Setting);<br />

procedure Put<br />

(Item : in Enum;<br />

Width : in Field := Default_Width;<br />

Set : in Type_Set := Default_Setting);<br />

procedure Get<br />

(From : in String;<br />

Item : out Enum;<br />

Last : out positive);<br />

procedure Put<br />

(To : out String;<br />

Item : in Enum;<br />

Set : in Type_Set := Default_Setting);<br />

end Enumeration_Io;<br />

-- Exceptions<br />

Status_Error : exception renames IO_Exceptions.Status_Error;<br />

Mode_Error : exception renames IO_Exceptions.Mode_Error;<br />

Name_Error : exception renames IO_Exceptions.Name_Error;<br />

Use_Error : exception renames IO_Exceptions.Use_Error;<br />

Device_Error : exception renames IO_Exceptions.Device_Error;<br />

End_Error : exception renames IO_Exceptions.End_Error;<br />

Data_Error : exception renames IO_Exceptions.Data_Error;<br />

Layout_Error : exception renames IO_Exceptions.Layout_Error;<br />

private<br />

-- Implementation defined...<br />

end <strong>Ada</strong>.Text_IO;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_a (8 of 8) [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_b<br />

Appendix B Sequential_IO package<br />

with IO_exceptions;<br />

generic<br />

type ELEMENT_TYPE is private;<br />

package SEQUENTIAL_IO is<br />

type file_type is limited private;<br />

type file_mode is (in_file,out_file);<br />

-- File management<br />

procedure CREATE (file :in out file_type;<br />

mode :in file_mode:=out_file;<br />

name :in string := "";<br />

form :in string := "");<br />

procedure OPEN ( file :in out file_type;<br />

mode :in file_mode;<br />

name :in string;<br />

form :in string := "");<br />

procedure CLOSE (file :in out file_type);<br />

procedure DELETE (file :in out file_type);<br />

procedure RESET (file :in out file_type;<br />

mode :in file_mode);<br />

procedure RESET (file :in out file_type);<br />

function MODE (file :in file_type) return file_mode;<br />

function NAME (file :in file_type) return string;<br />

function FORM (file :in file_type) return string;<br />

function IS_OPEN(file :in file_type) return boolean;<br />

-- Input and output operations<br />

procedure READ( file :in file_type;<br />

item :out element_type);<br />

procedure WRITE( file :in file_type;<br />

item :in element_type);<br />

function END_OF_FILE(file:in file_type) return boolean;<br />

-- Exceptions<br />

status_error :exception renames IO_EXCEPTIONS.status_error;<br />

mode_error :exception renames IO_EXCEPTIONS.mode_error;<br />

name_error :exception renames IO_EXCEPTIONS.name_error;<br />

use_error :exception renames IO_EXCEPTIONS.use_error;<br />

device_error :exception renames IO_EXCEPTIONS.device_error;<br />

end_error :exception renames IO_EXCEPTIONS.end_error;<br />

data_error :exception renames IO_EXCEPTIONS.data_error;<br />

private<br />

-- implementation dependent<br />

end sequential_IO<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_b [10/14/2000 12:55:51 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_c<br />

Appendix C Direct_IO package<br />

with IO_exceptions;<br />

generic<br />

type ELEMENT_TYPE is private;<br />

package DIRECT_IO is<br />

type file_type is limited private;<br />

package direct_io is<br />

type file_mode is (in_file, in_out_file, out_file);<br />

type count is range 0.. implementation_defined;<br />

subtype positive_count is range 1..count'last;<br />

-- File management<br />

procedure CREATE( file :in out file_type;<br />

mode :in file_mode := out_file;<br />

name :in string := "";<br />

form :in string := "");<br />

procedure OPEN( file :in out file_type;<br />

mode :in file_mode;<br />

name :in string;<br />

form :in string := "");<br />

procedure CLOSE (file :in out file_type);<br />

procedure DELETE (file :in out file_type);<br />

procedure RESET (file :in out file_type;<br />

mode :in file_mode);<br />

procedure RESET (file :in out file_type);<br />

function MODE (file :in file_type) return file_mode;<br />

function NAME (file :in file_type) return string;<br />

function FORM (file :in file_type) return string;<br />

function IS_OPEN( file :in file_type) return boolean;<br />

-- Input and output operations<br />

procedure READ( file :in file_type;<br />

item : out element_type);<br />

procedure READ( file :in file_type;<br />

item : out element_type;<br />

from :in positive_count);<br />

procedure WRITE( file :in file_type;<br />

item :in element_type;<br />

from :in positive_count);<br />

procedure WRITE( file :in file_type;<br />

item :in element_type);<br />

procedure SET_INDEX( file :in file_type;<br />

to :in positive_count);<br />

function INDEX( file :in file_type) return positive_count;<br />

function SIZE( file :in file_type) return count;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_c (1 of 2) [10/14/2000 12:55:52 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_c<br />

function END_OF_FILE(file:in file_type) return boolean;<br />

-- Exceptions<br />

status_error :exception renames IO_EXCEPTIONS.status_error;<br />

mode_error :exception renames IO_EXCEPTIONS.mode_error;<br />

name_error :exception renames IO_EXCEPTIONS.name_error;<br />

use_error :exception renames IO_EXCEPTIONS.use_error;<br />

device_error :exception renames IO_EXCEPTIONS.device_error;<br />

end_error :exception renames IO_EXCEPTIONS.end_error;<br />

private<br />

--implementation depenedent<br />

end DIRECT_IO;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_c (2 of 2) [10/14/2000 12:55:52 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_d<br />

Appendix D Text_package package<br />

The text_package package is used by first year students for variable length string<br />

manipulation. It is loosely based on that provided in the LRM.<br />

Equality can be tested by simply using the standard "=" operator because padding<br />

is maintained as spaces in the unused portion of the record.<br />

<strong>Ada</strong>95 allows the overriding of the "=" operator for all types, and allows the<br />

operands to be of different types, so such padding would not be required.<br />

with text_io;<br />

package text_package is<br />

max_chars :constant integer := 256;<br />

subtype length_range is integer range 0..max_chars;<br />

subtype index_range is length_range range<br />

1..length_range'last;<br />

type text is<br />

record<br />

value :string(index_range);<br />

length :length_range:=0;<br />

end record;<br />

-------------------------------------------------------------------<br />

-------------<br />

-- read a whole line of characters, throw away anything that<br />

doesn't fit.<br />

procedure get_line( item :in out text);<br />

procedure get_line(file :in out text_io.file_type;<br />

item :in out text<br />

);<br />

-------------------------------------------------------------------<br />

-------------<br />

-- put the characters in the_text to the screen. do not include a<br />

new_line at<br />

-- the end<br />

procedure put(item :in text);<br />

procedure put(file :in text_io.file_type;<br />

item :in text);<br />

-------------------------------------------------------------------<br />

-------------<br />

-- Put the characters in the_text to the screen. Do include a<br />

new_line at the<br />

-- end.<br />

procedure put_line(item :in text);<br />

procedure put_line(file :in text_io.file_type;<br />

item :in text);<br />

-------------------------------------------------------------------<br />

-------------<br />

-- get the string inside the text item.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_d (1 of 3) [10/14/2000 12:55:53 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_d<br />

function to_string(item :in text) return string;<br />

-------------------------------------------------------------------<br />

-------------<br />

-- return the length of the string<br />

function length(item :in text) return length_range;<br />

-------------------------------------------------------------------<br />

-------------<br />

-- return true if the string is empty<br />

function empty(item :in text) return boolean;<br />

-------------------------------------------------------------------<br />

-------------<br />

-- set the value of "the_text" to "a_string". Disregard any<br />

characters that<br />

-- don't fit into the record<br />

function to_text(item :in string) return text;<br />

-------------------------------------------------------------------<br />

-------------<br />

-- append a character onto the end of the text<br />

-- disregard anything that doesn't fit in<br />

procedure append( item :in out text;<br />

add_on :in character);<br />

-------------------------------------------------------------------<br />

-------------<br />

-- append a string onto the end of the text<br />

-- disregard anything that doesn't fit in<br />

procedure append( item :in out text;<br />

add_on :in string);<br />

-------------------------------------------------------------------<br />

-------------<br />

-- append the two text items together<br />

-- disregard anything that doesn't fit in<br />

procedure append( item :in out text;<br />

add_on :in text);<br />

-------------------------------------------------------------------<br />

-------------<br />

-- returns the first n characters of the string, the string is<br />

padded out with<br />

-- spaces if item is not long enough<br />

--<br />

function first_n_chars(item :in text;<br />

n :in natural) return string;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_d (2 of 3) [10/14/2000 12:55:53 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_d<br />

end text_package;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_d (3 of 3) [10/14/2000 12:55:53 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_e<br />

Appendix E Simple_io package<br />

The simple_io package is the simplified I/O package used by the first year students.<br />

It attempts to provide facilities similar to that found in the Turbo Pascal<br />

environment.<br />

with text_io;<br />

package simple_io is<br />

data_error: exception renames text_io.data_error;<br />

procedure get_line( item : out integer);<br />

procedure get_line( file :in out text_io.file_type;<br />

item : out integer);<br />

procedure get_line( item : out float);<br />

procedure get_line( file :in out text_io.file_type;<br />

item : out float);<br />

procedure get_line( item : out string);<br />

procedure get_line( file :in out text_io.file_type;<br />

item : out string);<br />

procedure get_line( item : out character);<br />

procedure get_line( file :in out text_io.file_type;<br />

item : out character);<br />

procedure get( item : out character)<br />

renames text_io.get;<br />

procedure skip_line(spacing :in text_io.positive_count:=1)<br />

renames text_io.skip_line;<br />

function end_of_file return boolean<br />

renames text_io.end_of_file;<br />

function end_of_line return boolean<br />

renames text_io.end_of_line;<br />

procedure put( item :in integer;<br />

width :in natural := 0);<br />

procedure put( file :in out text_io.file_type;<br />

item :in integer;<br />

width :in natural := 0);<br />

default_fore :natural := 6;<br />

default_aft :natural := 2;<br />

default_exp :natural := 0;<br />

procedure put( item :in float;<br />

fore :in natural := default_fore;<br />

aft :in natural := default_aft;<br />

exp :in natural := default_exp);<br />

procedure put( file :in out text_io.file_type;<br />

item :in float;<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_e (1 of 2) [10/14/2000 12:55:54 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_e<br />

fore :in natural := default_fore;<br />

aft :in natural := default_aft;<br />

exp :in natural := default_exp);<br />

procedure put( item :in string;<br />

width :in natural := 0);<br />

procedure put( item :in character);<br />

procedure new_line(line_count:in positive := 1);<br />

------------------------------------------------------------<br />

-- The following subprograms are used for screen and<br />

keyboard control<br />

-subtype<br />

row_range is integer range 0..23;<br />

subtype column_range is integer range 0..79;<br />

end simple_io;<br />

procedure move( row :in row_range;<br />

col :in column_range);<br />

procedure home;<br />

-- moves the cursor to row_range'first, column_range'first<br />

procedure clear_screen;<br />

-- clears the screen and sends the cursor home<br />

subtype color_range is integer range 30..37;<br />

black :constant color_range := 30;<br />

red :constant color_range := 31;<br />

green :constant color_range := 32;<br />

yellow :constant color_range := 33;<br />

blue :constant color_range := 34;<br />

magenta :constant color_range := 35;<br />

cyan :constant color_range := 36;<br />

white :constant color_range := 37;<br />

procedure text_color(color :color_range);<br />

function current_text_color return color_range;<br />

-- what is the current color used for displaying text?<br />

procedure get_cursor_pos(row : out row_range;<br />

col : out column_range);<br />

-- where is the cursor currently positioned?<br />

procedure wait_for_keypress;<br />

-- waits until the user hits a single key<br />

function read_key return character;<br />

-- returns the key the user types without needing<br />

-- the user to press the return key.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_e (2 of 2) [10/14/2000 12:55:54 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_f<br />

Appendix F GNAT <strong>Ada</strong><br />

The Gnat (GNU <strong>Ada</strong> Translator) project has developed a fully fledged <strong>Ada</strong> compiler that<br />

will<br />

work with the GNU linker. This will allow the compiler to be ported to any target<br />

that the GNU compiler/linker system is currently available for. The compiler is<br />

implementing the 1995 revision to the language, and so provides support for object<br />

oriented programming, enhanced concurrency support as well as numerous other<br />

features such as child and private child packages.<br />

The Gnat <strong>Ada</strong> compiler does away with the traditional library implementation of<br />

most <strong>Ada</strong> systems and presents a more traditional environment. Source code is<br />

compiled into a .o and a .ali file. The .o represents the translation of the<br />

instructions, the .ali file contains information about the relationship of the source to<br />

other components in the system.<br />

Gnat <strong>Ada</strong> source code must be in a file that has the same name as the compilation<br />

unit name, with either a .ads (for specifications) or a .adb (for bodies).<br />

Packages with child units (such as unix.file) should have the dot replaced with a '-',<br />

and the usual .ads or .adb suffix appended.<br />

For example the following program must be placed into a file called demo.adb.<br />

with simple_io; use simple_io;<br />

procedure demo is<br />

begin<br />

for i in 1..10 loop<br />

put("hello world");<br />

new_line;<br />

end loop;<br />

end;<br />

To compile this you type in the following...<br />

gcc -c fred.adb<br />

Typical questions now asked at this stage...<br />

Do you mean that the C compiler actually compiles this program?<br />

Does this mean that the code is compiled to C?<br />

The answer to both of these questions is NO!<br />

gcc is not a compiler, it is a program that inspects the file suffix (e.g. .c, .adb,<br />

.ads,<br />

.c++) and then invokes the appropriate compiler. The gnat (GNu <strong>Ada</strong> Translator)<br />

compiles the program into the standard GNU intermediate code representation,<br />

which is then taken by the code generator specific for the computer you are<br />

running, and produces a normal '.o' object file.<br />

Now that that's out of the way...<br />

To link you program...<br />

gnatbl demo.ali<br />

It doesn't matter how many other procedures/packages you with, you don't need to<br />

specify them in the link option.<br />

However if you want to link in some C routines, then you may have to do the<br />

following (this example is taken from an example using the C curses library)....<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_f (1 of 2) [10/14/2000 12:55:55 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_f<br />

gnatbl demo.ali -lcurses<br />

Sharing software<br />

The Gnat compiler looks for the environment variables ADA_INCLUDE_PATH<br />

and ADA_OBJECT_PATH. Any directory in these path variables will be searched<br />

for source code and object code, respectively.<br />

In this way a group can share some commonly developed routines.<br />

Compiler source code (and source to all <strong>Ada</strong> standard packages)<br />

Currently on Arcadia/Yallara the source for the compiler and all the <strong>Ada</strong> standard<br />

packages are in the directory<br />

/opt/gnu/adainclude<br />

Some of these names have been 'crunched' (i.e. reduces to 8.3 DOS filename<br />

conventions) so it is not always immediately obvious where a package spec.<br />

resides. For example the text_io package can be found in the file<br />

a-textio.ads (ada.text_io package specification)<br />

Other packages of interest are...<br />

interfac.ads package Interface<br />

i-c.ads package Interface.C<br />

i-cpoint.ads package Interface.C.Pointers<br />

i-cstrin.ads pacakge Interface.C.Strings<br />

a-calend.ads package Calendar<br />

a-finali.ads package <strong>Ada</strong>.Finalization (for destructors<br />

etc)<br />

a-string.ads package <strong>Ada</strong>.Strings<br />

a-strbou.ads package <strong>Ada</strong>.Strings.Bounded<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_f (2 of 2) [10/14/2000 12:55:55 PM]


http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_g<br />

Appendix G <strong>RMIT</strong> <strong>Ada</strong> resources<br />

<strong>RMIT</strong> currently has the Gnat <strong>Ada</strong> compiler installed on Arcadia and<br />

Yallara Sun computers.<br />

A public directory<br />

/public/ada<br />

contains various <strong>Ada</strong> resources.<br />

These include<br />

ada-lref.ps quick reference sheets on <strong>Ada</strong> syntax, attributes, library<br />

ada-syntax.ps packages etc.<br />

sources directory included in all search paths with some common<br />

code<br />

sources/unix Unix "mini" binding, giving <strong>Ada</strong> access to selected Unix<br />

services<br />

sources/unix-file<br />

sources/unix-process<br />

The directory<br />

/opt/local/gnu/adainclude<br />

contains the Gnat implementation of all the standard <strong>Ada</strong> services, such as package<br />

Calendar, Direct_IO, Text_IO etc.<br />

The Gnat <strong>Ada</strong> compiler for other computers can be found in the /public/ada directory<br />

on Yallara. Ports to OS/2, Linux and DOS are present.<br />

<strong>RMIT</strong>'s PC network also has the GNAT <strong>Ada</strong> compiler in the directory<br />

g:\pub\cs100\gnat<br />

In this directory are all the files you should need for running Gnat on a DOS<br />

computer, and is better maintained than the DOS distribution on Yallara.<br />

http://goanna.cs.rmit.edu.au/~dale/ada/aln/appendix_g [10/14/2000 12:55:56 PM]


Dale Stanbrough<br />

Go to the Tutorial Allocation System<br />

Go to the Tutorial Allocation Admin<br />

System (for Lecturers)<br />

Dale Stanbrough<br />

Consultation times:<br />

City Monday, 3.30 - 5.20pm<br />

Bundoora Friday 9 - 10.30<br />

Academic:<br />

Email: dale@rmit.edu.au<br />

Phone: 9925 6130 (Bundoora) Phone: 9925 3266 (City)<br />

Room 251.2.29, Bundoora East Campus<br />

Room 10.7.36, City Campus<br />

1st semester subjects<br />

● CS280 Software Engineering I<br />

● CS582 Software Engineering 2<br />

2nd semester subjects<br />

● CS280 Software Engineering I<br />

● CS504 Concurrent Computing<br />

● CS825 Software Engineering for Grad. Dips.<br />

● AV530 <strong>Ada</strong> programming<br />

How to write unmaintainable code. Definately everthing every poor programmer does :-)<br />

http://goanna.cs.rmit.edu.au/~dale/index.html (1 of 2) [10/14/2000 12:59:49 PM]


Dale Stanbrough<br />

Older Subjects<br />

<strong>Ada</strong> resources<br />

My family<br />

http://goanna.cs.rmit.edu.au/~dale/index.html (2 of 2) [10/14/2000 12:59:49 PM]

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!