A.18.18 The Generic Package Containers.Indefinite_Holders
{
AI05-0069-1}
The language-defined generic package Containers.Indefinite_Holders provides
a private type Holder and a set of operations for that type. A holder
container holds a single element of an indefinite type.
{
AI05-0069-1}
A holder container allows the declaration of an object that can be used
like an uninitialized variable or component of an indefinite type.
{
AI05-0069-1}
A holder container may be
empty. An empty holder does not contain
an element.
Static Semantics
{
AI05-0069-1}
The generic library package Containers.Indefinite_Holders has the following
declaration:
{
AI05-0069-1}
{
AI05-0084-1}
{
AI12-0112-1}
generic
type Element_Type (<>)
is private;
with function "=" (Left, Right : Element_Type)
return Boolean
is <>;
package Ada.Containers.Indefinite_Holders
with Preelaborate, Remote_Types,
Nonblocking, Global => in out synchronized is
pragma Preelaborate(Indefinite_Holders);
pragma Remote_Types(Indefinite_Holders);
Discussion: {
AI12-0112-1}
For discussion on the reasons and meaning of the
specifications of the Global and Nonblocking aspects in this generic
package, see the notes on the equivalent operations in the specification
of the Containers.Vectors package (see A.18.2).
{
AI12-0112-1}
{
AI12-0399-1}
type Holder
is tagged private
with Stable_Properties => (Is_Empty,
Tampering_With_The_Element_Prohibited),
Default_Initial_Condition => Is_Empty (Holder),;
pragma Preelaborable_Initialization
(Holder);
Empty_Holder :
constant Holder;
{
AI12-0112-1}
function Equal_Element (Left, Right : Element_Type) return Boolean
renames "=";
function "=" (Left, Right : Holder) return Boolean;
{
AI12-0112-1}
function Tampering_With_The_Element_Prohibited
(Container : Holder) return Boolean
with Nonblocking, Global => null, Use_Formal => null;
{
AI12-0339-1}
function Empty return Holder
is (Empty_Holder)
with Post =>
not Tampering_With_The_Element_Prohibited (Empty'Result)
and then Is_Empty (Empty'Result);
{
AI12-0112-1}
function To_Holder (New_Item : Element_Type)
return Holder
with Post => not Is_Empty (To_Holder'Result);
{
AI12-0112-1}
function Is_Empty (Container : Holder)
return Boolean
with Global => null, Use_Formal => null;
{
AI12-0112-1}
procedure Clear (Container :
in out Holder)
with Pre => not Tampering_With_The_Element_Prohibited (Container)
or else raise Program_Error,
Post => Is_Empty (Container);
{
AI12-0112-1}
function Element (Container : Holder)
return Element_Type
with Pre => not Is_Empty (Container) or else raise Constraint_Error,
Global => null, Use_Formal => Element_Type;
{
AI12-0112-1}
procedure Replace_Element (Container :
in out Holder;
New_Item :
in Element_Type)
with Pre => not Tampering_With_The_Element_Prohibited (Container)
or else raise Program_Error,
Post => not Is_Empty (Container);
{
AI12-0112-1}
procedure Query_Element
(Container :
in Holder;
Process :
not null access procedure (Element :
in Element_Type))
with Pre => not Is_Empty (Container) or else raise Constraint_Error;
{
AI05-0069-1}
{
AI05-0248-1}
{
AI12-0112-1}
procedure Update_Element
(Container :
in out Holder;
Process :
not null access procedure (Element :
in out Element_Type))
with Pre => not Is_Empty (Container) or else raise Constraint_Error;
{
AI05-0212-1}
{
AI12-0112-1}
type Constant_Reference_Type
(Element :
not null access constant Element_Type)
is private
with Implicit_Dereference => Element
,
Nonblocking, Global => in out synchronized,
Default_Initial_Condition => (raise Program_Error);
{
AI05-0212-1}
{
AI12-0112-1}
type Reference_Type
(Element :
not null access Element_Type)
is private
with Implicit_Dereference => Element
,
Nonblocking, Global => in out synchronized,
Default_Initial_Condition => (raise Program_Error);
{
AI05-0212-1}
{
AI12-0112-1}
function Constant_Reference (Container :
aliased in Holder)
return Constant_Reference_Type
with Pre => not Is_Empty (Container)
or else raise Constraint_Error,
Post => Tampering_With_The_Element_Prohibited (Container),
Nonblocking, Global => null, Use_Formal => null;
{
AI05-0212-1}
{
AI12-0112-1}
function Reference (Container :
aliased in out Holder)
return Reference_Type
with Pre => not Is_Empty (Container)
or else raise Constraint_Error,
Post => Tampering_With_The_Element_Prohibited (Container),
Nonblocking, Global => null, Use_Formal => null;
{
AI05-0001-1}
{
AI12-0112-1}
procedure Assign (Target :
in out Holder; Source :
in Holder)
with Post => (Is_Empty (Source) = Is_Empty (Target));
{
AI05-0001-1}
{
AI12-0112-1}
function Copy (Source : Holder)
return Holder
with Post => (Is_Empty (Source) = Is_Empty (Copy'Result));
{
AI12-0112-1}
procedure Move (Target :
in out Holder; Source :
in out Holder)
with Pre => (not Tampering_With_The_Element_Prohibited (Target)
or else raise Program_Error) and then
(not Tampering_With_The_Element_Prohibited (Source)
or else raise Program_Error),
Post => (if not Target'Has_Same_Storage (Source) then
Is_Empty (Source) and then (not Is_Empty (Target)));
procedure Swap (Left, Right : in out Holder)
with Pre => (not Tampering_With_The_Element_Prohibited (Left)
or else raise Program_Error) and then
(not Tampering_With_The_Element_Prohibited (Right)
or else raise Program_Error),
Post => Is_Empty (Left) = Is_Empty (Right)'Old and then
Is_Empty (Right) = Is_Empty (Left)'Old;
private
... -- not specified by the language
end Ada.Containers.Indefinite_Holders;
{
AI05-0069-1}
The actual function for the generic formal function "=" on
Element_Type values is expected to define a reflexive and symmetric relationship
and return the same result value each time it is called with a particular
pair of values. If it behaves in some other manner, the function "="
on holder values returns an unspecified value. The exact arguments and
number of calls of this generic formal function by the function "="
on holder values are unspecified.
Ramification: If the actual function
for "=" is not symmetric and consistent, the result returned
by any of the functions defined to use "=" cannot be predicted.
The implementation is not required to protect against "=" raising
an exception, or returning random results, or any other "bad"
behavior. And it can call "=" in whatever manner makes sense.
But note that only the results of the function "=" is unspecified;
other subprograms are not allowed to break if "=" is bad.
{
AI05-0069-1}
The type Holder is used to represent holder containers. The type Holder
needs finalization
(see
7.6).
{
AI05-0069-1}
Empty_Holder represents an empty holder object. If an object of type
Holder is not otherwise initialized, it is initialized to the same value
as Empty_Holder.
{
AI05-0069-1}
{
AI05-0262-1}
{
AI12-0112-1}
[Some operations
of this generic package have access-to-subprogram
parameters. To ensure such operations are well-defined, they guard against
certain actions by the designated subprogram. In particular, some operations
check for “tampering with the element” of a container because
they depend
on the element of the container not being
replaced.]
When tampering with the element is prohibited
for a particular holder object H, Program_Error
is propagated by the finalization of H[, as well as by a call
that passes H to certain of the operations of this package, as
indicated by the precondition of such an operation].
Paragraphs
30 through 35 are removed as preconditions now describe these rules.
It clears the element contained
by H, that is, it calls the Clear procedure with H as a
parameter;
It replaces the element contained
by H, that is, it calls the Replace_Element procedure with H
as a parameter;
It calls the Move procedure
with H as a parameter;
It finalizes H.
Reason: Complete replacement of an element
can cause its memory to be deallocated while another operation is holding
onto a reference to it. That can't be allowed. However, a simple modification
of (part of) an element is not a problem, so Update_Element does not
cause a problem.
{
AI05-0265-1}
{
AI12-0110-1}
{
AI12-0112-1}
When tampering
with the element is prohibited for a particular holder object
H, Program_Error is propagated by a call of any language-defined
subprogram that is defined to tamper with the element of H, leaving
H unmodified. These checks are made before any other defined behavior of the body of
the language-defined subprogram.
function "=" (Left, Right : Holder) return Boolean;
{
AI05-0069-1}
If Left and Right denote the same holder object, then the function returns
True. Otherwise, it compares the element contained in Left to the element
contained in Right using the generic formal equality operator, returning
the result of that operation. Any exception raised during the evaluation
of element equality is propagated.
Implementation Note: This wording describes
the canonical semantics. However, the order and number of calls on the
formal equality function is unspecified, so an implementation
need not call the equality function if the correct answer can be determined
without doing so.
{
AI12-0112-1}
function Tampering_With_The_Element_Prohibited
(Container : Holder) return Boolean
with Nonblocking, Global => null, Use_Formal => null;
{
AI12-0112-1}
Returns True if tampering with the element is currently
prohibited for Container, and returns False otherwise.
{
AI12-0112-1}
function To_Holder (New_Item : Element_Type)
return Holder
with Post => not Is_Empty (To_Holder'Result);
{
AI05-0069-1}
{
AI12-0035-1}
Returns a nonempty holder containing an element initialized to New_Item.
To_Holder performs indefinite insertion (see A.18).
{
AI12-0112-1}
function Is_Empty (Container : Holder)
return Boolean
with Global => null, Use_Formal => null;
{
AI05-0069-1}
Returns True if Container is empty, and False if it contains an element.
{
AI12-0112-1}
procedure Clear (Container :
in out Holder)
with Pre => not Tampering_With_The_Element_Prohibited (Container)
or else raise Program_Error,
Post => Is_Empty (Container);
{
AI05-0069-1}
{
AI12-0112-1}
Removes the element from Container.
Container is
empty after a successful Clear operation.
{
AI12-0112-1}
function Element (Container : Holder)
return Element_Type
with Pre => not Is_Empty (Container) or else raise Constraint_Error,
Global => null, Use_Formal => Element_Type;
{
AI05-0069-1}
{
AI12-0112-1}
Returns If Container
is empty, Constraint_Error is propagated. Otherwise, returns the
element stored in Container.
{
AI12-0112-1}
procedure Replace_Element (Container :
in out Holder;
New_Item :
in Element_Type)
with Pre => not Tampering_With_The_Element_Prohibited (Container)
or else raise Program_Error,
Post => not Is_Empty (Container);
{
AI05-0069-1}
{
AI12-0035-1}
{
AI12-0112-1}
Replace_Element assigns the value New_Item into Container, replacing
any preexisting content of Container
; Replace_Element
performs indefinite insertion (see A.18).
Container is not empty after a successful call to Replace_Element.
{
AI12-0112-1}
procedure Query_Element
(Container :
in Holder;
Process :
not null access procedure (Element :
in Element_Type))
with Pre => not Is_Empty (Container) or else raise Constraint_Error,
Global => null, Use_Formal => null;
{
AI05-0069-1}
{
AI05-0262-1}
{
AI05-0265-1}
{
AI12-0112-1}
If Container is empty, Constraint_Error is propagated.
Otherwise, Query_Element calls Process.
all with the contained
element as the argument. Tampering with the element of Container is prohibited
during the execution of the call on Process.
all. Any exception
raised by Process.
all is propagated.
Implementation Note: {
AI05-0005-1}
The “tamper with the element” check is intended to prevent
the Element parameter of Process from being replaced or deleted outside
of Process. The check prevents data loss (if Element_Type is passed by
copy) or erroneous execution (if Element_Type is an unconstrained type).
{
AI05-0069-1}
{
AI05-0248-1}
{
AI12-0112-1}
procedure Update_Element
(Container :
in out Holder;
Process :
not null access procedure (Element :
in out Element_Type))
with Pre => not Is_Empty (Container) or else raise Constraint_Error;
{
AI05-0069-1}
{
AI05-0262-1}
{
AI05-0265-1}
{
AI12-0112-1}
If Container is empty, Constraint_Error is propagated.
Otherwise, Update_Element calls Process.
all with the contained
element as the argument. Tampering with the element of Container is prohibited
during the execution of the call on Process.
all. Any exception
raised by Process.
all is propagated.
Implementation Note: The Element parameter
of Process.all may be constrained even if Element_Type is unconstrained.
{
AI05-0212-1}
{
AI12-0112-1}
type Constant_Reference_Type
(Element :
not null access constant Element_Type)
is private
with Implicit_Dereference => Element
,
Nonblocking, Global => in out synchronized,
Default_Initial_Condition => (raise Program_Error);
{
AI05-0212-1}
{
AI12-0112-1}
type Reference_Type (Element :
not null access Element_Type)
is private
with Implicit_Dereference => Element
,
Nonblocking, Global => in out synchronized,
Default_Initial_Condition => (raise Program_Error);
{
AI05-0212-1}
The types Constant_Reference_Type and Reference_Type need finalization.
This paragraph
was deleted.{
AI05-0212-1}
{
AI12-0112-1}
The default initialization of an object of type
Constant_Reference_Type or Reference_Type propagates Program_Error.
Reason: It is expected that Reference_Type
(and Constant_Reference_Type) will be a controlled type, for which finalization
will have some action to terminate the tampering check for the associated
container. If the object is created by default, however, there is no
associated container. Since this is useless, and supporting this case
would take extra work, we define it to raise an exception.
{
AI05-0212-1}
{
AI12-0112-1}
function Constant_Reference (Container :
aliased in Holder)
return Constant_Reference_Type
with Pre => not Is_Empty (Container) or else raise Constraint_Error,
Post => Tampering_With_The_Element_Prohibited (Container),
Nonblocking, Global => null, Use_Formal => null;
{
AI05-0212-1}
This function (combined with the Implicit_Dereference aspect) provides
a convenient way to gain read access to the contained element of a holder
container.
{
AI05-0212-1}
{
AI05-0262-1}
{
AI05-0265-1}
{
AI12-0112-1}
If Container is empty, Constraint_Error is propagated.
Otherwise, Constant_Reference returns an object whose discriminant
is an access value that designates the contained element. Tampering with
the element of Container is prohibited while the object returned by Constant_Reference
exists and has not been finalized.
{
AI05-0212-1}
{
AI12-0112-1}
function Reference (Container :
aliased in out Holder)
return Reference_Type
with Pre => not Is_Empty (Container) or else raise Constraint_Error,
Post => Tampering_With_The_Element_Prohibited (Container),
Nonblocking, Global => null, Use_Formal => null;
{
AI05-0212-1}
This function (combined with the Implicit_Dereference aspects) provides
a convenient way to gain read and write access to the contained element
of a holder container.
{
AI05-0212-1}
{
AI05-0262-1}
{
AI05-0265-1}
{
AI12-0112-1}
If Container is empty, Constraint_Error is propagated.
Otherwise, Reference returns an object whose discriminant is an
access value that designates the contained element. Tampering with the
element of Container is prohibited while the object returned by Reference
exists and has not been finalized.
{
AI12-0112-1}
procedure Assign (Target :
in out Holder; Source :
in Holder)
with Post => (Is_Empty (Source) = Is_Empty (Target));
{
AI05-0001-1}
If Target denotes the same object as Source, the operation has no effect.
If Source is empty, Clear (Target) is called. Otherwise, Replace_Element
(Target, Element (Source)) is called.
Discussion: {
AI05-0005-1}
This routine exists for compatibility with the other containers. For
a holder,
Assign(A, B) and
A := B behave effectively
the same. (Assign Clears the Target, while := finalizes the Target, but
these should have similar effects.)
{
AI12-0112-1}
function Copy (Source : Holder)
return Holder
with Post => (Is_Empty (Source) = Is_Empty (Copy'Result));
{
AI05-0001-1}
If Source is empty, returns an empty holder container; otherwise, returns
To_Holder (Element (Source)).
{
AI12-0112-1}
procedure Move (Target :
in out Holder; Source :
in out Holder)
with Pre => (not Tampering_With_The_Element_Prohibited (Target)
or else raise Program_Error) and then
(not Tampering_With_The_Element_Prohibited (Source)
or else raise Program_Error),
Post => (if not Target'Has_Same_Storage (Source) then
Is_Empty (Source) and then (not Is_Empty (Target)));
{
AI05-0069-1}
{
AI05-0248-1}
{
AI12-0112-1}
If Target denotes the same object as Source, then the operation has no
effect. Otherwise, the element contained by Source (if any) is removed
from Source and inserted into Target, replacing any preexisting content.
Source is empty after a successful call to Move.
procedure Swap (Left, Right : in out Holder)
with Pre => (not Tampering_With_The_Element_Prohibited (Left)
or else raise Program_Error) and then
(not Tampering_With_The_Element_Prohibited (Right)
or else raise Program_Error),
Post => Is_Empty (Left) = Is_Empty (Right)'Old and then
Is_Empty (Right) = Is_Empty (Left)'Old;
{
AI12-0350-1}
If Left denotes the same object as Right, then
the operation has no effect. Otherwise, operation exchanges the elements
(if any) contained by Left and Right.
Bounded (Run-Time) Errors
{
AI05-0022-1}
{
AI05-0069-1}
{
AI05-0248-1}
{
AI05-0262-1}
It is a bounded error for the actual function associated
with a generic formal subprogram, when called as part of an operation
of this package, to tamper with the element of any Holder parameter of
the operation. Either Program_Error is raised, or the operation works
as defined on the value of the Holder either prior to, or subsequent
to, some or all of the modifications to the Holder.
{
AI05-0027-1}
{
AI05-0069-1}
It is a bounded error to call any subprogram declared
in the visible part of Containers.Indefinite_Holders when the associated
container has been finalized. If the operation takes Container as an
in out parameter, then it raises Constraint_Error or Program_Error.
Otherwise, the operation either proceeds as it would for an empty container,
or it raises Constraint_Error or
Program_Error.
Erroneous Execution
{
AI05-0212-1}
{
AI05-0269-1}
Execution is erroneous if the holder container associated with the result
of a call to Reference or Constant_Reference is finalized before the
result object returned by the call to Reference or Constant_Reference
is finalized.
Reason: {
AI05-0212-1}
Each object of Reference_Type and Constant_Reference_Type probably contains
some reference to the originating container. If that container is prematurely
finalized (which is only possible via Unchecked_Deallocation, as accessibility
checks prevent passing a container to Reference that will not live as
long as the result), the finalization of the object of Reference_Type
will try to access a nonexistent object. This is a normal case of a dangling
pointer created by Unchecked_Deallocation; we have to explicitly mention
it here as the pointer in question is not visible in the specification
of the type. (This is the same reason we have to say this for invalid
cursors.)
Implementation Requirements
{
AI05-0069-1}
No storage associated with a holder object shall be lost upon assignment
or scope exit.
Implementation Note: {
AI05-0298-1}
An assignment of a holder container is a “deep” copy; that
is the element is copied as well as any data structures. We say “effect
of” in order to allow the implementation to avoid copying the element
immediately if it wishes. For instance, an implementation that avoided
copying until one of the containers is modified would be allowed. (Note
that this implementation would require care, see
A.18.2
for more.)
Implementation Advice
Implementation Advice:
Move and Swap in Containers.Indefinite_Holders.Move
should not copy any elements the
element, and should minimize copying of internal data structures.
Implementation Note: {
AI12-0350-1}
Usually that can be accomplished simply by moving the pointer(s) to the
internal data structures
appropriately from the Source holder to the Target holder.
{
AI05-0069-1}
{
AI05-0269-1}
If an exception is propagated from a holder operation, no storage should
be lost, nor should the element be removed from a holder container unless
specified by the operation.
Implementation Advice: If an exception
is propagated from a holder operation, no storage should be lost, nor
should the element be removed from a holder container unless specified
by the operation.
Reason: This is important so that programs
can recover from errors. But we don't want to require heroic efforts,
so we just require documentation of cases where this can't be accomplished.
Extensions to Ada 2005
Inconsistencies With Ada 2012
{
AI12-0035-1}
Corrigendum: Defined
some routines to “perform indefinite insertion”. This could
mean that some calls to those routines would now raise Program_Error
where they previously worked. However, this is extremely unlikely; see
Inconsistencies With Ada 2012 in A.18.11
for details.
Incompatibilities With Ada 2012
{
AI12-0111-1}
{
AI12-0112-1}
{
AI12-0350-1}
A number of new subprograms
and types were added to Containers.Indefinite_Holders to better support
contracts and provide additional functionality. Therefore, a use clause
conflict is possible; see the introduction of Annex
A for more on this topic.
Wording Changes from Ada 2012
{
AI12-0110-1}
Corrigendum: Clarified that tampering checks
precede all other checks made by a subprogram (but come after those associated
with the call).
{
AI12-0112-1}
Added contracts to this container. This includes
describing some of the semantics with pre- and postconditions, rather
than English text. Note that the preconditions can be Suppressed (see
11.5).
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe