Quick Ada - RMIT University
Quick Ada - RMIT University
Quick Ada - RMIT University
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]