Ada 95 Quality and Style Guide Chapter 7
Back to sections 7.0 through 7.4.4
7.4.5 Task Scheduling Algorithm
guideline
- Do not assume that tasks execute uninterrupted
until they reach a synchronization
point.
- Use pragma Priority
to distinguish general levels of importance only (see Guideline
6.1.6).
rationale
notes
exceptions
7.4.6 Abort
guideline
- Avoid using the abort statement.
rationale
7.4.7 Unprotected Shared Variables and Pragmas Atomic and Volatile
guideline
- Do not use unprotected shared variables.
- Consider using protected types to provide data synchronization.
- Have tasks communicate
through the rendezvous
mechanism.
- Do not use unprotected shared variables as a task
synchronization device.
- Consider using protected objects to encapsulate shared data.
- Use pragma Atomic or Volatile only
when you are forced to by run-time system deficiencies.
example
rationale
7.5 EXCEPTIONS
You should exercise care when using predefined exceptions because
aspects of their treatment may vary between implementations. Implementation-specific
exceptions must, of course, be avoided. See Guidelines 4.3 and
5.8 for further information on exceptions. See Guideline 7.1.6
for further information on vendor-supplied features.
7.5.1 Predefined and User-Defined Exceptions
guideline
- Do not depend on the exact locations at which predefined exceptions
are raised.
- Do not rely on the behavior of Ada.Exceptions beyond
the minimum defined in the language.
rationale
7.5.2 Implementation-Specific Exceptions
guideline
- Do not raise implementation-specific exceptions.
- Convert implementation-specific exceptions within interface
packages
to visible user-defined exceptions.
rationale
7.6 REPRESENTATION CLAUSES AND IMPLEMENTATION-DEPENDENT FEATURES
Ada provides many implementation-dependent features that permit
greater control over and interaction with the underlying hardware
architecture than is normally provided by a high-order language.
These mechanisms are intended to assist in systems programming
and real-time programming to obtain greater efficiency (e.g.,
specific size and layout of variables through representation clauses)
and direct hardware interaction (e.g., interrupt entries) without
having to resort to assembly level programming.
Given the objectives for these features, it is not surprising
that you must usually pay a significant price in portability to
use them. In general, where portability is the main objective,
do not use these features. When you must use these features, encapsulate
them in packages that are well-commented as interfacing to the
particular target environment. This section identifies the various
features and their recommended use with respect to portability.
7.6.1 Representation Clauses
guideline
- Use algorithms that do not depend
on the representation of the data and,
therefore, do not need representation clauses.
- Consider using representation clauses when accessing or defining
interface data or when a specific representation is needed to
implement a design.
- Do not assume that sharing source files between programs guarantees
the same representation of data types in those files.
rationale
- A change in a file earlier in the compilation order
- A change in the optimization strategy or level
- A change in versions of the compiler
- A change in actual compilers
- A change in the availability of system resources
Therefore, two independently linked programs or partitions should
only share data that has their representations explicitly controlled.
notes
7.6.2 Package System
guideline
- Avoid using package System constants except in attempting
to generalize other machine-dependent constructs.
rationale
notes
exceptions
7.6.3 Machine Code Inserts
guideline
- Avoid machine code inserts.
rationale
exceptions
7.6.4 Interfacing to Foreign Languages
guideline
- Use the package Interfaces and its language-defined
child packages rather than implementation-specific mechanisms.
- Consider using pragma Import rather than access-to-subprogram
types for interfacing to subprograms in other languages.
- Isolate all subprograms employing pragmas Import, Export,
and Convention
to implementation-specific (interface) package
bodies.
example
rationale
exceptions
7.6.5 Implementation-Specific Pragmas and Attributes
guideline
- Avoid pragmas and attributes added by the compiler implementor.
rationale
7.6.6 Unchecked Deallocation
guideline
- Avoid dependence on Ada.Unchecked_Deallocation (see
Guideline 5.9.2).
rationale
notes
exceptions
7.6.7 Unchecked Access
guideline
- Avoid dependence on the attribute Unchecked_Access
(see Guideline 5.9.2).
rationale
7.6.8 Unchecked Conversion
guideline
- Avoid dependence on Ada.Unchecked_Conversion (see Guideline
5.9.1).
rationale
exceptions
7.6.9 Run-Time Dependencies
guideline
- Avoid the direct invocation of or implicit dependence
upon an underlying host operating
system or Ada run-time support
system, except where the interface is explicitly defined in the
language (e.g., Annex C or D of the Ada Reference Manual [1995]).
- Use standard bindings and the package Ada.Command_Line
when you need to invoke the underlying
run-time support system.
- Use features defined in the Annexes rather than vendor-defined
features.
rationale
exceptions
7.7 INPUT/OUTPUT
I/O facilities in Ada are not a part of the syntactic definition
of the language. The constructs in the language have been used
to define a set of packages for this purpose. These packages are
not expected to meet all the I/O needs of all applications, in
particular, embedded systems. They serve as a core subset that
may be used on straightforward data and that can be used as examples
of building I/O facilities upon the low-level constructs provided
by the language. Providing an I/O definition that could meet the
requirements of all applications and integrate with the many existing
operating systems would result in unacceptable implementation
dependencies.
The types of portability problems encountered with I/O tend to
be different for applications running with a host operating system
versus embedded targets where the Ada run-time is self-sufficient.
Interacting with a host operating system offers the added complexity
of coexisting with the host file system structures (e.g., hierarchical
directories), access methods (e.g., indexed sequential access
method [ISAM]), and naming conventions (e.g., logical names and
aliases based on the current directory). The section on Input/Output
in ARTEWG (1986) provides some examples of this kind of dependency.
Embedded applications have different dependencies that often tie
them to the low-level details of their hardware devices.
The major defense against these inherent implementation dependencies
in I/O is to try to isolate their functionality in any given application.
The majority of the following guidelines are focused in this direction.
7.7.1 Name and Form Parameters
guideline
- Use constants and variables as symbolic actuals for the Name
and Form parameters on the predefined
I/O packages. Declare and initialize them in an implementation
dependency package.
rationale
notes
7.7.2 File Closing
guideline
- Close all files explicitly.
rationale
7.7.3 Input/Output on Access Types
guideline
- Avoid performing I/O on access types.
rationale
7.7.4 Package Ada.Streams.Stream_IO
guideline
- Consider using Sequential_IO or Direct_IO
instead of Stream_IO unless you need the low-level, heterogeneous
I/O features provided by Stream_IO.
rationale
7.7.5 Current Error Files
guideline
- Consider using Current_Error and Set_Error
for run-time error messages.
example
rationale
notes
7.8 SUMMARY
fundamentals
- In programs or components intended to have a long life, avoid
using the features of Ada declared as "obsolescent"
by Annex J of the Ada Reference Manual (1995), unless the use
of the feature is needed for backward compatibility with Ada 83
(Ada Reference Manual 1983).
- Document the use of any obsolescent features.
- Avoid using the following features:
- The short renamings of the packages in the predefined environment
(e.g., Text_IO as opposed to Ada.Text_IO)
- The character replacements of ! for |, :
for #, and % for quotation marks
- Reduced accuracy subtypes of floating-point types
- The 'Constrained attribute as applied to private types
- The predefined package ASCII
- The exception Numeric_Error
- Various representation specifications, including at
clauses, mod clauses, interrupt entries, and the Storage_Size
attribute
- Make informed assumptions about the support provided for the
following on potential target platforms:
- Number of bits available for type Integer
(range constraints)
- Number of decimal digits of precision
available for floating-point types
- Number of bits available for fixed-point
types (delta and range constraints)
- Number of characters per line of source text
- Number of bits for Root_Integer expressions
- Number of seconds for the range of Duration
- Number of milliseconds for Duration'Small
- Minimum and maximum scale for decimal types
- Avoid assumptions about the values and the number of values
included in the type Character.
- Use highlighting comments for
each package, subprogram,
and task where any nonportable
features are present.
- For each nonportable feature employed, describe the expectations
for that feature.
- Consider using only a parameterless
procedure as the main subprogram.
- Consider using Ada.Command_Line for accessing values
from the environment, but recognize that this package's behavior
and even its specification are nonportable.
- Encapsulate and document all uses of package Ada.Command_Line.
- Create packages specifically designed to isolate hardware and
implementation dependencies and designed so that their specification
will not change when porting.
- Clearly indicate
the objectives if machine or solution efficiency is the reason
for hardware or implementation-dependent code.
- For the packages that hide implementation dependencies, maintain
different package bodies
for different target environments.
- Isolate interrupt receiving
tasks into implementation-dependent
packages.
- Refer to Annex M of the Ada Reference Manual (1995) for a list
of implementation-dependent features.
- Avoid the use of vendor-supplied packages.
- Avoid the use of features added to the predefined
packages that are not specified in the Ada language definition
or Specialized Needs Annexes.
- Use features defined in the Specialized Needs Annexes rather
than vendor-defined features.
- Document clearly the use of any features from the Specialized
Needs Annexes (systems programming, real-time systems, distributed
systems, information systems, numerics, and safety and security).
- Do not write code whose correct execution depends on the particular
parameter passing mechanism used by an implementation (Ada Reference
Manual 1995, §6.2; Cohen 1986).
- If a subprogram has more than one formal parameter of a given
subtype, at least one of which is [in] out, make sure
that the subprogram can properly handle the case when both formal
parameters denote the same actual object.
- Avoid depending on the order in which certain constructs in
Ada are evaluated.
numeric types and expressions
- Avoid using the predefined numeric types in package Standard.
Use range and digits
declarations and let the implementation pick the appropriate representation.
- For programs that require greater accuracy
than that provided by the global assumptions, define a package
that declares a private type
and operations as needed; see Pappas (1985) for a full explanation
and examples.
- Consider using predefined numeric types (Integer, Natural,
Positive) for:
- Indexes into arrays where the index type is not significant,
such as type String
- "Pure" numbers, that is, numbers with no associated
physical unit (e.g., exponents)
- Values whose purpose is to control a repeat or iteration count
- Use an implementation that supports the Numerics Annex (Ada
Reference Manual 1995, Annex G) when performance and accuracy
are overriding concerns.
- Carefully analyze what accuracy
and precision you really need.
- Do not press the accuracy limits of the machine(s).
- Comment the analysis
and derivation of the numerical aspects of a program.
- Anticipate the range of values of subexpressions to avoid exceeding
the underlying range of their base type. Use derived types, subtypes,
factoring, and range constraints on numeric types.
- Consider using <= and >= to do relational
tests on real valued arguments, avoiding the <, >,
=, and /= operations.
- Use values of type attributes
in comparisons and checking for small values.
- In information systems, declare different numeric decimal types
to correspond to different scales (Brosgol, Eachus, and Emery
1994).
- Create objects of different decimal types to reflect different
units of measure (Brosgol, Eachus, and Emery 1994).
- Declare subtypes of the appropriately scaled decimal type to
provide appropriate range constraints for application-specific
types.
- Encapsulate each measure category in a package (Brosgol, Eachus,
and Emery 1994).
- Declare as few decimal types as possible for unitless data (Brosgol,
Eachus, and Emery 1994).
- For decimal calculations, determine whether the result should
be truncated toward 0 or rounded.
- Avoid decimal types and arithmetic on compilers that do not
support the Information Systems Annex (Ada Reference Manual 1995,
Annex F) in full.
storage control
- Do not use a representation clause to specify number of storage
units.
- Do not compare access-to-subprogram values.
- Consider using explicitly defined storage pool mechanisms.
tasking
- Do not depend on the order in which task objects are activated
when declared in the same declarative list.
- Do not depend on a particular delay being achievable (Nissen
and Wallis 1984).
- Never use knowledge of the execution
pattern of tasks to achieve timing requirements.
- Do not assume a correlation between System.Tick and
type Duration.
- Do not depend on the order in which guard
conditions are evaluated or on the algorithm for choosing among
several open select alternatives.
- Do not assume that tasks execute uninterrupted
until they reach a synchronization
point.
- Use pragma Priority
to distinguish general levels of importance only.
- Avoid using the abort statement.
- Do not use unprotected shared variables.
- Consider using protected types to provide data synchronization.
- Have tasks communicate
through the rendezvous
mechanism.
- Do not use unprotected shared variables as a task
synchronization device.
- Consider using protected objects to encapsulate shared data.
- Use pragma Atomic or Volatile only
when you are forced to by run-time system deficiencies.
exceptions
- Do not depend on the exact locations at which predefined exceptions
are raised.
- Do not rely on the behavior of Ada.Exceptions beyond
the minimum defined in the language.
- Do not raise implementation-specific exceptions.
- Convert implementation-specific exceptions within interface
packages to visible
user-defined
exceptions.
representation clauses and implementation-dependent features
- Use algorithms that do not depend
on the representation of the data and,
therefore, do not need representation clauses.
- Consider using representation clauses when accessing or defining
interface data or when a specific representation is needed to
implement a design.
- Do not assume that sharing source files between programs guarantees
the same representation of data types in those files.
- Avoid using package System constants except in attempting
to generalize other machine-dependent constructs.
- Avoid machine code inserts.
- Use the package Interfaces and its language-defined
child packages rather than implementation-specific mechanisms.
- Consider using pragma Import rather than access-to-subprogram
types for interfacing to subprograms in other languages.
- Isolate all subprograms employing pragmas Import, Export,
and Convention
to implementation-specific (interface) package
bodies.
- Avoid pragmas and attributes added by the compiler implementor.
- Avoid dependence on Ada.Unchecked_Deallocation.
- Avoid dependence on the attribute Unchecked_Access.
- Avoid dependence on Ada.Unchecked_Conversion.
- Avoid the direct invocation of or implicit dependence
upon an underlying host operating
system or Ada run-time support
system, except where the interface is explicitly defined in the
language (e.g., Annex C or D of the Ada Reference Manual [1995]).
- Use standard bindings and the package Ada.Command_Line
when you need to invoke the underlying
run-time support system.
- Use features defined in the Annexes rather than vendor-defined
features.
input/output
- Use constants and variables as symbolic actuals for the Name
and Form parameters on the predefined
I/O packages. Declare and initialize them in an implementation
dependency package.
- Close all files explicitly.
- Avoid performing I/O on access types.
- Consider using Sequential_IO or Direct_IO
instead of Stream_IO unless you need the low-level, heterogeneous
I/O features provided by Stream_IO.
- Consider using Current_Error and Set_Error
for run-time error messages.