The previous chapter introduced the new highlights of Ada 95. In contrast, this chapter gives an overview of the whole Ada language showing the overall framework within which the new features fit and thus also illustrates how Ada 95 is a natural evolutionary enhancement of Ada 83.
This describes two fundamental concepts of Ada: types,
which determine a set of values with associated operations, and objects,
which are instances of those types. Objects hold values.
Variables are objects whose values can be changed; constants are
objects whose values cannot be changed.
3.1.1 Objects and Their Types
Every object has an associated type. The type
determines a set of possible values that the object can contain, and the
operations that can be applied to it. Users write declarations to
define new types and objects.
3.1.2 Types, Classes and Views
Types in Ada can be categorized in a number of different ways.
There are elementary types, which cannot be decomposed further, and composite
types which, as the term implies, are composed of a number of components. The
most important form of composite type is the record which comprises a number of
named components themselves of arbitrary and possibly different types.
3.1.3 Operations and Overloading
There is a set of operations associated with each type. An
operation is associated with a type if it takes parameters of the type, or
returns a result of the type. Some operations are implicitly provided when a
type is defined; others are explicitly declared by the user.
For example, an attribute takes the form X'Attr, where X is
the name of an entity, and Attr is the name of an attribute of that
entity. Thus, Integer'First yields the lower bound of the type
Integer.
* Relational operators: = /= < <= > >=
* Binary adding operators: + - &
* Unary adding operators: + -
* Multiplying operators: * / mod rem
* Highest precedence operators: ** abs not
The explicitly declared operations of the type are the
subprograms that are explicitly declared to take a parameter of the
type, or to return a result of the type. There are two kinds of subprograms:
procedures and functions. A procedure defines a sequence of
actions; a procedure call is a statement that invokes those actions. A
function is like a procedure, but also returns a result; a function call is an
expression that produces the result when evaluated. Subprogram calls may be
indirect, through an access value.
3.1.4 Class Wide Types and Dispatching
Associated with each tagged type T is a class-wide
type T'Class. Class-wide types have no operations of their own.
However, users may define explicit operations on class-wide types. For
example
Continuing the example from above, we demonstrate both overloading and
dispatching
3.1.5 Abstraction and Static Evaluation
The emphasis on high performance in Ada applications, and the
requirement to support interfacing to special hardware devices, mean that Ada
programmers must be able to engineer the low-level mapping of algorithms and
data structures onto physical hardware. On the other hand, to build large
systems, programmers must operate at a high level of abstraction and compose
systems from understandable building blocks. The Ada type system and
facilities for separate compilation are ideally suited to reconciling these
seemingly conflicting requirements.
Statements are executed at run time to cause an
action to occur. Expressions are evaluated at run time to
produce a value of some type. Names are also evaluated at run time in
the general case; names refer to objects (containing values) or to other
entities such as subprograms and types. Declarations are
elaborated at run time to produce a new entity with a given name.
3.2.1 Declarative Parts
Several constructs of the language contain a declarative
part followed by a sequence of statements. For example, a procedure
body takes the form
3.2.2 Assignments and Control Structures
An assignment statement causes the value of a variable
to be replaced by that of an expression of the same type. Assignment is
normally performed by a simple bit copy of the value provided by the
expression. However, in the case of nonlimited controlled types, assignment
can be redefined by the user.
3.2.3 Expressions
Expressions may appear in many contexts, within both
declarations and statements. Expressions are similar to expressions in most
programming languages: they may refer to variables, constants and literals, and
they may use any of the value-returning operations described in 3.1.3. An
expression produces a value. Every expression has a type that is known at
compile time.
Ada was designed specifically to support the construction of
large, complex, software systems. Therefore, it must allow the composition of
programs from small, understandable building blocks, while still allowing
programmers to engineer the low-level mapping of algorithms and data structures
onto physical hardware. Ada provides support for modern software development
techniques with the following capabilities
3.3.1 Program Units
Ada programs are composed of the following kinds of program
units
As we shall see later, packages at the so-called library level may have child
units. Ada has a hierarchical structure both at the external level of
compilation and internal to a program unit.
3.3.2 Private Types and Information Hiding
Packages support information hiding in the sense that users of
the package cannot depend on the implementation details that appear in the
package body. Private types provide additional information-hiding
capability. By declaring a type and its operations in the visible part of a
package specification, the user can create a new abstract data type.
3.3.3 Object Oriented Programming
Modern software development practices call for building
programs from reusable parts, and for extending existing systems. Ada supports
such practices through object oriented programming features. The basic
principle of object oriented programming is to be able to define one part of a
program as an extension of another, pre-existing, part. The basic building
blocks of object-oriented programming were discussed in 3.1.
3.3.4 Generic Units
Generic program units allow parameterization of program units.
The parameters can be types and subprograms as well as objects. A normal
(non-generic) program unit is produced by instantiating a generic unit;
the normal program unit is said to be an instance of the generic unit.
An instance of a generic package is a package; an instance of a generic
subprogram is a subprogram.
3.3.5 Separate Compilation
Ada allows the specifications and bodies of program units to be
separately compiled. A separately compiled piece of code is called a
compilation unit. The Ada compiler provides the same level of
compile-time checking across compilation units as it does within a single
compilation unit. For example, in a procedure call, the actual parameters must
match the types declared for the formal parameters. This rule is checked
whether the procedure declaration and the procedure call are in the same
compilation unit, or different compilation units.
3.3.6 Library Units
A program environment contains information concerning a
collection of library units. Library units may be packages,
subprograms, or generic units.
3.3.7 Program Composition
An executable software system is known in Ada as a
program. A program is composed of one or more compilation units.
A program may be divided into separate partitions, which may represent
separate address spaces. Implementations may provide mechanisms for
user-defined inter-partition communication. The Distributed Systems annex
defines a minimal standard interface for such communication. Partitions are
intended to support distributed processing, as explained in the annex. Of
course, many programs will not be partitioned; such programs consist of a
single partition.
3.3.8 Interfacing to Other Languages
Large programs are often composed of parts written in several
languages. Ada supports this by allowing inter-language subprogram calls, in
both directions, and inter-language variable references, in both directions.
The user specifies these interfaces using pragmas.
Ada tasking provides a structured approach to concurrent
processing under the control of an Ada run-time system, which provides services
such as scheduling and synchronization. This describes tasks and the methods
that are used for synchronizing task execution and for communicating between
tasks.
3.4.1 Tasks
Tasks are entities whose execution may proceed in parallel. A
task has a thread of control. Different tasks proceed independently, except at
points where they synchronize.
3.4.2 Communication and Synchronization
For multiple tasks to cooperate, there must be mechanisms that
allow the tasks to communicate and to synchronize their execution.
Synchronization and communication usually go hand-in-hand. Ada tasks
synchronize and communicate in the following situations
Protected types are used to synchronize access to shared data.
A protected type may contain components in a private part. Moreover, a
protected type may also contain functions, procedures, and entries the
protected operations of the protected type. The data being shared is
declared either as components of the protected type, or as global variables,
possibly designated by the components of the protected type. Protected types
are inherently limited.
3.4.4 Protected Operations and Entries
Protected types may export functions, procedures, and entries
as described above. Tasks may export entries. All of these operations are
called using similar syntax: OBJ.OP(...), where OBJ is the
name of the task or protected object, OP is the name of the operation,
and (...) represents any actual parameters. Information is passed
back and forth using in, in out,
out parameters. It is the responsibility of the programmer to
ensure that operations of protected objects execute for a bounded and short
period of time.
Entries may also be declared in the private part of a task or protected object
and thus not visible to external clients. Such entries may be called by
internal tasks or by requeuing.
3.4.5 Select Statements
Select statements are used to specify that a task is willing to
wait for any of a number of alternative events. Select statements take various
forms.
3.4.6 Timing
Ada provides features for measuring real time. A task may read
the clock to find out what time it is. A task may delay for a certain period
of time, or until a specific time.
3.4.7 Scheduling
Ada separates the concepts of synchronization and scheduling.
Synchronization operations determine when tasks must block, and when they are
ready to run. Scheduling is the method of allocating processors to ready
tasks. The default scheduling policy is defined by the language. The
Real-Time Systems annex defines another, priority-based, scheduling policy,
based on a dispatching model. Finally, implementations are allowed to add
their own policies, which can be specified by pragmas.
Most programs need to recover gracefully from errors that occur
during execution. Exception handling allows programs to handle such error
situations without ceasing to operate.
This exception might be used to represent the situation of a program
trying to insert data into a buffer which is already full.
Although the majority of program text can be written in a
machine-independent manner, most large software systems contain small portions
that need to depend on low-level machine characteristics. Ada allows such
dependence, while still allowing the high-level aspects of the algorithms and
data structures to be described in an abstract manner. Many of an
implementation's machine-specific characteristics are accessible through the
package System. This defines storage-related types, an
Address type, and relational and arithmetic operations on addresses.
3.6.1 Pragmas
A pragma is used to convey information to the compiler;
it is similar to a compiler directive supported by other languages. A pragma
begins with the reserved word pragma, an identifier which is
the name of the pragma, and optionally one or more arguments.
3.6.2 Specifying Representations
Normally, the programmer lets the Ada compiler choose the most
efficient way of representing objects. However, Ada also provides
representation clauses, which allow the user to specify the
representation of an individual object, or of all objects of a type. Other
representation clauses apply to program units.
3.6.3 Unprotected Shared Variables
In Ada, variables may be shared among tasks according to the
normal visibility rules: if two tasks can see the name of the same variable,
then they may use that variable as shared data. However, it is up to the
programmer to properly synchronize access to these shared variables. In most
cases, data sharing can be achieved more safely with protected objects;
unprotected shared variables are primarily used in low-level systems
programming. Ada allows the user to specify certain aspects of memory
allocation and code generation that may affect synchronization by specifying
variables as volatile or atomic.
3.6.4 Unchecked Programming
Ada provides features for bypassing certain language
restrictions. These features are unchecked; it is the programmer's
responsibility to make sure that they do not violate the assumptions of the
rest of the program. For example, there are mechanisms for manipulating access
types that might leave dangling pointers, and there is a mechanism for
converting data from one type to another, bypassing the type-checking rules.
All implementations provide a standard library of various
packages. This includes the predefined package Standard which defines
the predefined types such as Integer, and the package System
which defines various entities relating to the implementation.
3.7.1 Input Output
Input-output capabilities are provided in Ada by predefined
packages and generic packages.
3.1 Objects, Types, Classes and Operations
All Types
|
+-------------------------+------------------------+
| |
Elementary Types Composite Types
| |
+------+----+ +---------+--------+------+
| | | | | |
access scalar array record protected task
|
+-------+--------------------------+
| |
| |
discrete real
| |
| __________________________________________________
+----+---|---------+ +----+-------+ |
| | | | | | Numeric Types
enumeration | integer float fixed |
| | | |
| | | |
| +-----+-------+ +-----+-----+ |
| | | | | |
| signed modular decimal ordinary |
| |
| |
|__________________________________________________|
Figure 3-1: Ada Type Hierarchy
type Display_Color is -- an enumeration type
(Red, Orange, Yellow, Green, Blue, Violet);
type Color_Mask is -- an array type
array (Display_Color) of Boolean;
type Money is -- a decimal fixed type
delta 0.01 digits 18;
type Payment is -- a record type
record
Amount: Money;
Due_Date: Date;
Paid: Boolean;
end record;
task type Device is -- a task type
entry Reset;
end Device;
type Dev is access Device; -- an access type
protected type Semaphore is -- a protected type
procedure Release;
entry Seize;
private
Mutex: Boolean := False;
end Semaphore;
type Animal is tagged
record
Species: Species_Name;
Weight: Grams;
end record;
and we may then declare
function Image(A: Animal) return String;
-- Returns a human-readable identification of an Animal
type Mammal is new Animal with
record
Hair_Color: Color_Enum;
end record;
and a corresponding
function Image(M: Mammal) return String;
-- Returns a human-readable identification of a Mammal
Animal'Class
|
+--------+--------+
| |
Reptile'Class Mammal'Class
|
|
|
Primate'Class
Figure 3-2: A Derivation Class Hierarchy
* Logical operators: and or xor
type Matrix is ...
for Matrix'Read use My_Matrix_Reader;
for Matrix'Write use My_Matrix_Writer;
the predefined operations are overridden by the user's own subprograms.
procedure Print(A: in Animal'Class);
-- Print human-readable information about an Animal
procedure Print(S: in String);
-- Print a string
procedure Print(A: in Animal'Class) is
-- Print information on an animal
begin
Print(Image(A));
end Print;
3.2 Statements, Expressions and Elaboration
procedure P(...) is
I: Integer := 1; -- this is the declarative part
...
begin
... -- this is the statement sequence
I := I * 2;
...
end P;
3.3 System Construction
-- this is a package specification:
package Example is
-- this is the visible part
-- declarations of exported entities appear here
type Counter is private;
procedure Reset(C: in out Counter);
procedure Increment(C: in out Counter);
--
private
-- this is the private part
-- declarations appearing here are not exported
type Counter is range 0 .. Max;
end Example;
-- this is the corresponding package body:
package body Example is
-- implementations of exported subprograms appear here
-- entities that are used only in the implementation
-- are also declared here
Zero: constant Counter := 0;
-- declaration of constant only used in the body
procedure Reset(C: in out Counter) is
begin
C := Zero;
end Reset;
procedure Increment(C: in out Counter) is
begin
C := C + 1;
end Increment;
end Example;
package Root is
-- specification of a root library unit
...
end Root;
-------------------------------
package Root.Child is
-- specification of a child library unitend Root.Child;
-------------------------------
package body Root.Child is
-- body of the child library unit
...
end Root.Child;
-------------------------------
private package Root.Local_Definitions is
-- a private child package specification
...
end Root.Local_Definitions;
-------------------------------
packagebody Root is
-- body of the root library unit
...
end Root;
3.4 Multitasking
Ada feature Synchronization Communication
Task Creation (not needed) Creator initializes discriminants
of new task
Task Activation Creator waits for tasks Activation failure might
being activated be reported
Task Termination Master waits for (none)
children
Rendezvous Entry caller and acceptor Entry parameters are passed between
wait for each other the entry caller and the acceptor
Protected Object Mutual exclusion during data Tasks communicate indirectly by
access; queued waiting reading and writing the components
for entry barriers of protected objects
Unprotected User-defined, low-level Reading and writing of shared
Shared Variables synchronization Variables
Table 3-1: Summary of Communication and Synchronization
3.4.3 Protected Objects
Both of these forms of select statement can also contain, either
3.5 Exception Handling
if Buffer_Index > Max_Buffer_Size then
raise Buffer_Full_Error;
end if;
begin
...
exception
when Buffer_Full_Error =>
Reset_Buffer;
when Error: others =>
Put_Line("Unexpected exception raised:");
Put_Line(Ada.Exceptions.Exception_Information(Error));
end;
3.6 Low Level Programming
3.7 Standard Library