9 Packages


Overview

Although its roots are in Pascal, Ada still borrowed heavily from other languages and was influenced by the latest in software engineering techniques discussed in the 1970's. Undoubtedly the major innovation from that period is the concept of packages. Separation of the specifications from the implementation of 'objects' results in far more flexible code. The containment of the data structures behind a wall of functional interfaces results in much more maintainable systems. This chapter discusses Ada's implementation of packages.

Packages are not about how a program will run, but about how it is constructed and how it is to be understood and maintained.


Package specifications

The package specification of an Ada package describes all the subprogram specifications, variables, types, constants etc that are visible to anyone who 'withs' the package into thier own code.

The following is an example of a package specification.

The items described in the package specification are often described as resources. We can access these values by 'with'ing the package in our code and then using the package name, a dot, and the name of the resource wanted, whether it is a type declaration, a function or a variable name.

It should be noted that accessing resources in a package is textually identical to accessing the fields in a record.

As accessing the resources of a package using the full dot notation can be cumbersome, the use clause may be employed. When a package is 'use'd in this way the resources are available as though they were declared directly in the code.

If two packages are with'ed and use'd in the one compilation unit (e.g. subprogram or another package) then there is the possibility of a name clash between the resources in the two packages. In this case the ambiguity can be removed by reverting to dot notation for the affected resources.

Another problem encountered is when local resources 'hide' the resources in a use'd package. In this case the ambiguity can still be removed by using dot notation.


Package body

The package body is where all the implementation details for the services specified in the package specification are placed. As in the specification the package body can contain type declarations, object (variable) declarations, subprograms etc.

The format of a package body is

The resources in the body of the package are unavailable for use by any other package. Any attempt to reference them will result in a compiler error.

The variables declared in the package body retain their value between successive calls to the public sub procedures. As a result we can create packages that store information for later use.

The begin/end at the end of the package contain initialisation statements for the package. These are executed before the main procedure is run. This is true for all packages. The order in which different package's initialisation statements are run is not defined.

All the resources specified in the package specification are available in the package body without the use of a with clause.


Private types

So far all the types declared in the package specification have been completely visible to the user of the package.

Sometimes when creating packages we want to maintain total control over the manipulation of objects. For instance in a package that controls accounts in general ledger, we only want to provide the facilities to make withdrawals, deposits and creation of accounts. No other package needs to know or should have access to the representation details of the account object.

In Ada packages type declarations (and also constants) can be declared private.

Outside the package the only operations that can be performed on a private type are assignment, tests for equality and those operations defined by subprograms in the package specification.

Full details of the representation details are available in the package body. Any routine in the package body can access and modify the private type as though it were not private - the privacy of an object applies only outside the package.

It may seem a contradiction to put the private specifications in the package specifications - the public part of the package when you are trying to hide the representation details of an object. This is required for programs that allocate an object of that private type- the compiler then knows how much space to allocate.

Although the reader of the package specifications can see what the representation of the private type really is, there is no way he or she can make explicit use of the knowledge.

Objects can be created outside a package even if of a private type.

E.g.


Limited private types

A private type can be made even more private by removing the ability to compare and assign the values outside the package. You generally then have to provide a function to test equality (you can overload the equality operator =, this implicity overloads /= as well). Also you may have to create a procedure to perform assignments.

Q Why do we need private types?

A Consider the following:

In this record the length field determines the number of characters in the field value that have meaning. Any characters from position length+1 to maximum_length are ignored by us when using the record. However if we ask the computer to compare two records of this type, it does not know the significance of the field length; it will compare the length field and all of the value field. Clearly in this situation we need to write a comparison function.
Ada95 allows the programmer to override the equality operator for all types.


Deferred constants

In some package specifications we may wish to declare a constant of a private type. In the same manner as declaring a private type (and most forward references) we give an incomplete declaration of the constant - the compiler expects the rest to follow in the private section of the package specifications.


Child units (Ada95)

It was found in large system developments that a single package specification could grow extraordinarily large, with subsequent costs in recompilation of compilation units that depend on it if it were to change. To remedy this situation, the concept of child compilation units was conceived. Child units allow a logically single package to be broken into several physically distinct packages and subprograms. As well as solving the problem of large recompilations, they also provide a convenient tool for providing multiple implementations for an abstract type and for producing self contained subsystems, by using private child units.


Extending an existing package

A package that consists of a set of declarations may need to be extended at some time. For example a stack package may need the addition of a peek facility. If this package is heavily "withed" by many other units, then modifying the specification to include this extra features could result in a large recompilation despite the fact that most clients would not be using the new feature.

A child package can be declared that logically extends the package, but in a physically separate manner.

For example

The package stacks.more_stuff is a child packge of stacks. It has the visibility of all the declarations preceeding point A, that is the parent package presents the same visibility to a child package as it does to its body. The child package can see all of its parents private parts. The package bodies would be compiled separately.

The clients who wish to use the function peek can simply have a with clause for the child package,

'With'ing the child package automatically causes the parent package to be 'with'ed, and its parent package to be 'with'ed, and so on. However the use clause does not act in this way. Visibiliy can only be gained on a package by package basis. This is no doubt a pragmatic decision based on what most organisations would prefer (as indicated by existing use of the 'use' clause).

A package can have child functions and procedures. Rules for use of these are easily inferred.


Private child units

Private child units allow a child unit to be created that is only visible within the hierachy of the parent package. In this way facilities for a subsystem can be encapsulated within its a hidden package, with the compilation and visibility benefits that this entails.

Because they are private, a child package's specification is allowed to advertise, in its specification, private parts of its parents. This would not normally be allowed as it would allow the breaking of the hidden implementation of a parent's private parts.

The procedure stack.statistics.increment_push_count could be called from within the implementation of the stacks package; this procedure is not available to any clients external to this package hierachy.


to the index...