10 An Introduction to Ada


  <--Last Chapter Table of Contents Next Chapter-->  


Ada is a full-featured language with many capabilities, rules, and nuances. Although the fundamentals are easy to learn (Ada somewhat resembles BASIC), it is several times larger that C, and to truly master the language requires considerable practice. To make understanding easier, the discussion is broken up into two chapters. This chapter outlines the basics of the language, and the next chapter discusses features for team development, large projects, and other specialized tasks.

This in no way covers everything there is to know about Ada. I've chosen to cover those features that have been the most use to me over the years in my projects. For example, array slicing alone could take up several pages of discussion, but I've never had a need for it in recent years. Of course, you may be involved in a project in which array slicing is crutial. In these cases, I recommend you get a good Ada 95 reference such as Barnes' Programming in Ada 95.

Likewise this is not a complete introduction to computer programming. Some background knowledge is assumed.

Ada also has many specialized features for specific tasks such as scientific computing and real-time systems. Where I deliberately skip a subject, I usually make a note that I have done so. I've also hilighted useful information for C programmers who are learning Ada.

Now, on to main programs.

10.1 Your Main Program

A main program in Ada is a procedure with no parameters that starts your program running. This is the set of instructions that the computer begins to follow when your program is first executed.

The following program will print a message on the screen when you run it.

with Ada.Text_IO;
procedure firstProgram is
-- my first Ada program
  Ada.Text_IO.Put_Line( "This is my first Ada program." );
end firstProgram;
C: Ada is not case sensitive. "WITH" or "With" is the same as "with".

An Ada program consists of sets of words and punctuation symbols. The words fall into two categories. First, the words in bold in bold are called keywords. These are words that have special meaning to Ada. Second, the words that aren't in bold are identifiers. These are the names of variables, procedures, packages and other items with names or titles in the language.

IDE: Ada IDE's will hilight keywords in bold for you. Some editors such as emacs, elvis and nedit will also hilight keywords. This is a good way to check for spelling mistakes.

In this program, begin is a keyword because Ada uses the word "begin" to denote where the program is to begin executing instructions. On the other hand, "firstProgram" is an identifier because it is the name of our program.

All keywords in Ada are also reserved words: this means that the keywords cannot be used as identifiers.

The main program can have any name, as long as the name matches the filename. In gnat, the source code for a main program ends in .adb (Ada body). This program should be saved as firstprogram.adb.

C: The main program doesn't have to be "main" unless you save the program as "main.adb".

NoteIf you call a program "test.adb", remember that test is a built-in shell command. To run a program named test, you'll have to type "./test" instead of "test" to avoid running the shell command by mistake.

Comments are denoted by two minus signs (--). This is a note to the reader; Ada will ignore it. Everything you type to the right of the symbol is treated as a remark to the reader.

C: Ada has no equivalent to the block comment /* and */.

10.2 Text_IO

Ada Description C Equivalent
put( s ); Display a string printf( "%s", s );
put( n'img ); Display a number printf( "%d", n );
put_line( s ); Display a line of text and start a new line printf( "%s\n", s );
new_line; Start a new line printf( "\n" );
get( c ); Read a character from the keyboard c = getc();
get_line( s, len ); Read a line of text from the keyboard gets(&s);

Like many modern computer languages, Ada doesn't have any built-in methods of reading the keyboard or writing messages on the screen. It doesn't assume you're writing a program for a PC (you could be doing embedded programming, for example)--but in general, you need to interpret what people type and display the results to the screen. You have to add this functionality specifically.

The standard input/output package for Ada is Text_IO. This package prints characters and strings to the screen and reads characters and strings from the keyboard. It can also read and write simple sequential text files. (Packages will be discussed in detail starting at 11.1 in the next chapter.)

Text_IO is only useful for simple programs. It doesn't have the ability to draw buttons, windows or menus. For X Windows programming, you'll require other packages/libraries to perform input and output.

C: In C, printf and company can use an arbitrary number of parameters, where the parameters can be of different types. Text_IO's puts have one parameter, and the parameter must be a string or a character. Upcoming sections demonstrate how to print other types.

The most commonly used operations are:

The following program is an example of Text_IO.

with Ada.Text_IO;
use Ada.Text_IO;

procedure basicio is
-- this program demonstrates basic input/output

  c : character; -- this is a letter


  Put_Line( "This program displays information on the screen" );
  Put_Line( "and reads information from the keyboard");

  Put_Line( "Put_Line displays a line of text and advances to" );
  Put_Line( "the next line." );

  Put( "Put " );
  Put_Line( "displays text, but it doesn't start a new line" );

  Put_Line( "New_Line displays a blank line");

  Put_Line( "Get waits for a character to be typed.");

  Put_Line( "Type a key and the Enter key to continue." );
  Get( c );
  Put_Line( "The character you typed was '" & c & "'" );

end basicio;

This program displays information on the screen
and reads information from the keyboard

Put_Line displays a line of text and advances to
the next line.
Put displays text, but it doesn't start a new line
New_Line displays a blank line

Get waits for a character to be typed.
Type a key and the Enter key to continue.
The character you typed was 'c'

Besides letters and numbers, there are special characters called control characters which, instead of displaying a character, change the Linux display. To print controls characters, you need to use one of Ada's built-in character sets. For example, ASCII is a predefined list of all the ASCII characters. To send an explicit form feed character, use

  Put( ASCII.FF );

Some common control characters are:

C: Put doesn't recognize C string escape codes like "\n" or "\r".

Besides ASCII, Ada has a number of other character sets defined in the Ada.Characters packages.

NoteThe ASCII set is officially made obsolete in Ada 95 by Ada.Characters.Latin_1, but it's still often used because it's easier to type.

10.3 Fundamental Data Types

Ada Type Description C Equivalent
Character A single character char
Integer An integer (32-bit) number int
Natural Zero or positive integer  -
Positive Positive integer  -
Long_Integer A big integer (same as long in Gcc) long (same as int in Gcc)
Long_Long_Integer A really big (64-bit) integer long long
Short_Integer A small (16-bit) integer short
Short_Short_Integer A really small (8-bit) integer char
Float A real number float
Long_Float A big real number double
Long_Long_Float A really big real number long double
Short_Float A smaller real number ?
Fixed A fixed-point real number  -
String An Ada fixed-length string char array
C: There are no built-in equivalents of unsigned types. Natural and Positive are integer values that aren't allowed to be negative, effectively requiring the sign bit to be zero.

Characters cannot be used for small integer values--characters variables can only represent character values.

Generally speaking, programs take data, process it in different ways, and create new information. Data is categorized into different data types.

Data that is typed into a program is known as literals. Ada has several kinds of literals:

C: Ada doesn't have long numerical literals, like "45L". Numeric literals are a special type called universal integer and adapt to fit the requirements of an expression.
C: Ada strings do not end with an ASCII 0 character: they end with the upper bound of the array that encloses them. To change an Ada string into a C string, concatenate a null character like this:
  "This is my string" & ASCII.NUL;

There are three kinds of real numbers. A fixed, or fixed point, number is a number that has a fixed number of decimal points. For example, U.S. dollars are often represented using fixed numbers because there are two decimal places. A float, or floating-point, number is a number that doesn't have a fixed number of decimal places. Decimal numbers are a variation of fixed numbers commonly used for currency.

Some Ada programmers recommend that floats are used whenever possible because float calculations are usually faster than fixed calculations. This is because most computers today have floating point support in their hardware.

Floating point numbers are very important for business and scientific applications. When floating point numbers are converted to integers, do the numbers round to the nearest integer or is the decimal part simply discarded? In C, this is system dependent: System V-based UNIX's usually round to the nearest integer, while some other systems discard the decimal part. (Others, like HP-UX, the number rounds towards the nearest even integer providing the floating point number is exactly half way between two integers.)

On Linux, C truncates the decimal part.

In Ada, the way numbers round are strictly defined by the language: you can be sure that, no matter what operating system you are using, floating point numbers converted to integers will always round to the nearest integer. If the floating point number is half way between two integers, it will round "up".

The following program demonstrates floating point rounding:

with ada.text_io, ada.float_text_io;
use ada.text_io, ada.float_text_io;

procedure rounding is
  -- rounding example

  procedure ShowRounding( f : float ) is
    -- show the floating point value, and show the value
    -- after it's converted to an integer
    int_value : integer;
    Put( "  Float number " );
    Put( f, fore => 5, aft => 3 );
    int_value := integer( f );
    Put_Line( " rounds to " & int_value'img );
  end ShowRounding;

  Put_Line( "This is a demonstration of how Ada 95 rounds" );

  ShowRounding( 253.0 );
  ShowRounding( 253.2 );
  ShowRounding( 253.5 );
  ShowRounding( 253.8 );
  ShowRounding( -253.8 );

end rounding;

This is a demonstration of how Ada 95 rounds Float number 2.530E+02 rounds to 253 Float number 2.532E+02 rounds to 253 Float number 2.535E+02 rounds to 254 Float number 2.538E+02 rounds to 254 Float number -2.538E+02 rounds to -254

You can compare the results with the following C program:

#include <stdio.h>

static void show_rounding( float f ) {
   int i;

   i = f;
   printf( "  Float number %g", f );
   printf( " rounds to %d\n", i );

} /* show rounding */

int main () {

  show_rounding( 253.0 );
  show_rounding( 253.2 );
  show_rounding( 253.5 );
  show_rounding( 253.8 );
  return 0;


  Float number 253 rounds to 253
  Float number 253.2 rounds to 253
  Float number 253.5 rounds to 253
  Float number 253.8 rounds to 253

Rounding to integers is a common way in C business applications to round money to the nearest dollar or cent. This is accomplished by multiplying the floating point value by 100.0, adding .5, and then taking the integer value and converting it once more into a floating point value. In Ada, there's a built-in type attribute to round floating point numbers: this makes conversion to an integer unnecessary.

C: The fundamental integer types don't "wrap around" the way C data types do. Values that grow too large produce overflow errors. However, gnat turns off integer overflow exceptions by default to improve performance. Ada provides properly behaved C types and conversion functions in the Interfaces.C package. Interfaces.C includes the following types:
type int is new Integer;
type short is new Short_Integer;
type long is range -(2 ** lbits1) .. +(2 ** lbits1) - 1;
type signed_char is range SCHAR_MIN .. SCHAR_MAX;
  for signed_char'Size use CHAR_BIT;
type unsigned is mod 2 ** int'Size;
type unsigned_short is mod 2 ** short'Size;
type unsigned_long is mod 2 ** long'Size;
type unsigned_char is mod (UCHAR_MAX + 1);
for unsigned_char'Size use CHAR_BIT;

GNAT has a second package, Interfaces.C.Extensions, that includes additional types, such as unsigned_long_long.

As it's name suggests, the Text_IO package only performs I/O with text, not numbers or other types of information. If you want to print, say, and integer value using Text_IO, you must first convert the integer to a string using the 'img attribute (or 'image). (Attributes are discussed in the next section.)

with Ada.Text_IO;
use Ada.Text_IO;

procedure basicio2 is
-- this program demonstrates more advanced input/output

  i : integer := 5; -- this variable contains an integer number
                    -- i initially has the value of 5
  s : string(1..20); -- this variable contains a 20 character string
  len : natural;


  Put_Line( "This program displays information on the screen" );
  Put_Line( "and reads information from the keyboard");

  Put_Line( "'img returns the string representation of a variable's" );
  Put_Line( "value. The value i is" & i'img);

  s := "...................."; -- set s to 20 periods

  Put_Line( "The variable s is " & s);
  Put_Line( "Get_Line reads a string from the keyboard" );
  Put_Line( "Type in a message up to 20 characters and press Enter:" );
  Get_Line( s, len );

  Put_Line( "After Get_Line copies your message to s, s is now '" & s & "'" );
  Put_Line( "The message is" & len'img & " characters long." );

  Put_Line( "The characters after your message remain unchanged." );

end basicio2;

This program displays information on the screen
and reads information from the keyboard

'img returns the string representation of a variable's
value. The value i is 5

The variable s is ....................

Get_Line reads a string from the keyboard
Type in a message up to 20 characters and press Enter:
jingle bells

After Get_Line copies your message to s, s is now 'jingle bells........'
The message is 12 characters long.

The characters after your message remain unchanged.

with Ada.Text_IO;
use Ada.Text_IO;

procedure basicio3 is
  -- this program demonstrates more even advanced input/output

  i : integer := 5; -- this variable contains an integer number
                    -- i initially has the value of 5
  s : string(1..5); -- this variable contains a 5 character string

  len : natural;    -- length of string


  Put_Line( "This program displays information on the screen" );
  Put_Line( "and reads information from the keyboard");

  Put_Line( "The value i is" & i'img);

  Put_Line( "integer'value changes a string into an integer value" );
  Put_Line( "Type in a 4 character integer characters with a leading" );
  Put_Line( "space or negative sign and press Enter:");
  Get_Line( s, len );

  i := integer'value( s );
  Put_Line( "The value of i is " & i'img);

end basicio3;

This program displays information on the screen
and reads information from the keyboard

The value i is 5

integer'value changes a string into an integer value

Type in a 4 character integer characters with a leading
space or negative sign and press Enter:

The value of i is 2345

Besides Text_IO, Ada provides additional "Text_IO" packages for the basic Ada data types. Using these packages, you don't need to use 'img to convert the variable to a string. For example, the package Ada.Integer_Text_IO can put and get integers, and Ada.Float_Text_IO can put and get floating point numbers. You can use these packages simultaneously with Text_IO.

These additional packages do not have Put_Line or Get_Line because these are specifically for strings. The Put command has two additional capabilities: to space information to fit into specified field widths, and to display numbers in formats other than base 10.

with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure basicio4 is
-- this program demonstrates integer input/output
  i : integer := 5; -- this variable contains an integer number
  -- i initially has the value of 5
  Put_Line( "This program displays information on the screen" );
  Put_Line( "and reads information from the keyboard" );
  Put( "The value i is" ); Put( i ); New_Line;
  Put_Line( "Type in an integer number." );
  Get( i );
  Put( "The value i is" ); Put( i ); New_Line;
  Put_Line( "'width =>' specifies the amount of room to display the number in." );
  Put_Line( "This can be used to display columns of numbers." );
  Put( "Using a width of 5, the value i is '" );
  Put( i, width => 5 );
  Put_Line( "'" );
  Put_Line( "'base =>' specifies a number system besides the normal base 10" );
  Put( "Using binary notation, the value i is " ); Put( i, base => 2 ); New_Line;
  Put_Line( "Set the variable Default_Width or Default_Base to avoid using" );
  Put_Line( "'width =>' and 'base =>'." );
  Put( "The Default_Width was " ); Put( Default_Width ); New_Line;
  Default_Width := 20;
  Put( "The Default_Width is now " ); Put( Default_Width ); New_Line;
  Put( "The value i is '" ); Put( i ); Put_Line( "'" );
end basicio4;

This program displays information on the screen
and reads information from the keyboard
The value i is 5
Type in an integer number.
The value i is         432
'width =>' specifies the amount of room to display the number in.
This can be used to display columns of numbers.
Using a width of 5, the value i is '   32'
'base =>' specifies a number system besides the normal base 10
Using binary notation, the value i is 2#110110000#
Set the variable Default_Width or Default_Base to avoid using
'width =>' and 'base =>'.
The Default_Width was 11
The Default_Width is now 20
The value i is '                    432'

Table: Predefined Text_IO packages for Numeric Types

Text_IO Package
Short_Short Integer (wide) Ada.Short_Short_Integer_Wide_Text_IO
Short_Float Ada.Short_Float_Text_IO
Short_Float (wide text) Ada.Short_Float_Wide_Text_IO
Short_Integer Ada.Short_Integer_Text_IO
Short_Integer (wide text) Ada.Short_Integer_Wide_Text_IO
Integer Ada.Integer_Text_IO
Integer (wide text) Ada.Integer_Wide_Text_IO
Float Ada.Float_Text_IO
Float (wide text) Ada.Float_Wide_Text_IO
Long_Float Ada.Long_Float_Text_IO
Long_Float (wide text) Ada.Long_Float_Wide_Text_IO
Long_Integer Ada.Long_Integer_Text_IO
Long_Integer (wide text) Ada.Long_Integer_Wide_Text_IO
Long_Long_Float Ada.Long_Long_Float_Text_IO
Long_Long_Float (wide) Ada.Long_Long_Float_Wide_Text_IO
Long_Long_Integer Ada.Long_Long_Integer_Text_IO
Long_Long_Integer (wide) Ada.Long_Long_Integer_Wide_Text_IO
Unbounded (String) Ada.Unbounded_IO
Wide_Unbounded (String) Ada.Wide_Unbounded_IO
Calender.Time Gnat.Time_IO

10.4 Type Attributes

Ada has a selection of attributes, or built-in functions, that can be applied to types and variables. Attributes are attached to the end of a type or variable name using a single quote.

The most useful attribute, the 'img attribute, returns the ASCII image of what it's attached to, which is handy for printing values on the screen using only Ada's Text_IO package. (5)'img, for example, is the string "5". false'image, the image of the boolean value false, is the string "FALSE" in capital letters. One quirk of 'img is that if the value is a positive number, 'img adds a leading blank.

'img is a GNAT shorthand for the Ada attribute 'image. 'image requires you to specify the type of the parameter. integer'image( 5 ) is the string "5". This attribute is useful on complicated expressions where 'img won't work because of the lack of parentheses.

Here's a list of Ada 95 attributes: [To Be Completed]

Access - access value for an identifier

Address - address value for an identifier
Adjacent - the floating point value adjacent to the given value
Aft - for fixed types, number of decimal digits after decimal to accommodate a subtype
Alignment - storage value of an identifier
Base - unconstrained subtype of a type
Bit_Order - whether or not bits are high order first
Body_Version [NQS]
Callable - true if task can be called
Ceiling - round up a floating point value
Class - classwide type of an identifier
Component_Size - size of array components in bits
First - first index in an array
Floor - round down a floating-point value
Input - convert value to a string
Last - last index in an array
Length - number of elements in an array
Machine_Emax - maximum real type exponent on your hardware
Machine_Emin - minimum real type exponent on your hardware
Machine_Mantissa - size of mantissa on your hardware in bits
Machine_Overflows - true of your machine overflows real types [NQS]
Max - maximum value
Min - minimum value
Partition_ID - for distributed processing
Pos - position in a discrete type (such as an enumerated) — opposite of val
Pred - previous value in a discrete type
Range - range of values for a type
Size - size of storage in bytes
Stoarge_Pool - used to set the storage pool for a pointer
Succ - next value in a discrete type
Tag - tag for a tagged record
Terminated - true if task has terminated
Unchecked_Access - return access type, but ignore scope checks
Val - value of a discrete type at a certain position — opposite of pos
Valid - determine if the expression evaluates to a legal result
Value - convert string to a value — opposite of image
Wide_Image - same as image, but for a 16-bit string
Wide_Value - same as value, but for a 16-bit string
Here's a list of additional gnat-specific attributes:
[should fold these in above]
Abort_Signal - task abort exception
Address_Size - number of bits in an address
Bit - offset to first bit in object
Default_Bit_Order - whether or not CPU uses high order first
Elab_Body - the procedure that elaborates a package body
Elab_Spec - the procedure that elaborates a package spec
Enum_Rep - the numerical value of an enumerated identifier
Fixed_Value - unchecked conversion of integer to a fixed type
Img - shorthand for ‘image
Integer_Value - the reverse of Fixed_Value
Machine_Bits - for compatibility with other Ada compilers
Max_Interrupt_Priority - the maximum interrupt priority
Max_Priority - the maximum task priority
Maximum_Alignment - determine the bit alignment of an external object
Mechanism_Code - how a parameter is passed to a subprogram
Null_Parameter - for passing null pointer for a composite object
Object_Size - for fixed and discrete types, default allocation size
Passed_By_Reference - true if type is normally passed by reference
Range_Length - number of values in a discrete type
Storage_Unit - same as System.Storage_Unit
Tick - same as System.Tick
Type_Class - return type basic class of an identifier (such an enumerated or array)
Universal_Literal_String - return a string literal for a number
Unrestricted_Access - like access, but has no accessibility or aliased view checks
Value_Size - number of bits to represent a value of a given subtype
Word_Size - same as System.Word_Size
The following program demonstrates some of the basic Ada attributes.
with text_io;
procedure attrib is
  type enum is ( dog, mica, megabyte );
  Text_IO.Put_Line( "Some Basic Ada Attributes:" );
  Text_IO.Put_Line( "Boolean bits is  " & boolean'size'img );
  Text_IO.Put_Line( "Short short integer bits is" &
    short_short_integer'size'img );
  Text_IO.Put_Line( "Short integer bits is " & short_integer'size'img );
  Text_IO.Put_Line( "Integer bits is " & integer'size'img );
  Text_IO.Put_Line( "Long integer bits is " & long_integer'size'img );
  Text_IO.Put_Line( "Long long integer bits is " &
    long_long_integer'size'img );
  Text_IO.Put_Line( "Natural bits is " & natural'size'img );
  Text_IO.Put_Line( "Positive bits is " & positive'size'img );
  Text_IO.Put_Line( "Short float bits is " & short_float'size'img );
  Text_IO.Put_Line( "Float bits is " & float'size'img );
  Text_IO.Put_Line( "Long float bits is " & long_float'size'img );
  Text_IO.Put_Line( "Long long float bits is " &
    long_long_float'size'img );
  Text_IO.Put_Line( "Our 3 item enumerated bits is " &
    enum'size'img );
  Text_IO.Put_Line( "First integer is " & integer'first'img );
  Text_IO.Put_Line( "Last integer is " & integer'last'img );
  Text_IO.Put_Line( "First enumerated is " & enum'first'img );
  Text_IO.Put_Line( "Last enumerated is " & enum'last'img );
  Text_IO.Put_Line( "Mica is in position" & enum'pos( mica )'img );
  Text_IO.Put_Line( "The third enumerated is " & enum'val(2)'img );
  Text_IO.Put_Line( "The smallest float is" & float'small'img );
  Text_IO.Put_Line( "The largest float is" & float'large'img );
  Text_IO.Put_Line( "The number of digits in float is" &
    integer'image(float'digits) );
  Text_IO.Put_Line( "The size of the mantissa in bits is" &
    float'mantissa'img );
  Text_IO.Put_Line( "However, the CPU's mantissa is" &
    float'machine_mantissa'img );
end attrib;

Here are the results of the program on a Pentium II with gnat 3.11:
Some Basic Ada Attributes:
Boolean bits is 1
Short short integer bits is 8
Short integer bits is 16
Integer bits is 32
Long integer bits is 32
Long long integer bits is 64
Natural bits is 31
Positive bits is 31
Short float bits is 32
Float bits is 32
Long float bits is 64
Long long float bits is 9
Our 3 item enumerated bits is 2
First integer is -2147483648
Last integer is 2147483647
First enumerated is DOG
Last enumerated is MEGABYTE
Mica is in position 1
The third enumerated is MEGABYTE
The smallest float is 1.17549435082228751E-38
The largest float is 1.93428038904620299E+25
The number of digits in float is 6
The size of the mantissa in bits is 21
However, the CPU's mantissa is 24

10.5 Operations and Expressions

Ada Operator Description C Equivalent
and Boolean and &&
or Boolean or ||
Boolean xor
Boolean not
Not equals
Absolute Value
Integer modulus
Float remainder
and then
Short circuited and
or else
Short circuited else
Value in range
not in
Short for not( ...in...)
Boolean operations: and, or, not, xor
Comparisons: >, >=, <, <=, =, /=
Unary operations: +, -, abs
Binary Operations: +, -, *, /, mod, rem, &, **
C: C boolean operators always short circuit. In Ada, there are both short circuiting operations and operations that do not short circuit.
NoteShort circuiting operations are not considered true operators, and as such, can't be overloaded (see below).
NoteMembership Tests (in and not in) are not considered true operators and can't be overloaded.
if dog in aDogBreed then
  Put_Line( "The dog is a breed in the enumerated aDogBreed" );
end if;
if i in 1..10 then
  Put_Line( "I is between 1 and 10" );
end if;
1..10 is called a range. The range attribute returns the range of values for a type.
if salary not in MiddleManagementSalary'range then
  Put_Line( "The salary is not in the middle management type's range" );
end if;
C: Assignment is considered a statement, not an operator.

10.6 Variable Declarations

You define a variable as the variable name, colon, the type of information the variable will hold, and a semicolon.
  totalSales : float;
This creates a new variable called totalSales that contains a real number. Some variations:

  runningTotal : integer := 0;

  -- this variable starts out at 0
  companyName : constant string := "Bob's Widgets Inc.";
  --companyName is set to Bob's Widgets Inc. and it can't be changed while
  --the program is running
  char1, char2 : character;
Complex variables references can be assigned a shorthand with a rename declaration.

  sb : float renames EmployeeList( CurrentEmployee ).SalaryInfo.Bonus;

  sb := 5.0; -- same as EmployeeList( CurrentEmployee ).SalaryInfo.Bonus := 5.0

C: There are no self-referential operators, such as C's +=.

10.7 New Types

Ada Statement Description C Equivalent
type Create a new type. typedef
subtype Create a variation of an existing type  -
New types are defined with the type statement.
type aSalary is new float;
type aSmallSalary is new Salary range 0.0 .. 35_000.0;
When you create a new type, the type is considered to be incompatible with the type it is derived from. If you want to add a small salary to a salary, you'll have to use type casting, even though they are both floats.
  totalSalary, BigSalary : aSalary;
  smallSalary : aSmallSalary;
  totalSalary := bigSalary + aSalary( smallSalary );
  To type cast one type into another, use the type name and the value to convert in parantheses after it.

C: "(type) value" style of type casting doesn't work in Ada.
C: "Ada has stronger restrictions on typecasting. In C, for example, you can cast a character pointer as an integer pointer. Although this is considered a bad programming practice, it is allowed. Ada will not allow this kind of type casting.
Use subtype to create a type aSmallSalary that is compatible with aSalary.
  subtype aSmallSalary is aSalary range 0..35_000.0;
Subtype can also be used to rename types.
  subtype sb is aSalaryBonusForEmployeesNamedBobStevens;

In this example, sb is a short form for aSalaryBonusForEmployeesNamedBobStevens. "sb" is techncially called a "subtype mark", a term which sometimes appears in Gnat error messages. One common error, "subtype mark required in this context", indicates that there are several different types that could be used and you have to indicate to the compiler which should used.


10.7.1 Modular Types

Normally, if a calculation produces an answer too large for the variable type being assigned to, it creates an error. This is called a numeric overflow. For example, if int is an integer,
  int := integer'max +1; -- check (KB)
will result in a constraint error because the answer is bigger than the biggest number an integer variable can contain.

Ada provides another type of integer called a modular type. The word "modular" comes from the mathematical modulus operation. Modular types never overflow. Instead, if a number becomes bigger than the largest possible number the variable can contain, the value of the modular type "wraps around" to the lowest value and continues to grow from there. If the int variable in the above example was a modular called int modular, then

  int := intmodular'max + 1;

would result in int being assigned intmodular'min.

C: C integer types are all modular because C doesn't catch overflow errors.
There are no built-in modular types. All modular types are new types created by a type statement.
  type mod10 is mod 10; -- value ranges from 0 to 9


10.7.2 Text_IO and New Types

To perform Text_IO input and output on new types, you have to create your own version of Text_IO for the new type. This process is called "instantiation", and is covered later in the section on generics. The format is
  package MyNewTextIOPackage isnew PredefinedGenericIOPackage( mytype );
For example, to create a Text_IO package for the aSalary type,

  package aSalary_Text_IO is new Ada.Text_IO.Float_IO( aSalary );

Ada creates a new package called aSalaray_Text_IO customized for the aSalary type. You can use your package just like one of the standard Ada numeric Text_IO packages.

  Salary := 50_000.00

  aSalary_Text_IO.Put( Salary );
The following table lists all the Text_IO packages that can be instantiated for a particular type.

Table : Predefined generic Text_IO packages for performing Input/Output
Base Type Package
Complex Numbers
Complex Numbers (wide text)
Decimals (NQS)
Decimal Numbers (wide text)
Enumerateds (wide text)
Fixed Points
Fixed Points (wide text)
Floating Points
Floating Points (wide text)
Integers Ada.Text_IO.Integer_IO
Integers (wide text)
Modulars (wide text)

10.8 Aggregate Types

Arrays are tables of values with specific bounds. For example, to declare a table of 10 people
  type peopleHeightList is array( 1..10 ) of integer;
The bounds can be specified as a type.

  type peopleHeight is new integer range 1..10;

  type peopleHeightList is array( peopleHeight ) of integer;
This creates an array from 1 to 10, the range of possible values for the type peopleHeight. This is the same as using array( peopleHeight'range ).
You can create a multidimensional array by using more than one index to the table.

  type peopleStats is array( peopleHeight, peopleAge ) of integer;

You can assign default values to an array using :=, the assignment operator. The list of values is enclosed in brackets. You can specify a specific value using =>, or specify a default with others =>.

  PeopleHeights1 : peopleHeightList := (others => 0);

  -- looks strange, but assigns 0 to all the heights in the entire list
  peopleHeights2 : peopleheightList := ( 10, others => 0 );
  -- first height is 10, others are 0
  peopleHeights3 : peopleHeightList := (5 => 15, others => 0 );
  -- fifth height is 15, others are 0
Arrays are accessed by specifying values for the indices. To get the height for the fifth element in the PeopleHeights1 array, you'd type:
  Put_Line( PeopleHeights1( 5 )'img );

Records are collections of related information. Each subsection is referred to as a field.

  type employeeProfile is record

    name : string( 1..80 );
    salary : aSalary;
    age : anAge;
  end record;
You can assign default values to the fields in a record using :=, the assignment operator.
  type employeeProfile isrecord
    name: string( 1..80 ) := (others => ' ');
    salary : aSalary := 30_000.0;
    age: anAge := 30;
  end record;
Default values for whole records can be specified when record variables are declared.

  Bob : employeeProfile := ("Bob Smith", 35_000.0, 37 );

  Denise : employeeProfile := ( name => "Denise Jones", salary => 39_000.0,
    age => 42 );
In the above examples, we are creating a temporary record and then assigning that record to the variable. You can use this in the executable part of your program, not just in declarations. Ada will require a "subtype mark", an indication of what type of record you are making.

  NewRec := employeeProfile'("Bob Smith", 35_000.0, 37 );

employeeProfile' indicates that the record we've built should be treated as an employeeProfile record.

Although this looks almost exactly the same as type casting, it isn't type casting. Consider the following:

  J := long_integer'( 5 );

  J := long_integer( 5 );
The first statement clarifies that 5 is a long_integer: this is a hint to the compiler that 5 should be treated as an long_integer. The second converts 5 from an integer to a long_integer.

Record fields are accessed using a period and the field name.

  Bob.age := 37;

A variant record is a record that contains different sets of mutually exclusive information.

  type employeeProfile( sex : aSex ) is record

    name : string( 1..80 );
    salary : aSalary;
    age : anAge;
    case sex is
    when male =>
    BeardLength : integer;
    when female => null;
  end record;
(check syntax)

In this example, a male employee has an additional field called BeardLength.

(when you create a variant record, you must specify the descriminant).


10.9 Enumerated Types

Enumerated types (lists of identifiers) are created using a type statement.
  type aDogBreed is ( Unknown, Boxer, Retriever, Shepherd, MixedBreed );

Different enumerated types may have the same values, but they are considered different from each other. For example,

  type aCatBreed is ( Unknown, Siamese, MixedBreed );

shares two values with aDogBreed, but an unknown cat breed is considered different from an unknown dog breed. If Ada is confused by the ambiguity, you can clarify values with a subtype mark, e.g. aCatBreed'(MixedBreed).

Many of the common attributes work with enumerated types, including 'first, 'last, and 'range. Especially useful are 'pred (get a previous enumerated identifier) and 'succ (get the next enumerated identifer). Specific values can be assigned (using a for clause) so the enumerated type can reflect an external integer value (such as error codes).

with ada.text_io, unchecked_conversion;
use ada.text_io;

procedure enumeration_fun is
  -- a demonstration of Ada 95 enumerated types

  type vowels is ( 'a', 'e', 'i', 'o', 'u', none );
  -- characters may be used as well as identifiers.  The standard
  -- character sets are implemented this way.

  type aDogBreed is ( Jack_Russel, Labrador, German_Shepherd, Other );
  type aCanadianRegion is ( West_Coast, Arctic, Labrador, Other );
  subtype coldPlaces is aCanadianRegion range Arctic..Other;
  -- names may overlap between enumerated types

  type anErrorCode is ( None, IOerror );
  for anErrorCode use ( None => 0, IOerror => 7 );
  -- specific values may be assigned to enumerated identifiers

  function toInteger is new unchecked_conversion( anErrorCode, integer );


  -- Basic enumerated type operations

  put( "The vowel 'u' has a position of" );
  put( integer'image( vowels'pos( 'u' ) ) );
  put_line( " in the list." );
  put( "The vowel after 'a' is" );
  put( vowels'image( vowels'succ( 'a' ) ) );
  put_line( "." );

  -- Using a regular enumerated.  Where an identifer belongs to
  -- two enumerated types, we have to apply a type qualifier when
  -- ambiguity comes up.

  put( "The item before German_Shepherd is " );
  put( aDogBreed'image( aDogBreed'pred( German_Shepherd ) ) );
  put_line( "." );
  put( "The item after Labrador (the region) is " );
  put( aCanadianRegion'image( aCanadianRegion'succ( Labrador ) ) );
  put_line( "." );
  put_line( "Listing of cold regions between 'Labrador' and 'Other':" );

  for cr in coldPlaces'(Labrador)..other loop
      put_line( aCanadianRegion'image( cr ) );
  end loop;

  -- Using an enumerated with assigned numbers.  To get the number
  -- we assigned, we need unchecked_conversion.

  put( "Error code " & anErrorCode'image( IOerror ) );
  put( " is in position" & integer'image(anErrorCode'pos( IOerror ) ) );

  put_line( "." );
  put( "IOerror has a value of" & integer'image( toInteger( IOerror ) ) );
  put_line( "." );
  put( "The code before IOerror is " );
  put( anErrorCode'image( anErrorCode'pred( IOerror ) ) );
  put_line( "." );
  put( "The code after NONE is " );
  put( anErrorCode'image( anErrorCode'succ( None ) ) );
  put_line( "." );
end enumeration_fun;

The vowel 'u' has a position of 4 in the list.
The vowel after 'a' is'e'.
The item before German_Shepherd is LABRADOR.
The item after Labrador (the region) is OTHER.
Listing of cold regions between 'Labrador' and 'Other':
Error code IOERROR is in position 1.
IOerror has a value of 7.
The code before IOerror is NONE.
The code after NONE is IOERROR.

The boolean type is implemented as an enumerated with two values, true and false. False is always the predecessor of true.

C: Ada enumerated types have more features than C's. You can use 'pred and 'succ to move through the list without casting the enumerated as an integer and using arithmetic. The position of an enumerated identifer is independent of its assigned value.

10.10 Procedures and Functions

Ada Statement
C Equivalent
A subprogram that returns no value for expressions.
void f(...);
A subprogram that returns a value for expressions..
sometype f(...);
A nested block

There are several ways to break up an Ada program. First, a procedure, such as the main program, is a subprogram which returns no value.

procedure print_test is

  Text_IO.Put_Line( "This is a test" );
end print_test;

C: a procedure is a void function.
The second is a function. A function is a procedure that can be used in a expressions because it returns a value. The value is returned with a return statement.
function AddOne( X : integer ) return integer is
  return X+1;
end AddOne;
AddOne adds one to whatever is in the brackets. To add one to a variable called subtotal, you'd use it like this:

  Total := AddOne( SubTotal );

The value in the brackets is the parameter to the function. Parameters have modes: in, out or in out. In, which is the default if you specify a mode, means that the variable is treated as a constant. Out means the value is returned when the subprogram is finished. In out means the value goes into the subprogram, is changed, and is returned again when the subprogram is finished.

In Ada, functions can only have in parameters, but procedures can have all three.

procedure AddOne( x : in out integer ) is

  X := x + 1
end AddOne;
AddOne( Subtotal );

C: There is no equivalent of pass-by-copy or pass-by-reference. See the section on interfacing C and Ada.
There is also a special access mode, which means that the parameter must be an access variable. This is especially useful with tagged records. You can also get around the in out restriction on functions with the access mode. It is discussed in 11.10.2.
C: An access variable is basically a pointer. These are described later.
An example of multiple parameters:
procedure DisplayCurrency( c : aCurrency;
  fieldWidth : integer; useDollarSign : boolean := true ) is
useDollarSign, the third parameter, has a default value of true. Here's now you can call this procedure:

DisplayCurrency( 1.97, 8 );

DisplayCurrency( 1.97, 8, false );
DisplayCurrency( 1.97, 8, useDollarSign => false );
DisplayCurrency( c => 1.97, fieldWidth => 8, useDollarSign => false );
If you really wanted to, you can also change the order of the parameters using the => convention.

  DisplayCurrency( fieldWidth => 8, useDollarSign => false, c => 1.97 );

You can also declare arbitrary blocks in Ada. These let you declare variables in the middle of a procedure or function or set apart the designated source code in it's own block. The form of a block is an optional declare section, and the block denoted by a begin and end.

procedure nested is

  Date : integer;
  Date := 0;
    DaysInYear : constant integer := 365;
    Date := Date + DaysInYear;
end nested;
Here, DaysInYear only exists for the assignment statement that follows it.

The main reason for blocks is to add an exception hander to a particular line without having to write a one line procedure.


    Total := Total / Average;
  exception when numeric_error =>
    Text_IO.Put_Line( "Division by zero!" );
    Total := 0;
Here, if there's a numeric error during the division statement, it's caught and handled.

Operators are in-fix functions, ones that take parameters on their left and right. For example, "+" is an operator. Ada lets you redefine most of the standard operators so they work with types of your choosing. You enclose the operators' symbol in double quotes.

function "+"( e : employeeRecord, s : aSalary ) returns aSalary is

  return e.salary + s;
end function;
The above function will let you add a salary to an employee record, which assumes you are referring to the salary field in the employee record.

Ada subprograms can have the same name as long as their parameters or return values are different. This is called overloading. If Ada can't determine which subprogram you are referring to, you'll receive an error when compiling. In the above example, "+" is overloaded since there's integer addition, floating addition, and the other built-in meanings for "+", and our special salary addition we just defined.

C: Assignment is not an operator in Ada. Assignment overloading can be simulated with controlled tagged records and the Adjust procedure.

10.11 Control of Flow

Ada Statement
C Equivalent
Conditional execution
Interative loop
Pretest loop
Indefinite loop
Loop exit
Multiple case conditional execution
Unconditional jump

The if statement is, well, a standard if statement which you can find in many languages. Here's an example:

if x > 0 then

  Text_IO.Put_Line( "X is positive" );
elsif x < 0 then
  Text_IO_Put_Line( X is negative );
  Text_IO.Put_Line( "X is zero" );
end if;
Ada provides two expression short-circuiting operators: "or else" and "and then". Short-circuiting means that the expression will not be evaluated if the left side doesn't satisfy the condition.

  if x > 0 or else y > 0 then

In this case, y > 0 is only checked if x is not greater than zero.

There is a general purpose loop statement, loop. Loops are exited with an exit statement. Ada provides a shorthand, exit when, to exit on a condition.


  X := X / 2.5;
  exit when X < 4.0;
  X := X + 1.0;
end loop;

C: There is no general purpose loop in C.
C: There's no equivalent of the C continue statement.
There is a pretest loop, while, which determines whether or not the loop should be entered or reentered based on an expression at the top of the loop;
  while X >= 4.0 loop
    x := ( x / 2.5 ) + 1.0;
  end loop;

C: This is the equivalent of a C while loop. There is no post-test loop, like C's do loop.
There is the standard for loop as well, to loop through a range of numbers. For loops in Ada may only loop by discrete numbers one unit at a time: no real numbers and no arbitrary stepping values. To go backwards through a range, use the word reverse.
  for I in 1..10 loop
    Total := Total + 1;
  end loop;
To loop through an entire range of a type, use the 'range attribute. To loop through all the dogs in an enumerated type called aDogBreed,

  for dog in aDogBreed'range loop

Also note that dog is implicitly defined. You don't have to declare it. Ada understands the type from the loop and the loop variable exists for the duration of the loop.

C: For is much more structured than C's for.
Any loop can be exited with exit (or exit when). Ada allows you to label loops in order to exit out of several loops at once. In the following example, exit will exit the current loop and all loops up to and including OuterLoop. In this case, that's both loops.
OuterLoop: while y > 0 loop
  while x > 0 loop
    x := x - y;
    if x = 37 then
      exit OuterLoop;
    end if;
  end loop;
  y := y * 2;
  end loop;
There is a case statement as well, for testing a lot of different individual values. Ada requires a when others case to make sure that all possible cases are handed.

case DogBreed is

when Unknown =>
  Text_IO.Put_Line( "I don't know the breed" );
when Shepherd =>
  Text_IO.Put_Line( "It's a shepherd" );
when others =>
  Text_IO.Put_Line( "It's something else" );
end case;

Ada also as a null statement, which is a placeholder to use when a statement is expected but none is needed. For example, you can't have an empty if statement--there must be at least a "null;". Multiple cases can be included with the vertical bar, or a range can be specified with an ellipsis.

case TaxType is

when local_tax => Tax := Tax + LocalTax;
when federal_tax | govt_taxable => Tax := Tax + FederalTax;
when others => null; -- perhaps a warning would be better here instead
end case;

C: case is like switch, but the cases don't fall through.
Cases can also use ranges, such as 1..10 or TaxSubtype'range.
I really like goto's, and through a stroke of good luck, Ada includes a goto.
Goto labels are denoted with double angle brackets (unlike loop labels that use a colon).

for I in 0..10 loop

  -- some computations here
  if emergency then
    goto Help;
  end if;
end loop;
-- stuff that must not be executed in an emergency
<<Help>> Text_IO.Put_Line( "We are now down here" );

  <--Last Chapter Table of Contents Next Chapter-->