2 Synthesis‎ > ‎Computing‎ > ‎Programming‎ > ‎Ada‎ > ‎Ada Language‎ > ‎

Packages

UNFINISHED CONTENT

Packages are a kind of program unit (a part of a program consisting of one or more files).

Package Specification Syntax

package_specification::=
          package defining_program_unit_name is
          -- declarations of items visible outside the package
            {basic_declarative_item}
         [private -- declarations of items invisible outside the package
            {basic_declarative_item}]
          end [[parent_unit_name.]identifier]
 

Package Specification Example

 
         package Rational_Numbers is
 
         type Rational is
            record
               Numerator   : Integer;
               Denominator : Positive;
            end record;
 
         function "="(X,Y : Rational) return Boolean;
 
         function "/"  (X,Y : Integer)  return Rational; 
            --  to construct a rational number

 
         function "+"  (X,Y : Rational) return Rational;
         function "-"  (X,Y : Rational) return Rational;
         function "*"  (X,Y : Rational) return Rational;
         function "/"  (X,Y : Rational) return Rational;
      end Rational_Numbers;
 

Package Body Syntax

package_body::=
          package body defining_program_unit_name is
             declarative_part
         [begin
              handled_sequence_of_statements]
          end [[parent_unit_name.]identifier];

Package Body Example

where there is ... it means part of the program has been omitted.

      package body Rational_Numbers is
 
         procedure Same_Denominator (X,Y : in out Rational) is
         begin
            --  reduces X and Y to the same denominator:
            ...
         end Same_Denominator;
 
         function "="(X,Y : Rational) return Boolean is
            U : Rational := X;
            V : Rational := Y;
         begin
            Same_Denominator (U,V);
            return U.Numerator = V.Numerator;
         end "=";
 
         function "/" (X,Y : Integer) return Rational is
         begin
            if Y > 0 then
               return (Numerator => X,  Denominator => Y);
            else
               return (Numerator => -X, Denominator => -Y);
            end if;
         end "/";
 
         function "+" (X,Y : Rational) return Rational is ... end "+";
         function "-" (X,Y : Rational) return Rational is ... end "-";
         function "*" (X,Y : Rational) return Rational is ... end "*";
         function "/" (X,Y : Rational) return Rational is ... end "/";
 
         end Rational_Numbers;
 
 

Private Types and Private Extensions

The declaration (in the visible part of a package) of a type as a
private type or private extension serves to separate the characteristics that
can be used directly by outside program units (that is, the logical
properties) from other characteristics whose direct use is confined to the
package (the details of the definition of the type itself). See 3.9.1 for an
overview of type extensions.
 
 
                                   Syntax
 
      private_type_declaration ::= 
         type defining_identifier [discriminant_part]
            is [[abstract] tagged] [limited] private;

 
      private_extension_declaration ::= 
         type defining_identifier [discriminant_part] is
           [abstract] [limited | synchronized] new ancestor_subtype_indication
           [and interface_list] with private;
 
 
 

Private Operations

 
 
For a type declared in the visible part of a package or generic package,
certain operations on the type do not become visible until later in the
package - either in the private part or the body. Such private operations are
available only inside the declarative region of the package or generic
package.
 
 
Example of a type with private operations:
 
15    package Key_Manager is
         type Key is private;
         Null_Key : constant Key; -- a deferred constant declaration (see 7.4)
         procedure Get_Key(K : out Key);
         function "<" (X, Y : Key) return Boolean;
      private
         type Key is new Natural;
         Null_Key : constant Key := Key'First;
      end Key_Manager;
 
16    package body Key_Manager is
         Last_Key : Key := Null_Key;
         procedure Get_Key(K : out Key) is
         begin
            Last_Key := Last_Key + 1;
            K := Last_Key;
         end Get_Key;
 
17       function "<" (X, Y : Key) return Boolean is
         begin
            return Natural(X) < Natural(Y);
         end "<";
      end Key_Manager;
 
      NOTES
 
18    12  Notes on the example: Outside of the package Key_Manager, the
      operations available for objects of type Key include assignment, the
      comparison for equality or inequality, the procedure Get_Key and the
      operator "<"; they do not include other relational operators such as
      ">=", or arithmetic operators.
 
19    The explicitly declared operator "<" hides the predefined operator "<"
      implicitly declared by the full_type_declaration. Within the body of the
      function, an explicit conversion of X and Y to the subtype Natural is
      necessary to invoke the "<" operator of the parent type. Alternatively,
      the result of the function could be written as not (X >= Y), since the
      operator ">=" is not redefined.
 
20    The value of the variable Last_Key, declared in the package body,
      remains unchanged between calls of the procedure Get_Key. (See also the
      NOTES of 7.2.)
 
 
7.4 Deferred Constants
 
 
1     Deferred constant declarations may be used to declare constants in the
visible part of a package, but with the value of the constant given in the
private part. They may also be used to declare constants imported from other
languages (see Annex B).
 
 
                               Legality Rules
 
2     A deferred constant declaration is an object_declaration with the
reserved word constant but no initialization expression. The constant declared
by a deferred constant declaration is called a deferred constant. A deferred
constant declaration requires a completion, which shall be a full constant
declaration (called the full declaration of the deferred constant), or a
pragma Import (see Annex B).
 
3     A deferred constant declaration that is completed by a full constant
declaration shall occur immediately within the visible part of a
package_specification. For this case, the following additional rules apply to
the corresponding full declaration:
 
4     The full declaration shall occur immediately within the private part of
      the same package;
 
5/2   The deferred and full constants shall have the same type, or shall have
      statically matching anonymous access subtypes;
 
6/2   If the deferred constant declaration includes a subtype_indication that
      defines a constrained subtype, then the subtype defined by the
      subtype_indication in the full declaration shall match it statically. On
      the other hand, if the subtype of the deferred constant is
      unconstrained, then the full declaration is still allowed to impose a
      constraint. The constant itself will be constrained, like all constants;
 
7/2   If the deferred constant declaration includes the reserved word aliased,
      then the full declaration shall also;
 
7.1/2 If the subtype of the deferred constant declaration excludes null, the
      subtype of the full declaration shall also exclude null.
 
8     A deferred constant declaration that is completed by a pragma Import
need not appear in the visible part of a package_specification, and has no
full constant declaration.
 
9/2   The completion of a deferred constant declaration shall occur before the
constant is frozen (see 13.14).
 
 
                              Dynamic Semantics
 
10    The elaboration of a deferred constant declaration elaborates the
subtype_indication or (only allowed in the case of an imported constant) the
array_type_definition.
 
      NOTES
 
11    13  The full constant declaration for a deferred constant that is of a
      given private type or private extension is not allowed before the
      corresponding full_type_declaration. This is a consequence of the
      freezing rules for types (see 13.14).
 
 
                                  Examples
 
12    Examples of deferred constant declarations:
 
13    Null_Key : constant Key;      -- see 7.3.1
 
14    CPU_Identifier : constant String(1..8);
      pragma Import(Assembler, CPU_Identifier, Link_Name => "CPU_ID");
                                    -- see B.1
 
 
7.5 Limited Types
 
 
1/2   A limited type is (a view of) a type for which copying (such as for an
assignment_statement) is not allowed. A nonlimited type is a (view of a) type
for which copying is allowed.

 
Example of a package with a limited type:
 
      package IO_Package is
         type File_Name is limited private;
 
         procedure Open (F : in out File_Name);
         procedure Close(F : in out File_Name);
         procedure Read (F : in File_Name; Item : out Integer);
         procedure Write(F : in File_Name; Item : in  Integer);
      private
         type File_Name is
            limited record
               Internal_Name : Integer := 0;
            end record;
      end IO_Package;
 
      package body IO_Package is
         Limit : constant := 200;
         type File_Descriptor is record  ...  end record;
         Directory : array (1 .. Limit) of File_Descriptor;
         ...
         procedure Open (F : in out File_Name) is  ...  end;
         procedure Close(F : in out File_Name) is  ...  end;
         procedure Read (F : in File_Name; Item : out Integer) is ... end;
         procedure Write(F : in File_Name; Item : in  Integer) is ... end;
      begin
         ...
      end IO_Package;
 
      NOTES
 
Notes on the example: In the example above, an outside subprogram
      making use of IO_Package may obtain a file name by calling Open and
      later use it in calls to Read and Write. Thus, outside the package, a
      file name obtained from Open acts as a kind of password; its internal
      properties (such as containing a numeric value) are not known and no
      other operations (such as addition or comparison of internal names) can
      be performed on a file name. Most importantly, clients of the package
      cannot make copies of objects of type File_Name.
 
This example is characteristic of any case where complete control over
      the operations of a type is desired. Such packages serve a dual purpose.
      They prevent a user from making use of the internal structure of the
      type. They also implement the notion of an encapsulated data type where
      the only operations on the type are those given in the package
      specification.
 
The fact that the full view of File_Name is explicitly declared limited
      means that parameter passing will always be by reference and function
      results will always be built directly in the result object (see 6.2 and
      6.5).
 
 
7.6 User-Defined Assignment and Finalization
 
 
1     Three kinds of actions are fundamental to the manipulation of objects:
initialization, finalization, and assignment. Every object is initialized,
either explicitly or by default, after being created (for example, by an
object_declaration or allocator). Every object is finalized before being
destroyed (for example, by leaving a subprogram_body containing an
object_declaration, or by a call to an instance of Unchecked_Deallocation). An
assignment operation is used as part of assignment_statements, explicit
initialization, parameter passing, and other operations.
 
2     Default definitions for these three fundamental operations are provided
by the language, but a controlled type gives the user additional control over
parts of these operations. In particular, the user can define, for a
controlled type, an Initialize procedure which is invoked immediately after
the normal default initialization of a controlled object, a Finalize procedure
which is invoked immediately before finalization of any of the components of a
controlled object, and an Adjust procedure which is invoked as the last step
of an assignment to a (nonlimited) controlled object.
 
 
                              Static Semantics
 
3     The following language-defined library package exists:
 
4/1   package Ada.Finalization is
          pragma Preelaborate(Finalization);
          pragma Remote_Types(Finalization);
 
5/2       type Controlled is abstract tagged private;
          pragma Preelaborable_Initialization(Controlled);
 
6/2       procedure Initialize (Object : in out Controlled) is null;
          procedure Adjust     (Object : in out Controlled) is null;
          procedure Finalize   (Object : in out Controlled) is null;
 
7/2       type Limited_Controlled is abstract tagged limited private;
          pragma Preelaborable_Initialization(Limited_Controlled);
 
8/2       procedure Initialize (Object : in out Limited_Controlled) is null;
          procedure Finalize   (Object : in out Limited_Controlled) is null;
      private
          ... -- not specified by the language
      end Ada.Finalization;
 
9/2   A controlled type is a descendant of Controlled or Limited_Controlled.
The predefined "=" operator of type Controlled always returns True, since this
operator is incorporated into the implementation of the predefined equality
operator of types derived from Controlled, as explained in 4.5.2. The type
Limited_Controlled is like Controlled, except that it is limited and it lacks
the primitive subprogram Adjust.
 
9.1/2 A type is said to need finalization if:
 
9.2/2 it is a controlled type, a task type or a protected type; or
 
9.3/2 it has a component that needs finalization; or
 
9.4/2 it is a limited type that has an access discriminant whose designated
      type needs finalization; or
 
9.5/2 it is one of a number of language-defined types that are explicitly
      defined to need finalization.
 
 
                              Dynamic Semantics
 
10/2  During the elaboration or evaluation of a construct that causes an
object to be initialized by default, for every controlled subcomponent of the
object that is not assigned an initial value (as defined in 3.3.1), Initialize
is called on that subcomponent. Similarly, if the object that is initialized
by default as a whole is controlled, Initialize is called on the object.
 
11/2  For an extension_aggregate whose ancestor_part is a subtype_mark
denoting a controlled subtype, the Initialize procedure of the ancestor type
is called, unless that Initialize procedure is abstract.
 
12    Initialize and other initialization operations are done in an arbitrary
order, except as follows. Initialize is applied to an object after
initialization of its subcomponents, if any (including both implicit
initialization and Initialize calls). If an object has a component with an
access discriminant constrained by a per-object expression, Initialize is
applied to this component after any components that do not have such
discriminants. For an object with several components with such a discriminant,
Initialize is applied to them in order of their component_declarations. For an
allocator, any task activations follow all calls on Initialize.
 
13    When a target object with any controlled parts is assigned a value,
either when created or in a subsequent assignment_statement, the assignment
operation proceeds as follows:
 
14    The value of the target becomes the assigned value.
 
15    The value of the target is adjusted.
 
16    To adjust the value of a (nonlimited) composite object, the values of
the components of the object are first adjusted in an arbitrary order, and
then, if the object is controlled, Adjust is called. Adjusting the value of an
elementary object has no effect, nor does adjusting the value of a composite
object with no controlled parts.
 
17    For an assignment_statement, after the name and expression have been
evaluated, and any conversion (including constraint checking) has been done,
an anonymous object is created, and the value is assigned into it; that is,
the assignment operation is applied. (Assignment includes value adjustment.)
The target of the assignment_statement is then finalized. The value of the
anonymous object is then assigned into the target of the
assignment_statement. Finally, the anonymous object is finalized. As explained
below, the implementation may eliminate the intermediate anonymous object, so
this description subsumes the one given in 5.2, "Assignment Statements".
 
 
                         Implementation Requirements
 
17.1/2 For an aggregate of a controlled type whose value is assigned, other
than by an assignment_statement, the implementation shall not create a
separate anonymous object for the aggregate. The aggregate value shall be
constructed directly in the target of the assignment operation and Adjust is
not called on the target object.
 
 
                         Implementation Permissions
 
18    An implementation is allowed to relax the above rules (for nonlimited
controlled types) in the following ways:
 
19    For an assignment_statement that assigns to an object the value of that
      same object, the implementation need not do anything.
 
20    For an assignment_statement for a noncontrolled type, the implementation
      may finalize and assign each component of the variable separately
      (rather than finalizing the entire variable and assigning the entire new
      value) unless a discriminant of the variable is changed by the
      assignment.
 
21/2  For an aggregate or function call whose value is assigned into a target
      object, the implementation need not create a separate anonymous object
      if it can safely create the value of the aggregate or function call
      directly in the target object. Similarly, for an assignment_statement,
      the implementation need not create an anonymous object if the value
      being assigned is the result of evaluating a name denoting an object
      (the source object) whose storage cannot overlap with the target. If the
      source object might overlap with the target object, then the
      implementation can avoid the need for an intermediary anonymous object
      by exercising one of the above permissions and perform the assignment
      one component at a time (for an overlapping array assignment), or not at
      all (for an assignment where the target and the source of the assignment
      are the same object).
 
22/2  Furthermore, an implementation is permitted to omit implicit Initialize,
Adjust, and Finalize calls and associated assignment operations on an object
of a nonlimited controlled type provided that:
 
23/2  any omitted Initialize call is not a call on a user-defined Initialize
      procedure, and
 
24/2  any usage of the value of the object after the implicit Initialize or
      Adjust call and before any subsequent Finalize call on the object does
      not change the external effect of the program, and
 
25/2  after the omission of such calls and operations, any execution of the
      program that executes an Initialize or Adjust call on an object or
      initializes an object by an aggregate will also later execute a Finalize
      call on the object and will always do so prior to assigning a new value
      to the object, and
 
26/2  the assignment operations associated with omitted Adjust calls are also
      omitted.
 
27/2  This permission applies to Adjust and Finalize calls even if the
implicit calls have additional external effects.
 
 
7.6.1 Completion and Finalization
 
 
1     This subclause defines completion and leaving of the execution of
constructs and entities. A master is the execution of a construct that
includes finalization of local objects after it is complete (and after waiting
for any local tasks - see 9.3), but before leaving. Other constructs and
entities are left immediately upon completion.
 
 
                              Dynamic Semantics
 
2/2   The execution of a construct or entity is complete when the end of that
execution has been reached, or when a transfer of control (see 5.1) causes it
to be abandoned. Completion due to reaching the end of execution, or due to
the transfer of control of an exit_statement, return statement,
goto_statement, or requeue_statement or of the selection of a
terminate_alternative is normal completion. Completion is abnormal otherwise -
when control is transferred out of a construct due to abort or the raising of
an exception.
 
3/2   After execution of a construct or entity is complete, it is left,
meaning that execution continues with the next action, as defined for the
execution that is taking place. Leaving an execution happens immediately after
its completion, except in the case of a master: the execution of a body other
than a package_body; the execution of a statement; or the evaluation of an
expression, function_call, or range that is not part of an enclosing
expression, function_call, range, or simple_statement other than a simple_-
return_statement. A master is finalized after it is complete, and before it is
left.
 
4     For the finalization of a master, dependent tasks are first awaited, as
explained in 9.3. Then each object whose accessibility level is the same as
that of the master is finalized if the object was successfully initialized and
still exists. These actions are performed whether the master is left by
reaching the last statement or via a transfer of control. When a transfer of
control causes completion of an execution, each included master is finalized
in order, from innermost outward.
 
5     For the finalization of an object:
 
6     If the object is of an elementary type, finalization has no effect;
 
7     If the object is of a controlled type, the Finalize procedure is called;
 
8     If the object is of a protected type, the actions defined in 9.4 are
      performed;
 
9/2   If the object is of a composite type, then after performing the above
      actions, if any, every component of the object is finalized in an
      arbitrary order, except as follows: if the object has a component with
      an access discriminant constrained by a per-object expression, this
      component is finalized before any components that do not have such
      discriminants; for an object with several components with such a
      discriminant, they are finalized in the reverse of the order of their
      component_declarations;
 
9.1/2 If the object has coextensions (see 3.10.2), each coextension is
      finalized after the object whose access discriminant designates it.
 
10    Immediately before an instance of Unchecked_Deallocation reclaims the
storage of an object, the object is finalized. If an instance of
Unchecked_Deallocation is never applied to an object created by an allocator,
the object will still exist when the corresponding master completes, and it
will be finalized then.
 
11/2  The order in which the finalization of a master performs finalization of
objects is as follows: Objects created by declarations in the master are
finalized in the reverse order of their creation. For objects that were
created by allocators for an access type whose ultimate ancestor is declared
in the master, this rule is applied as though each such object that still
exists had been created in an arbitrary order at the first freezing point (see
13.14) of the ultimate ancestor type; the finalization of these objects is
called the finalization of the collection. After the finalization of a master
is complete, the objects finalized as part of its finalization cease to exist,
as do any types and subtypes defined and created within the master.
 
12/2  The target of an assignment_statement is finalized before copying in the
new value, as explained in 7.6.
 
13/2  The master of an object is the master enclosing its creation whose
accessibility level (see 3.10.2) is equal to that of the object.
 
13.1/2 In the case of an expression that is a master, finalization of any
(anonymous) objects occurs as the final part of evaluation of the expression.
 
 
                          Bounded (Run-Time) Errors
 
14/1  It is a bounded error for a call on Finalize or Adjust that occurs as
part of object finalization or assignment to propagate an exception. The
possible consequences depend on what action invoked the Finalize or Adjust
operation:
 
15    For a Finalize invoked as part of an assignment_statement, Program_Error
      is raised at that point.
 
16/2  For an Adjust invoked as part of assignment operations other than those
      invoked as part of an assignment_statement, other adjustments due to be
      performed might or might not be performed, and then Program_Error is
      raised. During its propagation, finalization might or might not be
      applied to objects whose Adjust failed. For an Adjust invoked as part of
      an assignment_statement, any other adjustments due to be performed are
      performed, and then Program_Error is raised.
 
17    For a Finalize invoked as part of a call on an instance of
      Unchecked_Deallocation, any other finalizations due to be performed are
      performed, and then Program_Error is raised.
 
17.1/1 For a Finalize invoked as part of the finalization of the anonymous
      object created by a function call or aggregate, any other finalizations
      due to be performed are performed, and then Program_Error is raised.
 
17.2/1 For a Finalize invoked due to reaching the end of the execution of a
      master, any other finalizations associated with the master are
      performed, and Program_Error is raised immediately after leaving the
      master.
 
18/2  For a Finalize invoked by the transfer of control of an exit_statement,
      return statement, goto_statement, or requeue_statement, Program_Error is
      raised no earlier than after the finalization of the master being
      finalized when the exception occurred, and no later than the point where
      normal execution would have continued. Any other finalizations due to be
      performed up to that point are performed before raising Program_Error.
 
19    For a Finalize invoked by a transfer of control that is due to raising
      an exception, any other finalizations due to be performed for the same
      master are performed; Program_Error is raised immediately after leaving
      the master.
 
20    For a Finalize invoked by a transfer of control due to an abort or
      selection of a terminate alternative, the exception is ignored; any
      other finalizations due to be performed are performed.
 
      NOTES
 
21    17  The rules of Section 10 imply that immediately prior to partition
      termination, Finalize operations are applied to library-level controlled
      objects (including those created by allocators of library-level access
      types, except those already finalized). This occurs after waiting for
      library-level tasks to terminate.
 
22    18  A constant is only constant between its initialization and
      finalization. Both initialization and finalization are allowed to change
      the value of a constant.
 
23    19  Abort is deferred during certain operations related to controlled
      types, as explained in 9.8. Those rules prevent an abort from causing a
      controlled object to be left in an ill-defined state.
 
24    20  The Finalize procedure is called upon finalization of a controlled
      object, even if Finalize was called earlier, either explicitly or as
      part of an assignment; hence, if a controlled type is visibly controlled
      (implying that its Finalize primitive is directly callable), or is
      nonlimited (implying that assignment is allowed), its Finalize procedure
      should be designed to have no ill effect if it is applied a second time
      to the same object.






Comments