Note that the concept of a "part" is intentionally vague here. A single package does not need to be independent of each other package in a reuse library, if the "parts" from that library which are typically reused are entire subsystems. If the entire subsystem is perceived as providing a useful function, the entire subsystem is reused. However, the subsystem should not be tightly coupled to all the other subsystems in the reuse library, so that it is difficult or impossible to reuse the subsystem without reusing the entire library. Coupling between reusable parts should only occur when it provides a strong benefit perceptible to the user.
Language Ref Manual references: 10.4 The Program Library
with
clauses on reusable parts, especially on their
specifications.
with
statements to reduce the number
of context clauses on a reusable part.
with
statements to import portions
of a package rather than the entire package.
------------------------------------------------------------------------ with Package_A; procedure Produce_And_Store_A is ... begin -- Produce_And_Store_A ... Package_A.Produce (...); ... Package_A.Store (...); ... end Produce_And_Store_A; ------------------------------------------------------------------------ |
can be rewritten as a generic unit:
------------------------------------------------------------------------ generic with procedure Produce (...); with procedure Store (...); procedure Produce_And_Store; ------------------------------------------------------------------------ procedure Produce_And_Store is ... begin -- Produce_And_Store ... Produce (...); ... Store (...); ... end Produce_And_Store; ------------------------------------------------------------------------ |
and then instantiated:
------------------------------------------------------------------------ with Package_A; with Produce_And_Store; procedure Produce_And_Store_A is new Produce_And_Store (Produce => Package_A.Produce, Store => Package_A.Store); ------------------------------------------------------------------------ |
with
) clauses specify the names of other units upon which this unit
depends. Such dependencies cannot and should not be entirely avoided, but it
is a good idea to minimize the number of them which occur in the specification
of a unit. Try to move them to the body, leaving the specification independent
of other units so that it is easier to understand in isolation. Also, organize
your reusable parts in such a way that the bodies of the units do not contain
large numbers of dependencies on each other. Partitioning your library into
independent functional areas with no dependencies spanning the boundaries of
the areas is a good way to start. Finally, reduce dependencies by using
generic formal parameters instead of with
statements, as shown in the example
above. If the units in a library are too tightly coupled, then no single part
can be reused without reusing most or all of the library.
The first (nongeneric) version of Produce_And_Store_A
above is difficult to
reuse because it depends on Package_A
which may not be general purpose or
generally available. If the operation Produce_And_Store
has reuse potential
which is reduced by this dependency, a generic unit and an instantiation
should be produced as shown above. Note that the with clause for Package_A
has
been moved from the Produce_And_Store
generic procedure which encapsulates the
reusable algorithm to the Produce_And_Store_A
instantiation. Instead of naming
the package which provides the required operations, the generic unit simply
lists the required operations themselves. This increases the independence and
reusability of the generic unit.
This use of generic formal parameters in place of with clauses also allows
visibility at a finer granularity. The with clause on the nongeneric version
of Produce_And_Store_A
makes all of the contents of Package_A
visible to
Produce_And_Store_A
, while the generic parameters on the generic version make
only the Produce
and Store
operations available to the generic instantiation.
Language Ref Manual references: 10.1.1 Context Clauses - With Clauses, 12.1 Generic Declarations
Elaborate
.
Elaborate
for generics named in a context clause.
Priority
in tasks hidden in reusable parts.
------------------------------------------------------------------------ generic ... package Stack is ... end Stack; ------------------------------------------------------------------------ with Stack; pragma Elaborate (Stack); -- in case the body is not yet elaborated package My_Stack is new Stack (...); ------------------------------------------------------------------------ package body Stack is begin ... end Stack; --------------------------------------------------------------------- |
Elaborate
controls the order of elaboration of one unit with respect to
However, as more compilers begin to allow generics to be instantiated before
the bodies are compiled, elaboration orders that generally follow compilation
order may result in program errors. By forcing the compiler to elaborate the
generic before the instantiation, this error can be avoided or possibly
identify a problem of circularity
(see 10.5 of Department of Defense 1983).
Pragma Priority
controls the priority of a task relative to all other tasks in
a particular system. It is inappropriate in a reusable part which does not
know anything about the requirements and importance of other parts of the
systems in which it is reused. Give careful consideration to a reusable part
which claims that it can only be reused if its embedded task has the highest
priority in the system. No two such parts can ever be used together.
Language Ref Manual references: 2.8 Pragmas, 9.1 Abort Statements, 9.8 Priorities, 10.1.1 Context Clauses - With Clauses, 10.5 Elaboration of Library Units
Language Ref Manual references: 12.1 Generic Declarations
------------------------------------------------------------------------ package Matrix_Math is ... type Algorithm is (Gaussian, Pivoting, Choleski, Tri_Diagonal); generic Which_Algorithm : in Algorithm := Gaussian; procedure Invert ( ... ); end Matrix_Math; ------------------------------------------------------------------------ package body Matrix_Math is ... --------------------------------------------------------------------- procedure Invert ( ... ) is ... begin -- Invert case Which_Algorithm is when Gaussian => ... ; when Pivoting => ... ; when Choleski => ... ; when Tri_Diagonal => ... ; end case; end Invert; --------------------------------------------------------------------- end Matrix_Math; ------------------------------------------------------------------------ |
This feature should be used when other factors prevent the code from being separated into separate subunits. In the above example, it would be preferable to have a different procedure for each algorithm. But the algorithms may differ in slight but complex ways so as to make separate procedures difficult to maintain.
Language Ref Manual references: 10.6 Program Optimization, 12.3 Generic Instantiation
with
'ed at compile time or read from a file at run-time. In
appropriate circumstances, table-driven programming provides a very powerful
way of creating general-purpose, easily tailorable, reusable parts.