Language Ref Manual references: 8 Visibility Rules
In this section... 4.2.1 Minimization of Interfaces 4.2.2 Nested Packages 4.2.3 Restricting Visibility 4.2.4 Hiding Tasks |
Summary of Guidelines from this section |
with
) clauses in a package specification.
------------------------------------------------------------------------- package Telephone_Book is type Listing is limited private; procedure Set_Name (New_Name : in String; Current : in out Listing); procedure Insert (Additional : in Listing); procedure Delete (Obsolete : in Listing); private type Information; type Listing is access Information; end Telephone_Book; ------------------------------------------------------------------------- package body Telephone_Book is -- Full details of record for a listing type Information is record ... Next : Listing; end record; First : Listing; procedure Set_Name (New_Name : in String; Current : in out Listing) is separate; procedure Insert (Additional : in Listing) is separate; procedure Delete (Obsolete : in Listing) is separate; end Telephone_Book; ------------------------------------------------------------------------- |
Pushing as many as possible of the context dependencies into the body makes the reader's job easier, localizes the recompilation required when library units change, and helps prevent a ripple effect during modifications. See also Guideline 4.2.3.
Subprograms with large numbers of parameters often indicate poor design decisions (e.g., the functional boundaries of the subprogram are inappropriate, or parameters are structured poorly). Conversely, subprograms with no parameters are likely to be accessing global data.
Objects visible within package specifications can be modified by any unit that has visibility to them. The object cannot be protected or represented abstractly by its enclosing package. Objects which must persist should be declared in package bodies. Objects whose value depends on program units external to their enclosing package are probably either in the wrong package or are better accessed by a subprogram specified in the package specification.
Language Ref Manual references: 6.1 Subprogram Declarations, 6.6 Parameter and Result Type Profile - Overloading of Subprograms, 7.1 Package Structure, 7.2 Package Specifications and Declarations, 7.3 Package Bodies, 8.3 Visibility, 10.1.1 Context Clauses - With Clauses
Integer_IO
, Float_IO
, Fixed_IO
, and Enumeration_IO
are nested within the
specification of package Text_IO
. Each of them is a generic, grouping closely
related operations and needing to use hidden details of the implementation of
Text_IO
.
An abstraction occasionally needs to present different views to different classes of users. Building one view upon another as an additional abstraction does not always suffice, because the functionality of the operations presented by the views may be only partially disjoint. Nesting specifications groups the facilities of the various views, yet associates them with the abstraction they present. Abusive mixing of the views by another unit would be easy to detect due to the multiple use clauses or an incongruous mix of qualified names.
Language Ref Manual references: 7.2 Package Specifications and Declarations, 12.1 Generic Declarations, 14.3 Text Input-Output, 14.3.10 Specification of the Package Text_IO
with
clauses apply.
with
those units directly needed.
------------------------------------------------------------------------- procedure Compiler is ---------------------------------------------------------------------- package Listing_Facilities is procedure New_Page_Of_Listing; procedure New_Line_Of_Print; ... end Listing_Facilities; ---------------------------------------------------------------------- package body Listing_Facilities is separate; begin -- Compiler ... end Compiler; ------------------------------------------------------------------------- with Text_IO; separate (Compiler) package body Listing_Facilities is ---------------------------------------------------------------------- procedure New_Page_Of_Listing is begin ... end New_Page_Of_Listing; ---------------------------------------------------------------------- procedure New_Line_Of_Print is begin ... end New_Line_Of_Print; ... end Listing_Facilities; ------------------------------------------------------------------------- |
Restricting visibility of a library unit, by using with clauses on subunits
rather than on the entire parent unit, is useful in the same way. In the
example above, it is clear that the package Text_IO
is used only by the
Listing_Facilities
package of the compiler.
with
clause is to use it only with
subunits that really need it. Consider making those subunits separate
compilation units when the need for visibility to a library unit is restricted
to a subprogram or two.Language Ref Manual references: 8.1 Declarative Region, 8.2 Scope of Declarations, 8.3 Visibility, 10.1.1 Context Clauses - With Clauses, 10.2 Subunits of Compilation Units
------------------------------------------------------------------------- package Disk_Head_Scheduler is type Words is ... type Track_Number is ... procedure Transmit (Track : in Track_Number; Data : in Words); ... end Disk_Head_Scheduler; ------------------------------------------------------------------------- package body Disk_Head_Scheduler is ... task Control is entry Sign_In (Track : in Track_Number); ... end Control; ---------------------------------------------------------------------- task Track_Manager is entry Transfer(Track_Number) (Data : in Words); end Track_Manager; ---------------------------------------------------------------------- ... procedure Transmit (Track : in Track_Number; Data : in Words) is begin Control.Sign_In(Track); Track_Manager.Transfer(Track)(Data); end Transmit; ---------------------------------------------------------------------- ... end Disk_Head_Scheduler; ------------------------------------------------------------------------- |
Hiding a task specification in a package body and exporting (via subprograms) only required entries reduces the amount of extraneous information in the package specification. It allows your subprograms to enforce any order of entry calls necessary to the proper operation of the tasks. It also allows you to impose defensive task communication practices (see Guideline 6.2.2) and proper use of conditional and timed entry calls. Finally, it allows the grouping of entries into sets for export to different classes of users (e.g., producers versus consumers), or the concealment of entries that should not be made public at all (e.g., initialization, completion, signals). Where performance is an issue and there are no ordering rules to enforce, the entries can be renamed as subprograms to avoid the overhead of an extra procedure call.
An argument which can be viewed as an advantage or disadvantage is that hiding the task specification in a package body hides the fact of a tasking implementation from the user. If the application is such that a change to or from a tasking implementation, or a reorganization of services among tasks, need not concern users of the package then this is an advantage. However, if the package user must know about the tasking implementation to reason about global tasking behavior, then it is better not to hide the task completely. Either move it to the package specification or add comments stating that there is a tasking implementation, describing when a call may block, etc. Otherwise, it is the package implementor's responsibility to ensure that users of the package do not have to concern themselves with behaviors such as deadlock, starvation, and race conditions.
Finally, keep in mind that hiding tasks behind a procedural interface prevents the usage of conditional and timed entry calls and entry families, unless you add parameters and extra code to the procedures to make it possible for callers to direct the procedures to use these capabilities.
Language Ref Manual references: 7.2 Package Specifications and Declarations, 9.7.2 Conditional Entry Calls, 9.7.3 Timed Entry Calls, 9 Tasks