!standard 7.6(4/1) 11-10-12 AI05-0212-1/14 !standard A.18.2(6/2) !standard A.18.2(8/2) !standard A.18.2(11/2) !standard A.18.2(34/2) !standard A.18.2(72/2) !standard A.18.2(74/2) !standard A.18.2(97/2) !standard A.18.2(147/2) !standard A.18.2(225/2) !standard A.18.2(226/2) !standard A.18.2(230/2) !standard A.18.2(240/2) !standard A.18.2(252/2) !standard A.18.3(5/2) !standard A.18.3(6/2) !standard A.18.3(9/2) !standard A.18.3(17/2) !standard A.18.3(44/2) !standard A.18.3(46/2) !standard A.18.3(69/2) !standard A.18.3(86/2) !standard A.18.3(139/2) !standard A.18.3(140/2) !standard A.18.3(144/2) !standard A.18.3(157/2) !standard A.18.4(18/2) !standard A.18.4(41/2) !standard A.18.4(72/2) !standard A.18.4(73/2) !standard A.18.4(80/2) !standard A.18.5(2/2) !standard A.18.5(3/2) !standard A.18.5(6/2) !standard A.18.5(17/2) !standard A.18.5(33/2) !standard A.18.5(37/2) !standard A.18.5(61/2) !standard A.18.6(2/2) !standard A.18.6(4/2) !standard A.18.6(7/2) !standard A.18.6(16/2) !standard A.18.6(43/2) !standard A.18.6(51/2) !standard A.18.6(94/2) !standard A.18.7(19/2) !standard A.18.7(36/2) !standard A.18.7(83/2) !standard A.18.7(84/2) !standard A.18.7(96/2) !standard A.18.7(101/2) !standard A.18.8(2/2) !standard A.18.8(3/2) !standard A.18.8(6/2) !standard A.18.8(17/2) !standard A.18.8(45/2) !standard A.18.8(49/2) !standard A.18.8(58/2) !standard A.18.8(85/2) !standard A.18.9(2/2) !standard A.18.9(4/2) !standard A.18.9(7/2) !standard A.18.9(16/2) !standard A.18.9(53/2) !standard A.18.9(61/2) !standard A.18.9(73/2) !standard A.18.9(113/2) !standard A.18.10(0) !standard A.18.18(0) !standard A.18.32(0) !class Amendment 10-04-26 !status Amendment 2012 11-06-18 !status ARG Approved (by Letter Ballot) 9-0-2 11-05-16 !status work item 11-04-26 !status ARG Approved 8-0-0 11-03-17 !status work item 10-04-26 !status received 10-02-27 !priority Medium !difficulty Medium !subject Accessors and Iterators for Ada.Containers !summary (See proposal.) !problem We've defined a number of new features that potentially can improve the use of the containers libraries in AI05-0139-2, AI05-0142-4, AI05-0143-1, and AI05-0162-1. The containers ought to be updated to use these new features. !proposal Add accessors and iterators to the containers packages. !wording [Editor's note: The following is based on AI05-0139-2/07.] Replace 7.6(4/1) with: package Finalization is pragma Pure(Finalization); [This changes this to a Pure package, and drops "Remote_Types" (which is implicit in Pure).] Add the following with to each container package that has iterators: with Ada.Iterator_Interfaces; Modify the definition of the container types for each of the containers that has a cursor (with suitable substitutions for the names of types) as follows [note: Set does not have Variable_Indexing as it does not allow in-place modification of elements]: type Vector is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type; pragma Preelaborable_Initialization(Vector); Move after constant No_Element: function Has_Element (Position : Cursor) return Boolean; Add following that: package Vector_Iterator_Interfaces is new Ada.Iterator_Interfaces (Cursor, Has_Element); Add the following to each Ada.Containers package (other than the Queues packages, but including Indefinite_Holders) immediately after Update_Element (with suitable substitutions for the names of types): type Constant_Reference_Type (Element : not null access constant Element_Type) is private with Implicit_Dereference => Element; type Reference_Type (Element : not null access Element_Type) is private with Implicit_Dereference => Element; The types Constant_Reference_Type and Reference_Type need finalization. The default initialization of an object of type Constant_Reference_Type or Reference_Type propagates Program_Error. AARM 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. Add the following to each Ada.Containers package that has a Cursor type immediately after the above (with suitable substitutions for the names of types) - exception: there is no Reference in package Sets [meta-rule: There is a Constant_Reference for each Query_Element; a Reference for each Update_Element]: function Constant_Reference (Container : aliased in Vector; Position : in Cursor) return Constant_Reference_Type; This function (combined with the Constant_Indexing and Implicit_Dereference aspects) provides a convenient way to gain read access to the individual elements of a container starting with a cursor. If Position equals No_Element, then Constraint_Error is propagated; if Position does not designate an element in Container, then Program_Error is propagated. Otherwise, Constant_Reference returns an object whose discriminant is an access value that designates the element designated by Position. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Constant_Reference exists and has not been finalized. function Reference (Container : aliased in out Vector; Position : in Cursor) return Reference_Type; This function (combined with the Variable_Indexing and Implicit_Dereference aspects) provides a convenient way to gain read and write access to the individual elements of a container starting with a cursor. If Position equals No_Element, then Constraint_Error is propagated; if Position does not designate an element in Container, then Program_Error is propagated. Otherwise, Reference returns an object whose discriminant is an access value that designates the element designated by Position. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Reference exists and has not been finalized. The element designated by Position is not an empty element after successful completion of this operation. [This is only needed for the Vectors case. - ED] Add the following to Ada.Containers.Vectors and its relatives: function Constant_Reference (Container : aliased in Vector; Index : in Index_Type) return Constant_Reference_Type; This function (combined with the Constant_Indexing and Implicit_Dereference aspects) provides a convenient way to gain read access to the individual elements of a container starting with an index value. If Index is not in the range First_Index (Container) .. Last_Index (Container), then Constraint_Error is propagated. Otherwise, Constant_Reference returns an object whose discriminant is an access value that designates the element at position Index. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Constant_Reference exists and has not been finalized. function Reference (Container : aliased in out Vector; Index : in Index_Type) return Reference_Type; This function (combined with the Variable_Indexing and Implicit_Dereference aspects) provides a convenient way to gain read and write access to the individual elements of a container starting with an index value. If Index is not in the range First_Index (Container) .. Last_Index (Container), then Constraint_Error is propagated. Otherwise, Reference returns an object whose discriminant is an access value that designates the element at position Index. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Reference exists and has not been finalized. The element at position Index is not an empty element after successful completion of this operation. Modify A.18.2(240/2): Reading the value of an empty element by calling Element, Query_Element, Update_Element, {Constant_Reference, Reference,} Swap, Is_Sorted, Sort, Merge, "=", Find, or Reverse_Find is a bounded error. The implementation may treat the element as having any normal value (see 13.9.1) of the element type, or raise Constraint_Error or Program_Error before modifying the vector. Add the following to Ada.Containers.Hashed_Maps and the various other Map containers: function Constant_Reference (Container : aliased in Map; Key : in Key_Type) return Constant_Reference_Type; This routine (combined with the Constant_Indexing and Implicit_Dereference aspects) provides a convenient way to gain read access to the individual elements of a container starting with a key value. Equivalent to Constant_Reference (Container, Find (Container, Key)). function Reference (Container : aliased in out Map; Key : in Key_Type) return Reference_Type; This function (combined with the Variable_Indexing and Implicit_Dereference aspects) provides a convenient way to gain read and write access to the individual elements of a container starting with an key value. Equivalent to Reference (Container, Find (Container, Key)). Add the following as inside of the Generic_Keys package for the various Set containers: [Note: Reference_Type is also declared inside of package Generic_Keys.] function Reference_Preserving_Key (Container : aliased in out Set; Position : in Cursor) return Reference_Type; This function (combined with the Implicit_Dereference aspect) provides a convenient way to gain read and write access to the individual elements of a container starting with a cursor. If Position equals No_Element, then Constraint_Error is propagated; if Position does not designate an element in Container, then Program_Error is propagated. Otherwise, Reference_Preserving_Key uses Key to save the key value *K*; then returns an object whose discriminant is an access value that designates the element designated by Position. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Reference_Preserving_Key exists and has not been finalized. When the object returned by Reference_PReserving_Key is finalized, a check is made if *K* determines the same equivalence class as that for the new element; if not, the element is removed from the set and Program_Error is propagated. function Constant_Reference (Container : aliased in Set; Key : in Key_Type) return Constant_Reference_Type; This routine (combined with the Implicit_Dereference aspect) provides a convenient way to gain read access to the individual elements of a container starting with a key value. Equivalent to Constant_Reference (Container, Find (Container, Key)). function Reference_Preserving_Key (Container : aliased in out Set; Key : in Key_Type) return Reference_Type; This function (combined with the Implicit_Dereference aspect) provides a convenient way to gain read and write access to the individual elements of a container starting with an key value. Equivalent to Reference_Preserving_Key (Container, Find (Container, Key)). [Note: The versions for the set containers are unfortunately not primitive operations; therefore they cannot be used via the _Indexing magic nor can they be called with prefix notation. However, the reference semantics is operational, so the discriminant and .all can be omitted; and they're probably still easier to use than Update_Element. That seems like enough to justify defining them.] Add the following to Ada.Containers.Indefinite_Holders and its relatives: function Constant_Reference (Container : aliased in Holder) return Constant_Reference_Type; This routine (combined with the Implicit_Dereference aspect) provides a convenient way to gain read access to the contained element of a holder container. 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. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Constant_Reference exists and has not been finalized. function Reference (Container : aliased in out Holder) return Reference_Type; 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. If Container is empty, Constraint_Error is propagated. Otherwise, Reference returns an object whose discriminant is an access value that designates the contained element. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Reference exists and has not been finalized. After the existing procedure Reverse_Iterate of each container that has Reverse_Iterate, add a new function: function Iterate (Container : in Vector) return Vector_Iterator_Interfaces.Reversible_Iterator'Class; Iterate returns a reversible iterator object that will generate a value for the loop parameter designating each node in Container, starting with the first node and moving the cursor as per the Next function when used as a forward iterator, and starting with the last node and moving the cursor as per the Previous function when used as a reverse iterator. Program_Error is propagated if any operation (in particular, the sequence_of_statements of the loop_statement whose iterator_specification denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization. [Note: This wording will need to be adjusted for containers that don't have Next, like Maps. The wording will be parallel to that of the existing Iterate procedure.] After the above routine for various Vector and List containers, add a new function: function Iterate (Container : in Vector; Start : in Cursor) return Vector_Iterator_Interfaces.Reversible_Iterator'Class; If Start is not No_Element and does not designate an item in Container, then Program_Error is propagated. If Start is No_Element, the call is equivalent to Iterate (Container). Otherwise, Iterate returns a reversible iterator object that will generate a value for the loop parameter designating each node in Container, starting with the node designated by Start and moving the cursor as per the Next function when used as a forward iterator, or moving the cursor as per the Previous function when used as a reverse iterator. Program_Error is propagated if any operation (in particular, the sequence_of_statements of the loop_statement whose iterator_specification denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization. AARM Note: Exits are allowed from the loops created using the iterator objects. In particular, to stop the iteration at a particular cursor, just add exit when Cur = Stop; in the body of the loop (assuming the Cur is the loop parameter and Stop is the cursor that you want to stop at). After the existing procedure Iterate of each container that does not have a procedure Reverse_Iterate (for example, Hashed_Sets), add a new function: function Iterate (Container : in Set) return Set_Iterator_Interfaces.Forward_Iterator'Class; Iterate returns an iterator object that will generate a value for the loop parameter designating each node in Container, starting with the first node and moving the cursor as per the Next function. Program_Error is propagated if any operation (in particular, the sequence_of_statements of the loop_statement whose iterator_specification denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization. Instead of the above, the Multiway_Tree containers would have: function Iterate (Container : in Tree) return Tree_Iterator_Interfaces.Forward_Iterator'Class; Iterate returns an iterator object that will generate a value for the loop parameter designating each node in Container, starting with the root node and proceeding in a depth-first order. Program_Error is propagated if any operation (in particular, the sequence_of_statements of the loop_statement whose iterator_specification denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization. function Iterate_Subtree (Position : in Cursor) return Tree_Iterator_Interfaces.Forward_Iterator'Class; Iterate_Subtree returns an iterator object that will generate a value for the loop parameter designating each element in the subtree rooted by the node designated by Position, starting with the node designated by Position and proceeding in a depth-first order. If Position equals No_Element, then Constraint_Error is propagated. Program_Error is propagated if any operation (in particular, the sequence_of_statements of the loop_statement whose iterator_specification denotes this object) tampers with the cursors of the container in which the node designated by Position exists while the iterator object exists. The iterator object needs finalization. function Iterate_Children (Container : in Tree; Parent : in Cursor) return Tree_Iterator_Interfaces.Reversible_Iterator'Class; Iterate_Children returns a reversible iterator object that will generate a value for the loop parameter designating each child node of Parent. If Parent equals No_Element, then Constraint_Error is propagated. If Parent does not designate a node in Container, then Program_Error is propagated. Otherwise, when used as a forward iterator, the nodes are designated starting with the first child node and moving the cursor as per the function Next_Sibling; when used as a reverse iterator, the nodes are designated starting with the last child node and moving the cursor as per the function Previous_Sibling. Program_Error is propagated if any operation (in particular, the sequence_of_statements of the loop_statement whose iterator_specification denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization. Add the following to the Erroneous Execution section of each (unbounded) container package: Execution is erroneous if the vector 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. AARM Reason: 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 non-existent 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.) Modify A.18.2(230/2), A.18.3(143/2), A.18.6(94/2), and A.18.9(113/2): "... as per {procedure} Iterate, ..." [Necessary as we are now adding Iterate functions, and this would then be ambiguous without the clarification.] For each bounded container, delete AARM implementation note that says: Package Containers.Bounded_Vectors cannot depend on package Ada.Finalization (because that package has Preelaborate categorization). Add an AARM note after the bullet about when the container needs finalization: Implementation Note: The type Vector cannot depend on package Ada.Finalization unless the element type depends on that package. The objects returned from the Iterator and Reference functions probably do depend on package Ada.Finalization. Restricted environments may need to avoid use of those functions and their associated types. Add a new clause at the end of the containers section: (Currently A.18.32) A.18.32 Example of Container Use The following example is an implementation of Dijkstra's shortest path algorithm in a directed graph with positive distances. The graph is represented by a map from nodes to sets of edges. with Ada.Containers.Vectors; with Ada.Containers.Doubly_Linked_Lists; use Ada.Containers; generic type Node is range <>; package Shortest_Paths is type Distance is new Float range 0.0 .. Float'Last; type Edge is record To, From : Node; Length : Distance; end record; package Node_Maps is new Vectors (Node, Node); -- The algorithm builds a map to indicate the node used to reach a given -- node in the shortest distance. package Adjacency_Lists is new Doubly_Linked_Lists (Edge); use Adjacency_Lists; package Graphs is new Vectors (Node, Adjacency_Lists.List); package Paths is new Doubly_Linked_Lists (Node); function Shortest_Path (G : Graphs.Vector; Source : Node; Target : Node) return Paths.List with Pre => G (Source) /= Adjacency_Lists.Empty_List; end Shortest_Paths; package body Shortest_Paths is function Shortest_Path (G : Graphs.Vector; Source : Node; Target : Node) return Paths.List is use Adjacency_Lists, Node_Maps, Paths, Graphs; Reached : array (Node) of Boolean := (others => False); -- The set of nodes whose shortest distance to the source is known. Reached_From : array (Node) of Node; So_Far : array (Node) of Distance := (others => Distance'last); The_Path : Paths.List := Paths.Empty_List; Nearest_Distance : Distance; Next : Node; begin Reached (Source) := True; So_Far (Source) := 0.0; while not Reached (Target) loop Nearest_Distance := Distance'Last; -- Find closest node not reached yet, by iterating over all nodes. -- A more efficient algorithm uses a priority queue for this step. Next := Source; for N in Node'First .. Node'Last loop if not Reached (N) and then So_Far (N) < Nearest_Distance then Next := N; Nearest_Distance := So_Far (N); end if; end loop; if Next = Source then -- No next node found, graph is not connected return Paths.Empty_List; else Reached (Next) := True; end if; -- Update minimum distance to newly reachable nodes. for E of G (Next) loop if not Reached (E.To) then Nearest_Distance := Distance'Min (So_Far (E.To) + So_Far (Next), So_Far (E.To)); if Nearest_Distance < So_Far (E.To) then Reached_From (E.To) := Next; So_Far (E.To) := Nearest_Distance; end if; end if; end loop; end loop; -- Rebuild path from target to source. declare N : Node := Target; begin while N /= Source loop N := Reached_From (N); Prepend (The_Path, N); end loop; end; return The_Path; end; end Shortest_Paths; Note that the effect of the Constant_Indexing aspect (on type Vector) and the Implicit_Dereference aspect (on type Reference_Type) is that G (Next) is a convenient short hand for G.Constant_Reference (Next).Element.all Similarly, the effect of the loop: for E of G (Next) loop if not Reached (E.To) then ... end if; end loop; is the same as: for C in G (Next).Iterate loop declare E : Edge renames G (Next)(C).all; begin if not Reached (E.To) then ... end if; end; end loop; which is the same as: declare L : Adjacency_Lists.List renames G (Next); C : Adjacency_Lists.Cursor := L.First; begin while Has_Element (C) loop declare E : Edge renames L(C).all; begin if not Reached (E.To) then ... end if; end; C := L.Next (C); end loop; end; !discussion Note that the accessibility of the returned object from a call to Reference will prevent converting the returned access discriminant to any type that lives longer than the returned object. That means that we can assume that the discriminant value cannot be accessed after the object disappears (thus we can tie the tampering check to the lifetime of the returned object). It is possible to use Unchecked_Deallocation to destroy the returned Reference_Type object prematurely, while continuing to use the returned access. The accessibility check on the container argument to the Reference function means that the returned access can't live longer than the container, but the use of Unchecked_Deallocation would allow tampering on the container. This could look like: declare type AR is access MV.Reference_Type; procedure Free is new Ada.Unchecked_Deallocation (MV.Reference_Type, AR); PAR : AR := new MV.Reference_Type'(Vect.Reference (1)) Element : access Element_Type := PAR.Element; -- OK. begin Free (PAR); -- Tampering checking ends here! Vect.Delete (1); -- No check here. ... Element.all ... -- Oops, gone. end; Obviously, this is highly unlikely to happen by accident. This is more like shooting oneself in the foot by attaching a laser target to your foot and then firing a laser-seeking missile. It doesn't seem worth worrying about someone who wants to go to these lengths to avoid a tampering check - they're just as likely to use Unchecked_Conversion or .all'Unchecked_Access or something else nasty. --- We need to be able to define the period of existence of the return object of the accessor as a time when tampering with elements is prohibited. If we didn't do this, we would be opening the door to all manner of problems (up to and including erroneousness) if an element is added or deleted to the container. To see how this could happen, consider the following example: Assume that Ada.Containers.Vectors has an accessor function defined as follows: function Reference (C : aliased in out Vector; I : Index_Type) return access Element_Type; Now consider the following program fragments: package Data is type Node (D : Boolean := True) is record case D is when True => CT : Natural; when False => CF : Float; end case; end record; subtype True_Node is Node (D => True); subtype False_Node is Node (D => False); package Node_Vector is new Ada.Containers.Vectors (Element_Type => Node, Index_Type => Positive); Node_List : Node_Vector.Vector; end Data; with Data; package Far_Away is procedure Some_Complex_Operation (...); end Far_Away; package body Far_Away is procedure Some_Complex_Operation (...) is Some_Index : Positive; begin ... Some_Index := ; ... Data.Node_List.Delete (Some_Index); ... end Some_Complex_Operation; end Far_Away; with Data; package Process is procedure Process_True (NT : in out Data.True_Node); end Process; with Far_Away; package body Process is procedure Process_True (NT : in out Data.True_Node) is Component : renames NT.CT; -- OK, NT is constrained. begin ... Far_Away.Some_Complex_Operation (...); if Component > 10 then -- ** Oh-oh!! Component has moved. ... end if; end Process_True; end Process; with Data, Process; procedure Main is begin Data.Node_List.Append (New_Item => (D => False, CF => 1.0)); -- Element 1. Data.Node_List.Append (New_Item => (D => True, CT => 4)); -- Element 2. Data.Node_List.Append (New_Item => (D => False, CF => 26.5)); -- Element 3. ... Process.Process_True (Data.Node_List.Reference (2).all); ... end Main; In the canonical implementation for a vector, all of the data is stored in an array of some size, and when a component is deleted, the rest of them slide down. So, when the Far_Away operation (probably called through many levels of calls) deletes the first element of the vector, all of the rest of them move. That means that the second element passed to Process_True unexpectedly changes its value to that which was in the third element. But that element doesn't match the constraint and doesn't even have a CT component! So we get erroneous behavior. Note that the same thing would happen if an element was inserted at the front of the vector. Note that in this example no one has copied the access value returned by the accessor. So a rule preventing copying does no good. You can also get a similar effect by renaming the access value itself and doing bad things while the renames exists. Note that the erroneousness can't happen if you use Update_Element instead, because the tampering check will cause the deletion to raise Program_Error. Similarly, cursors have rules allowing implementations to detect this sliding and raise Program_Error. This example code seems plausible; the record type is similar to many found in the author's compiler and using a library-level container is not unusual. So it seems reasonable that cases like this would happen in practice. Thus we conclude that a tampering check is needed for the accessor functions. !examples The new Reference function can be used to update an element directly. For example (based on a recent example from comp.lang.ada): with Ada.Containers.Vectors; procedure Check is package Integer_Vectors is new Ada.Containers.Vectors (Index_Type => Natural, Element_Type => Integer); package Nested_Vectors is new Ada.Containers.Vectors (Index_Type => Natural, Element_Type => Integer_Vectors.Vector, "=" => Integer_Vectors."="); IV : Integer_Vectors.Vector; NV : Nested_Vectors.Vector; begin IV.Append(42); NV.Append(IV); NV.Reference(0).Element.all.Append(43); end Check; The dereference does not need to be explicitly given in this case, and the fact that the object returned by Reference is limited and (probably) controlled is not visible in the code as the function call is used directly as a prefix. So the call can be just: NV.Reference(0).Element.Append(43); The "Implicit_Dereference" aspect of the returned object allows us to drop the ".Element" as well. That leave us with: NV.Reference(0).Append(43); Since containers have the "Variable_Indexing" aspect set to the Reference function, directly indexing the container is equivalent to a call to Reference. So we also could have written: NV(0).Append(43); This is essentially the same as what we could have written had the type of NV been an array type. You could also save the entire object as long as needed: declare My_IV : Nested_Vectors.Reference_Type := NV.Reference(0); -- Or just NV(0). begin (since My_NV would be built-in-place). Note that this design still works as intended even if the user renames only the discriminant access value: declare My_IV : Integer_Vectors.Vector renames NV.Reference(0).Element.all; begin Even in this case, the wrapping (probably controlled) object still is protecting the container, because the master of the returned object is the master of the renames: it will stick around as long as the renames does. Moreover, splitting the access from the object usually isn't possible: declare My_IV : access Integer_Vectors.Vector := NV.Reference(0).Element; -- Illegal! begin This is illegal because the accessibility check fails. The (anonymous) object returned here will have the master of the expression (as a prefix, the special "directly initializing" rule does not apply to this function call), which is shorter than the master of the declaration of My_IV. Thus the accessibility check preventing shorter lifetimes will fail. It is OK to rename the access: declare My_IV : access Integer_Vectors.Vector renames NV.Reference(0).Element; begin as in this case, the anonymous object will continue to exist until the renames is finalized. This element can be passed as a parameter, as again the wrapping object will live as long as the parameter: Operate (NV.Reference(0).Element); or Munge (NV.Reference(0).Element.all); For Maps, we define indexing by the key as well as by cursors. So if we have: type Point is record X, Y : Natural; end record; package Point_Maps is new Ada.Containers.Indefinite_Ordered_Maps (String, Point); Locations : Point_Maps.Map; Locations.Insert ("Tucker", (X | Y => 0)); Locations.Insert ("Randy", (X => 10, Y => 5)); Locations.Insert ("Steve", (X | Y => 20)); Locations.Insert ("John", (X => 50, Y => 100)); we can reference the elements directly via the keys (again we're using the Variable_Indexing aspect to simplify the calls): if Locations("Randy").X > 10 then ... if Locations("Tucker").all = (X | Y => 0) then ... Locations ("Steve").X := 15; Locations ("John").all := (X | Y => 60); The iterator functions can be used directly in a for loop: for C in My_Vector.Iterator loop if My_Vector(C).B then My_Vector(C).Cnt := My_Vector(C).Cnt + 1; end if; end loop; Note that the Reference and Constant_Reference functions (implicitly called above) are very helpful in writing the body of this sort of loop. When reasonable, the keyword "reverse" is supported with the usual meaning: for C in reverse My_Vector.Iterator loop Containers have the "Iterator" function as their default iterator. This is always the iterator that unconditionally iterates the entire contents of the container. These iterators can be used with the "of" syntax to directly access the elements of a container. This means that the first loop above can be written: for E of My_Vector loop if E.B then E.Cnt := E.Cnt + 1; end if; end loop; Note however that the cursor form is more powerful. For instance, for a Map, the default iterator can only access the elements and not the keys, while the cursor form can access both. Similarly, for a tree, the element form does not allow the loop body to know where in the tree the element exists; the cursor form allows querying Depth, Parent, and many other useful functions. !corrigendum 7.6(4/2) @drepl @xcode<@b Ada.Finalization @b @b Preelaborate(Finalization); @b Remote_Types(Finalization);> @dby @xcode<@b Ada.Finalization @b @b Pure(Finalization);> !corrigendum A.18.2(6/2) @drepl @xcode<@b @b Index_Type @b <@>; @b Element_Type @b; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Vectors @b @b Preelaborate(Vectors);> @dby @xcode<@b Ada.Iterator_Interfaces; @b @b Index_Type @b <@>; @b Element_Type @b; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Vectors @b @b Preelaborate(Vectors);> !corrigendum A.18.2(8/2) @drepl @xcode< @b Vector @b; @b Preelaborable_Initialization(Vector);> @dby @xcode< @b Vector @b @b Constant_Indexing =@> Constant_Reference, Variable_Indexing =@> Reference, Default_Iterator =@> Iterate, Iterator_Element =@> Element_Type; @b Preelaborable_Initialization(Vector);> !corrigendum A.18.2(11/2) @dinsa @xcode< No_Element : @b Cursor;> @dinss @xcode< @b Has_Element (Position : Cursor) @b Boolean; @b Vector_Iterator_Interfaces @b Ada.Iterator_Interfaces (Cursor, Has_Element);> !corrigendum A.18.2(34/2) @dinsa @xcode< @b Update_Element (Container : @b Vector; Position : @b Cursor; Process : @b (Element : @b Element_Type));> @dinss @xcode< @b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Constant_Reference (Container : @b Vector; Index : @b Index_Type) @b Constant_Reference_Type; @b Reference (Container : @b Vector; Index : @b Index_Type) @b Reference_Type; @b Constant_Reference (Container : @b Vector; Position : @b Cursor) @b Constant_Reference_Type; @b Reference (Container : @b Vector; Position : @b Cursor) @b Reference_Type;> !corrigendum A.18.2(72/2) @ddel @xcode< @b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.2(74/2) @dinsa @xcode< @b Reverse_Iterate (Container : @b Vector; Process : @b (Position : @b Cursor));> @dinss @xcode< @b Iterate (Container : @b Vector) @b Vector_Iterator_Interfaces.Reversible_Iterator'Class;> @xcode< @b Iterate (Container : @b Vector; Start : @b Cursor) @b Vector_Iterator_Interfaces.Reversible_Iterator'Class;> !corrigendum A.18.2(97/2) @dinsa @xbullet, that is, it calls the Replace_Element, Reverse_Elements, or Swap procedures or the Sort or Merge procedures of an instance of Generic_Sorting with @i as a parameter.> @dinss @xcode<@b Has_Element (Position : Cursor) @b Boolean;> @xindent !corrigendum A.18.2(147/2) @dinsa @xindent @dinss @xcode<@b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xcode<@b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xindent @xindent @xcode<@b Constant_Reference (Container : @b Vector; Index : @b Index_Type) @b Constant_Reference_Type;> @xindent @xindent @xcode<@b Reference (Container : @b Vector; Index : @b Index_Type) @b Reference_Type;> @xindent @xindent @xindent @xcode<@b Constant_Reference (Container : @b Vector; Position : @b Cursor) @b Constant_Reference_Type;> @xindent @xindent @xcode<@b Reference (Container : @b Vector; Position : @b Cursor) @b Reference_Type;> @xindent @xindent @xindent !corrigendum A.18.2(225/2) @ddel @xcode<@b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.2(226/2) @ddel @xindent !corrigendum A.18.2(230/2) @drepl @xindent @ddy @xindent @xcode<@b Iterate (Container : @b Vector) @b Vector_Iterator_Interfaces.Reversible_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> @xcode<@b Iterate (Container : @b Vector; Start : @b Cursor) @b Vector_Iterator_Interfaces.Reversible_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> !corrigendum A.18.2(240/2) @drepl Reading the value of an empty element by calling Element, Query_Element, Update_Element, Swap, Is_Sorted, Sort, Merge, "=", Find, or Reverse_Find is a bounded error. The implementation may treat the element as having any normal value (see 13.9.1) of the element type, or raise Constraint_Error or Program_Error before modifying the vector. @dby Reading the value of an empty element by calling Element, Query_Element, Update_Element, Constant_Reference, Reference, Swap, Is_Sorted, Sort, Merge, "=", Find, or Reverse_Find is a bounded error. The implementation may treat the element as having any normal value (see 13.9.1) of the element type, or raise Constraint_Error or Program_Error before modifying the vector. !corrigendum A.18.2(252/2) @dinsa The result of "=" or Has_Element is unspecified if it is called with an invalid cursor parameter. Execution is erroneous if any other subprogram declared in Containers.Vectors is called with an invalid cursor parameter. @dinst Execution is erroneous if the vector 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. !corrigendum A.18.3(5/2) @drepl @xcode<@b @b Element_Type @b; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Doubly_Linked_Lists @b @b Preelaborate(Doubly_Linked_Lists);> @dby @xcode<@b Ada.Iterator_Interfaces; @b @b Element_Type @b; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Doubly_Linked_Lists @b @b Preelaborate(Doubly_Linked_Lists); @b Remote_Types(Doubly_Linked_Lists);> !corrigendum A.18.3(6/2) @drepl @xcode< @b List @b; @b Preelaborable_Initialization(List);> @dby @xcode< @b List @b @b Constant_Indexing =@> Constant_Reference, Variable_Indexing =@> Reference, Default_Iterator =@> Iterate, Iterator_Element =@> Element_Type; @b Preelaborable_Initialization(List);> !corrigendum A.18.3(9/2) @dinsa @xcode< No_Element : @b Cursor;> @dinss @xcode< @b Has_Element (Position : Cursor) @b Boolean; @b List_Iterator_Interfaces @b Ada.Iterator_Interfaces (Cursor, Has_Element);> !corrigendum A.18.3(17/2) @dinsa @xcode< @b Update_Element (Container : @b List; Position : @b Cursor; Process : @b (Element : @b Element_Type));> @dinss @xcode< @b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Constant_Reference (Container : @b List; Position : @b Cursor) @b Constant_Reference_Type; @b Reference (Container : @b List; Position : @b Cursor) @b Reference_Type;> !corrigendum A.18.3(44/2) @ddel @xcode< @b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.3(46/2) @dinsa @xcode< @b Reverse_Iterate (Container : @b List; Process : @b (Position : @b Cursor));> @dinss @xcode< @b Iterate (Container : @b List) @b List_Iterator_Interfaces.Reversible_Iterator'Class;> @xcode< @b Iterate (Container : @b List; Start : @b Cursor) @b List_Iterator_Interfaces.Reversible_Iterator'Class;> !corrigendum A.18.3(69/2) @dinsa @xbullet, that is, it calls the Replace_Element or Swap procedures with @i as a parameter.> @dinst @xcode<@b Has_Element (Position : Cursor) @b Boolean;> @xindent !corrigendum A.18.3(86/2) @dinsa @xindent shall be unconstrained.> @dinss @xcode<@b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xcode<@b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xindent @xindent @xcode<@b Constant_Reference (Container : @b List; Position : @b Cursor) @b Constant_Reference_Type;> @xindent @xindent @xcode<@b Reference (Container : @b List; Position : @b Cursor) @b Reference_Type;> @xindent @xindent !corrigendum A.18.3(139/2) @ddel @xcode<@b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.3(140/2) @ddel @xindent !corrigendum A.18.3(144/2) @drepl @xindent @dby @xindent @xcode<@b Iterate (Container : @b List) @b List_Iterator_Interfaces.Reversible_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> @xcode<@b Iterate (Container : @b List; Start : @b Cursor) @b List_Iterator_Interfaces.Reversible_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> !corrigendum A.18.3(157/2) @dinsa The result of "=" or Has_Element is unspecified if it is called with an invalid cursor parameter. Execution is erroneous if any other subprogram declared in Containers.Doubly_Linked_Lists is called with an invalid cursor parameter. @dinst Execution is erroneous if the list 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. !corrigendum A.18.4(19/2) @dinsa Execution of the default implementation of the Input, Output, Read, or Write attribute of type Cursor raises Program_Error. @dinst @xcode<@b Has_Element (Position : Cursor) @b Boolean;> @xindent !corrigendum A.18.4(41/2) @dinsa @xindent shall be unconstrained.> @dinst @xcode<@b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xcode<@b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xindent @xindent @xcode<@b Constant_Reference (Container : @b Map; Position : @b Cursor) @b Constant_Reference_Type;> @xindent @xindent @xcode<@b Reference (Container : @b Map; Position : @b Cursor) @b Reference_Type;> @xindent @xindent @xcode<@b Constant_Reference (Container : @b Map; Key : @b Key_Type) @b Constant_Reference_Type;> @xindent @xindent @xcode<@b Reference (Container : @b Map; Key : @b Key_Type) @b Reference_Type;> @xindent @xindent !corrigendum A.18.4(72/2) @ddel @xcode<@b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.4(73/2) @ddel @xindent !corrigendum A.18.4(80/2) @dinsa The result of "=" or Has_Element is unspecified if it is called with an invalid cursor parameter. Execution is erroneous if any other subprogram declared in Containers.Hashed_Maps or Containers.Ordered_Maps is called with an invalid cursor parameter. @dinst Execution is erroneous if the map 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. !corrigendum A.18.5(2/2) @drepl @xcode<@b @b Key_Type @b; @b Element_Type @b; @b Hash (Key : Key_Type) @b Hash_Type; @b Equivalent_Keys (Left, Right : Key_Type) @b Boolean; @b "=" (Left, Right : Element_Type) @b Boolean is <@>; @b Ada.Containers.Hashed_Maps @b @b Preelaborate(Hashed_Maps);> @dby @xcode<@b Ada.Iterator_Interfaces; @b @b Key_Type @b; @b Element_Type @b; @b Hash (Key : Key_Type) @b Hash_Type; @b Equivalent_Keys (Left, Right : Key_Type) @b Boolean; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Hashed_Maps @b @b Preelaborate(Hashed_Maps); @b Remote_Types(Hashed_Maps);> !corrigendum A.18.5(3/2) @drepl @xcode< @b Map @b; @b Preelaborable_Initialization(Map);> @dby @xcode< @b Map @b @b Constant_Indexing =@> Constant_Reference, Variable_Indexing =@> Reference, Default_Iterator =@> Iterate, Iterator_Element =@> Element_Type; @b Preelaborable_Initialization(Map);> !corrigendum A.18.5(6/2) @dinsa @xcode< No_Element : @b Cursor;> @dinss @xcode< @b Has_Element (Position : Cursor) @b Boolean; @b Map_Iterator_Interfaces @b Ada.Iterator_Interfaces (Cursor, Has_Element);> !corrigendum A.18.5(17/2) @dinsa @xcode< @b Update_Element (Container : @b Map; Position : @b Cursor; Process : @b (Key : @b Key_Type; Element : @b Element_Type));> @dinss @xcode< @b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Constant_Reference (Container : @b Map; Position : @b Cursor) @b Constant_Reference_Type; @b Reference (Container : @b Map; Position : @b Cursor) @b Reference_Type; @b Constant_Reference (Container : @b Map; Key : @b Key_Type) @b Constant_Reference_Type; @b Reference (Container : @b Map; Key : @b Cursor) @b Reference_Type;> !corrigendum A.18.5(33/2) @ddel @xcode< @b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.5(37/2) @dinsa @xcode< @b Iterate (Container : @b Map; Process : @b (Position : @b Cursor));> @dinss @xcode< @b Iterate (Container : @b Map) @b Map_Iterator_Interfaces.Forward_Iterator'Class;> !corrigendum A.18.5(61/2) @dinsa Equivalent to Equivalent_Keys (Left, Key (Right)). @dinss @xcode<@b Iterate (Container : @b Map) @b Map_Iterator_Interfaces.Forward_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> !corrigendum A.18.6(2/2) @drepl @xcode<@b @b Key_Type @b; @b Element_Type @b; @b "<" (Left, Right : Key_Type) @b Boolean @b <@>; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Ordered_Maps @b @b Preelaborate(Ordered_Maps);> @dby @xcode<@b Ada.Iterator_Interfaces; @b @b Key_Type @b; @b Element_Type @b; @b "<" (Left, Right : Key_Type) @b Boolean @b <@>; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Ordered_Maps @b @b Preelaborate(Ordered_Maps); @b Remote_Types(Ordered_Maps);> !corrigendum A.18.6(4/2) @drepl @xcode< @b Map @b; @b Preelaborable_Initialization(Map);> @dby @xcode< @b Map @b @b Constant_Indexing =@> Constant_Reference, Variable_Indexing =@> Reference, Default_Iterator =@> Iterate, Iterator_Element =@> Element_Type; @b Preelaborable_Initialization(Map);> !corrigendum A.18.6(7/2) @dinsa @xcode< No_Element : @b Cursor;> @dinss @xcode< @b Has_Element (Position : Cursor) @b Boolean; @b Map_Iterator_Interfaces @b Ada.Iterator_Interfaces (Cursor, Has_Element);> !corrigendum A.18.6(16/2) @dinsa @xcode< @b Update_Element (Container : @b Map; Position : @b Cursor; Process : @b (Key : @b Key_Type; Element : @b Element_Type));> @dinss @xcode< @b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Constant_Reference (Container : @b Map; Position : @b Cursor) @b Constant_Reference_Type; @b Reference (Container : @b Map; Position : @b Cursor) @b Reference_Type; @b Constant_Reference (Container : @b Map; Key : @b Key_Type) @b Constant_Reference_Type; @b Reference (Container : @b Map; Key : @b Cursor) @b Reference_Type;> !corrigendum A.18.6(43/2) @ddel @xcode< @b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.6(46/2) @dinsa @xcode< @b Reverse_Iterate (Container : @b Map; Process : @b (Position : @b Cursor));> @dinss @xcode< @b Iterate (Container : @b Map) @b Map_Iterator_Interfaces.Reversible_Iterator'Class;> !corrigendum A.18.6(94/2) @drepl @xindent @dby @xindent @xcode<@b Iterate (Container : @b Map) @b Map_Iterator_Interfaces.Reversible_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> !corrigendum A.18.7(18/2) @dinsa Execution of the default implementation of the Input, Output, Read, or Write attribute of type Cursor raises Program_Error. @dinst @xcode<@b Has_Element (Position : Cursor) @b Boolean;> @xindent !corrigendum A.18.7(36/2) @dinsa @xindent with the element designated by Position as the argument. Program_Error is propagated if Process.@b tampers with the elements of Container. Any exception raised by Process.@b is propagated.> @dinss @xcode<@b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xindent @xindent @xcode<@b Constant_Reference (Container : @b Set; Position : @b Cursor) @b Constant_Reference_Type;> @xindent @xindent !corrigendum A.18.7(83/2) @ddel @xcode<@b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.7(84/2) @ddel @xindent !corrigendum A.18.7(96/2) @dinsa If Element_Type is unconstrained and definite, then the actual Element parameter of Process.@b shall be unconstrained. @dinss @xcode<@b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element;> @xindent @xindent @xcode<@b Reference_Preserving_Key (Container : @b Set; Position : @b Cursor) @b Reference_Type;> @xindent @xindent; then returns an object whose discriminant is an access value that designates the element designated by Position. Program_Error is propagated if any operation tampers with the elements of Container while the object returned by Reference_Preserving_Key exists and has not been finalized. When the object returned by Reference_PReserving_Key is finalized, a check is made if @i determines the same equivalence class as that for the new element; if not, the element is removed from the set and Program_Error is propagated.> @xcode<@b Constant_Reference (Container : @b Set; Key : @b Key_Type) @b Constant_Reference_Type;> @xindent @xindent @xcode<@b Reference_Preserving_Key (Container : @b Set; Key : @b Key_Type) @b Reference_Type;> @xindent @xindent !corrigendum A.18.7(101/2) @dinsa The result of "=" or Has_Element is unspecified if it is called with an invalid cursor parameter. Execution is erroneous if any other subprogram declared in Containers.Hashed_Sets or Containers.Ordered_Sets is called with an invalid cursor parameter. @dinst Execution is erroneous if the set 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. !corrigendum A.18.8(2/2) @drepl @xcode<@b @b Element_Type @b; @b Hash (Element : Element_Type) @b Hash_Type; @b Equivalent_Elements (Left, Right : Element_Type) @b Boolean; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Hashed_Sets @b @b Preelaborate(Hashed_Sets);> @dby @xcode<@b Ada.Iterator_Interfaces; @b @b Element_Type @b; @b Hash (Element : Element_Type) @b Hash_Type; @b Equivalent_Elements (Left, Right : Element_Type) @b Boolean; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Hashed_Sets @b @b Preelaborate(Hashed_Sets); @b Remote_Types(Hashed_Sets);> !corrigendum A.18.8(3/2) @drepl @xcode< @b Set @b; @b Preelaborable_Initialization(Set);> @dby @xcode< @b Set @b @b Constant_Indexing =@> Constant_Reference, Default_Iterator =@> Iterate, Iterator_Element =@> Element_Type; @b Preelaborable_Initialization(Set);> !corrigendum A.18.8(6/2) @dinsa @xcode< No_Element : @b Cursor;> @dinss @xcode< @b Has_Element (Position : Cursor) @b Boolean; @b Set_Iterator_Interfaces @b Ada.Iterator_Interfaces (Cursor, Has_Element);> !corrigendum A.18.8(17/2) @dinsa @xcode< @b Query_Element (Position : @b Cursor; Process : @b (Element : @b Element_Type));> @dinss @xcode< @b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Constant_Reference (Container : @b Set; Position : @b Cursor) @b Constant_Reference_Type;> !corrigendum A.18.8(45/2) @ddel @xcode< @b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.9(49/2) @dinsa @xcode< @b Iterate (Container : @b Set; Process : @b (Position : @b Cursor));> @dinss @xcode< @b Iterate (Container : @b Set) @b Set_Iterator_Interfaces.Forward_Iterator'Class;> !corrigendum A.18.8(58/2) @dinsa @xcode< @b Update_Element_Preserving_Key (Container : @b Set; Position : @b Cursor; Process : @b (Key : @b Key_Type; Element : @b Element_Type));> @dinss @xcode< @b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Reference_Preserving_Key (Container : @b Set; Position : @b Cursor) @b Reference_Type; @b Constant_Reference (Container : @b Set; Key : @b Key_Type) @b Constant_Reference_Type; @b Reference_Preserving_Key (Container : @b Set; Key : @b Cursor) @b Reference_Type;> !corrigendum A.18.8(85/2) @dinsa Equivalent to Equivalent_Elements (Left, Element (Right)). @dinss @xcode<@b Iterate (Container : @b Set) @b Map_Iterator_Interfaces.Forward_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> !corrigendum A.18.9(2/2) @drepl @xcode<@b @b Element_Type @b; @b "<" (Left, Right : Element_Type) @b Boolean @b <@>; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Ordered_Sets @b @b Preelaborate(Ordered_Sets);> @dby @xcode<@b Ada.Iterator_Interfaces; @b @b Element_Type @b; @b "<" (Left, Right : Element_Type) @b Boolean @b <@>; @b "=" (Left, Right : Element_Type) @b Boolean @b <@>; @b Ada.Containers.Ordered_Sets @b @b Preelaborate(Ordered_Sets); @b Remote_Types(Ordered_Sets);> !corrigendum A.18.9(4/2) @drepl @xcode< @b Set @b; @b Preelaborable_Initialization(Set);> @dby @xcode< @b Set @b @b Constant_Indexing =@> Constant_Reference, Default_Iterator =@> Iterate, Iterator_Element =@> Element_Type; @b Preelaborable_Initialization(Set);> !corrigendum A.18.9(7/2) @dinsa @xcode< No_Element : @b Cursor;> @dinss @xcode< @b Has_Element (Position : Cursor) @b Boolean; @b Set_Iterator_Interfaces @b Ada.Iterator_Interfaces (Cursor, Has_Element);> !corrigendum A.18.9(16/2) @dinsa @xcode< @b Query_Element (Position : @b Cursor; Process : @b (Element : @b Element_Type));> @dinss @xcode< @b Constant_Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Constant_Reference (Container : @b Set; Position : @b Cursor) @b Constant_Reference_Type;> !corrigendum A.18.9(53/2) @ddel @xcode< @b Has_Element (Position : Cursor) @b Boolean;> !corrigendum A.18.9(61/2) @dinsa @xcode< @b Reverse_Iterate (Container : @b Set; Process : @b (Position : @b Cursor));> @dinss @xcode< @b Iterate (Container : @b Set) @b Set_Iterator_Interfaces.Reversible_Iterator'Class;> !corrigendum A.18.9(73/2) @dinsa @xcode< @b Update_Element_Preserving_Key (Container : @b Set; Position : @b Cursor; Process : @b (Key : @b Key_Type; Element : @b Element_Type));> @dinss @xcode< @b Reference_Type (Element : @b Element_Type) @b @b Implicit_Dereference =@> Element; @b Reference_Preserving_Key (Container : @b Set; Position : @b Cursor) @b Reference_Type; @b Constant_Reference (Container : @b Set; Key : @b Key_Type) @b Constant_Reference_Type; @b Reference_Preserving_Key (Container : @b Set; Key : @b Cursor) @b Reference_Type;> !corrigendum A.18.9(113/2) @drepl @xindent @dby @xindent @xcode<@b Iterate (Container : @b Set) @b Map_Iterator_Interfaces.Reversible_Iterator'Class;> @xindent of the @fa whose @fa denotes this object) tampers with the cursors of Container while the iterator object exists. The iterator object needs finalization.> !corrigendum A.18.10(0) @dinsc Force a conflict; the real text is in the conflict file. !corrigendum A.18.18(0) @dinsc Force a conflict; the real text is in the conflict file. !corrigendum A.18.32(0) @dinsc The following example is an implementation of Dijkstra's shortest path algorithm in a directed graph with positive distances. The graph is represented by a map from nodes to sets of edges. @xcode<@b Ada.Containers.Vectors; @b Ada.Containers.Doubly_Linked_Lists; @b Ada.Containers; @b @b Node @b <@>; @b Shortest_Paths @b @b Distance @b Float @b 0.0 .. Float'Last; @b Edge @b To, From : Node; Length : Distance; @b;> @xcode< @b Node_Maps @b Vectors (Node, Node); -- @i<@ft> -- @i<@ft>> @xcode< @b Adjacency_Lists @b Doubly_Linked_Lists (Edge); @b Adjacency_Lists;> @xcode< @b Graphs @b Vectors (Node, Adjacency_Lists.List);> @xcode< @b Paths @b Doubly_Linked_Lists (Node);> @xcode< @b Shortest_Path (G : Graphs.Vector; Source : Node; Target : Node) @b Paths.List @b Pre =@> G (Source) /= Adjacency_Lists.Empty_List;> @xcode<@b Shortest_Paths;> @xcode<@b Shortest_Paths @b> @xcode< @b Shortest_Path (G : Graphs.Vector; Source : Node; Target : Node) @b Paths.List @b @b Adjacency_Lists, Node_Maps, Paths, Graphs; Reached : @b (Node) @b Boolean := (@b =@> False); -- @i<@ft>> @xcode< Reached_From : @b (Node) @b Node; So_Far : @b (Node) @b Distance := (@b =@> Distance'Last); The_Path : Paths.List := Paths.Empty_List; Nearest_Distance : Distance; Next : Node; @b Reached (Source) := True; So_Far (Source) := 0.0;> @xcode< @b Reached (Target) @b Nearest_Distance := Distance'Last;> @xcode< -- @i<@ft> -- @i<@ft>> @xcode< Next := Source; @b N @b Node'First .. Node'Last @b @b Reached (N) @b So_Far (N) < Nearest_Distance @b Next := N; Nearest_Distance := So_Far (N); @b; @b;> @xcode< @b Next = Source @b -- @i<@ft> @b Paths.Empty_List;> @xcode< @b Reached (Next) := True; @b;> @xcode< -- @i<@ft>> @xcode< @b E @b G (Next) @b @b Reached (E.To) @b Nearest_Distance := Distance'Min (So_Far (E.To) + So_Far (Next), So_Far (E.To));> @xcode< @b Nearest_Distance < So_Far (E.To) @b Reached_From (E.To) := Next; So_Far (E.To) := Nearest_Distance; @b; @b; @b; @b;> @xcode< -- @i<@ft>> @xcode< @b N : Node := Target; @b @b N /= Source @b N := Reached_From (N); Prepend (The_Path, N); @b; @b;> @xcode< @b The_Path; @b; @b Shortest_Paths;> Note that the effect of the Constant_Indexing aspect (on type Vector) and the Implicit_Dereference aspect (on type Reference_Type) is that @xcode is a convenient short hand for @xcode> Similarly, the effect of the loop: @xcode<@b E @b G (Next) @b @b Reached (E.To) @b ... @b; @b;> is the same as: @xcode<@b C @b G (Next).Iterate @b @b E : Edge @b G (Next)(C).@b; @b @b Reached (E.To) @b ... @b; @b; @b;> which is the same as: @xcode<@b L : Adjacency_Lists.List @b G (Next); C : Adjacency_Lists.Cursor := L.First; @b @b Has_Element (C) @b @b E : Edge @b L(C).@b; @b @b Reached (E.To) @b ... @b; @b; C := L.Next (C); @b; @b;> !ACATS test ACATS tests would be needed for these changes. !appendix From: Randy Brukardt Sent: Never (Written January 28, 2011) Notes on version /03 (compared to the /02 discussed at the phone meeting): The declaration of the container type does not need an interface because we replaced that by an aspect several versions back in AI05-0139-2. Sorry about having completely forgotten that. That simplifies the description and eliminates the incompatibility that we talked about. Used the name "Vector_Iterator_Interfaces" for the magic instance, as the suggested name "Iterator_Implementation" doesn't make much sense. Added notes about the Set implementation of the Reference and Constant_Reference routines for Keys. Added a sentence about the object returned from the various iterator functions needing finalization. Added a note that all of the bounded containers are Preelaborated, not Pure. (This drops A.18.19(3/3) and similar bullets.) **************************************************************** From: Randy Brukardt Sent: Monday, February 7, 2011 7:28 PM Steve brings up an interesting question in some private mail: should the Ada strings packages have changes to incorporate iterators and indexing? Presumably, we'd make those changes in this same AI. Here is a list of the various changes and how they might be used in strings packages: Aspect Implicit_Dereference This doesn't make sense for the strings packages; we don't want to be making pointers at individual characters. Requiring the individual characters to be aliased to support this strategy could cause some implementations to waste a lot of memory (on a word-addressable machine in particular). Aspect Constant_Indexing This maps nicely on the existing function Element for the Bounded and Unbounded strings. (Fixed strings, of course, already have this as an array type.) So I think we should consider adding this. Aspect Variable_Indexing This maps to procedure Replace_Element, but not sensibly. The current scheme imagines that it maps to a function returning an access to an element (using reference) -- but using an access type doesn't make much sense in this context. Rather, we'd rather pass a value to a procedure to set the element. That of course would require some sort of change to the currently defined mechanism for Variable_Indexing. I'd guess that is too complicated to define, so I think it probably would make more sense to just forget this capability. (Whether this means we should forget about providing *read* access I can't say.) Iterator interfaces These seem like overkill here, since the indexes are integers and iterating an entire object S just requires for I in 1..Length(S) loop But see the next item. Aspects Default_Iterator and Iterator_Element These would be useful, as they would enable the string to be iterated with the container syntax: for Ch of My_Unb_String loop There are a couple of issues: (1) First, without Variable_Indexing, Ch could only be used in a read-only way. That might be pretty limiting. (2) We'd have to define an iterator function much like we do in the containers to be used here. That adds a bit of complication (no big deal, though). It's not clear if this is worth it. So, the only slam-dunk is Constant_Indexing. It's not clear if that alone is worth defining (it could be confusing that writing elements isn't supported). Or we could consider trying to come up with an extra mapping for Variable_Indexing that would map: S(N) := C; into Replace_Element (S, N, C); In which case we would want to define all of these things. Thoughts?? **************************************************************** From: Tucker Taft Sent: Monday, February 7, 2011 8:06 PM > Aspect Implicit_Dereference > This doesn't make sense for the strings packages; we don't want to > be making pointers at individual characters. Requiring the individual > characters to be aliased to support this strategy could cause some > implementations to waste a lot of memory (on a word-addressable > machine in particular). We could generalize Implicit_Dereference a bit by having a different equivalence, such as "Ref" by itself is equivalent to "Ref.Discrim.all(Ref.Index)" or "Ref.Discrim.all.Field" and thereby avoid the need to have the individual elements be aliased, so long as some enclosing composite object is aliased. ... > Aspect Variable_Indexing > This maps to procedure Replace_Element, but not sensibly. The > current scheme imagines that it maps to a function returning an access > to an element (using reference) -- but using an access type doesn't > make much sense in this context. Rather, we'd rather pass a value to a > procedure to set the element. That of course would require some sort > of change to the currently defined mechanism for Variable_Indexing. > I'd guess that is too complicated to define, so I think it probably > would make more sense to just forget this capability. (Whether this > means we should forget about providing *read* access I can't say.) For Variable_Indexing, if we generalize Implicit_Dereference, then we can also generalize Variable_Indexing. ... > There are a couple of issues: > (1) First, without Variable_Indexing, Ch could only be used in a > read-only way. That might be pretty limiting. But see above. ... > Thoughts?? Might be worth investigating a generalization of Implicit_Dereference which works with unaliased subcomponents of an aliased composite object. **************************************************************** From: Randy Brukardt Sent: Monday, February 7, 2011 8:22 PM ... > We could generalize Implicit_Dereference a bit by having a different > equivalence, such as "Ref" by itself is equivalent to > "Ref.Discrim.all(Ref.Index)" or "Ref.Discrim.all.Field" > and thereby avoid the need to have the individual elements be aliased, > so long as some enclosing composite object is aliased. I don't see how this helps. The public view of an Unbounded_String is a featureless partial view. Whether or not there is any enclosing composite object that is aliased is not something that the public can know about. OTOH, the aspects are all about the *public* view of something. I don't think it is a good idea to expose anything about the implementation of this type, because there probably are implementations that violate it. For instance, you could make the scheme you suggest here work if you were willing to require that all implementations store unbounded strings as an access to a string. [Then an operation that returned such a pointer could be used as an implicit dereference.] But that would break any implementations that use a chunked implementation (which might make sense if assignments/modifications are more common in programs than the reading operations). **************************************************************** From: Tucker Taft Sent: Monday, February 7, 2011 10:28 PM > I don't see how this helps. The public view of an Unbounded_String is > a featureless partial view. Whether or not there is any enclosing > composite object that is aliased is not something that the public can know about. > OTOH, the aspects are all about the *public* view of something. Hmmm... Getting creative here. Suppose we define a new aspect "Private_Implicit_Dereference" which indicates that the associated type is a reference type which when implicitly dereferenced has the specified type: type Element_Reference(<>) is private with Private_Implicit_Dereference => Character; ... private type Unbounded_String is new Finalization.Controlled with record Chars : access String; end record; type Element_Reference(UStr : access Unbounded_String) is record Index : Positive := 1; end record with Implicit_Dereference => UStr.Chars(Index); Then we extend the Implicit_Dereference aspect to take not just a name of an access discriminant, but allow a more general name as exemplified by the above. > I don't think it is a good idea to expose anything about the > implementation of this type, because there probably are implementations that > violate it... Agreed. **************************************************************** From: Randy Brukardt Sent: Monday, February 7, 2011 10:50 PM > Hmmm... Getting creative here. Suppose we define a new aspect > "Private_Implicit_Dereference" which indicates that the associated > type is a reference type which when implicitly dereferenced has the > specified type: ... > Then we extend the Implicit_Dereference aspect to take not just a name > of an access discriminant, but allow a more general name as > exemplified by the above. That seems like a lot of mechanism for not much gain. We *really* want to be able to do assignments of values directly into elements of this type; I don't see much point in even faking a by-reference mechanism. (Note that there can be components that you can't usefully take an address of; this seems similar.) That's why I suggested changing Variable_Indexing so that it could work by calling the existing procedure Replace_Element. That also seems like quite a lot of mechanism, but at least it doesn't require faking something no one wants in the first place. That is, allow Variable_Indexing to match a procedure with three parameters, then convert S(N) := C; to Replace_Element (S, N, C); This would also require a bit more care with the definition of the two cases. In particular, for "in out" parameter passing by-copy, you would have to use Constant_Indexing on the way in and Variable_Indexing on the way out. The only unusual thing is that S(N) would not be the name of an object (rather the name of a value), so it couldn't be renamed. ============= In any case, I don't want this corner case to screw up the nice proposal. So let's not try too hard with this one... **************************************************************** From: Tucker Taft Sent: Monday, February 7, 2011 11:14 PM > That seems like a lot of mechanism for not much gain. We *really* want > to be able to do assignments of values directly into elements of this > type; I don't see much point in even faking a by-reference mechanism. > (Note that there can be components that you can't usefully take an > address of; this seems similar.) I don't understand the above paragraph. You say we want to be able to do assignments directly into elements of this type, but then you don't see much point in faking a by-reference mechanism. These seem to be contradictory statements (at least to me). > That's why I suggested changing Variable_Indexing so that it could > work by calling the existing procedure Replace_Element. That also > seems like quite a lot of mechanism, but at least it doesn't require > faking something no one wants in the first place. This would only work for elements of a by-copy type when passing an OUT or IN-OUT parameter. > That is, allow Variable_Indexing to match a procedure with three > parameters, then convert > S(N) := C; > to > Replace_Element (S, N, C); > > This would also require a bit more care with the definition of the two > cases. In particular, for "in out" parameter passing by-copy, you > would have to use Constant_Indexing on the way in and Variable_Indexing on the > way out. > > The only unusual thing is that S(N) would not be the name of an object > (rather the name of a value), so it couldn't be renamed. Sounds pretty ugly, particularly since it wouldn't work at all for non-by-copy element types. > ============= > > In any case, I don't want this corner case to screw up the nice > proposal. So let's not try too hard with this one... Agreed. We have much bigger fish to fry... **************************************************************** From: Randy Brukardt Sent: Monday, February 7, 2011 11:33 PM > I don't understand the above paragraph. You say we want to be able to > do assignments directly into elements of this type, but then you don't > see much point in faking a by-reference mechanism. > These seem to be contradictory statements (at least to me). I don't see any reason that assignment necessarily has anything to do with a by-reference mechanism. The obvious counter-example is by-copy parameter passing -- you still can assign into such parameters; even if you can get a useful address for them (and you don't have to be able to), it doesn't correspond to any other object (and certainly not the actual). In implementation terms, Janus/Ada has the concept of assignable components that you can't take an address of. For instance, most bit-packed components don't have a machine address per se (some container does, of course). Such things will always be passed by copy, and assignments always require changing the entire "container" (in the case of bit operations, you have to do a read, some masking, and then a write). Setting a single bit in a packed array always falls into this model. I was trying to abstract this model into something that makes sense at the language level. For such a thing, you can *only* talk about the container and operations on it; it doesn't make sense to even think about references to individual elements because you cannot make an assignment that way, anymore than you can talk about setting a single bit in a word (or whatever your addressible unit is). Anyway, it seems obvious that this isn't going to work very easily, so it's probably best to leave it for Ada 2020. Which leaves the question of whether to use Constant_Indexing in Bounded_Strings and Unbounded_Strings now, or forget it as insufficiently helpful. **************************************************************** From: Tucker Taft Sent: Monday, February 7, 2011 11:58 PM > I don't see any reason that assignment necessarily has anything to do > with a by-reference mechanism. The obvious counter-example is by-copy > parameter passing -- you still can assign into such parameters; even > if you can get a useful address for them (and you don't have to be > able to), it doesn't correspond to any other object (and certainly not the > actual). I think we are using the term "reference" in two different ways. The idea of the "Private_Implicit_Dereference" is that a pointer-like thing can be converted into a name of a component of a data structure. It doesn't mean it is converted into an *address*, so it would work just fine for a packed array. It necessarily *starts* with going through a "true" pointer, but then it might refer to a subcomponent of some packed structure. > In implementation terms, Janus/Ada has the concept of assignable > components that you can't take an address of. For instance, most > bit-packed components don't have a machine address per se (some > container does, of course). Such things will always be passed by copy, > and assignments always require changing the entire "container" (in the > case of bit operations, you have to do a read, some masking, and then > a write). Setting a single bit in a packed array always falls into this model. > > I was trying to abstract this model into something that makes sense at > the language level. For such a thing, you can *only* talk about the > container and operations on it; it doesn't make sense to even think > about references to individual elements because you cannot make an > assignment that way, anymore than you can talk about setting a single > bit in a word (or whatever your addressible unit is). Here is where you are using the term "reference" and seem to be implying that it requires an address. I was thinking that Ref.Discrim.all.Chars(Index) was a "reference" to a particular component of the Chars array, but there is no requirement that Ref.Discrim.all.Chars(Index)'Address be well defined. Merely that "Ref.Discrim.all.Chars(Index)" be a variable name that can be used on the LHS of an assignment or passed as an OUT parameter. Presumably it will be passed by copy if necessary, in the normal way. > Anyway, it seems obvious that this isn't going to work very easily, so > it's probably best to leave it for Ada 2020. Agreed. > > Which leaves the question of whether to use Constant_Indexing in > Bounded_Strings and Unbounded_Strings now, or forget it as > insufficiently helpful. I think it could be confusing to be able to use these in some contexts but not others. **************************************************************** From: Jean-Pierre Rosen Sent: Tuesday, February 8, 2011 3:37 AM > Which leaves the question of whether to use Constant_Indexing in > Bounded_Strings and Unbounded_Strings now, or forget it as > insufficiently helpful. I'm hesitant (like anybody I guess), but here are some thoughts FWIW: 1) This is all syntactic sugar with no new functionality. I know "it's the sugar that helps the medicine go down", but I don't think we heard much complaints that the string packages were too complicated to be usable, or anything like that. 2) We have no idea how people will receive the new forms of iterators. Will they adopt it immediately, or just stick to the old-fashioned way on the ground that it is closer to what is actually happening? No one knows. 3) Extending the new iterator mechanism in the next revision of Ada if there is demand for it will be easy. Undoing a last-minute extension that missed some important point will not. So, I'm inclined to saying: "nice idea; take a note of it for the next revision". **************************************************************** From: Edmond Schonberg Sent: Tuesday, February 8, 2011 2:42 PM I think that iteration over unbounded strings is adequately supported currently, and there is no need to extend the new iterator machinery to cover them. We know the lower bound is 1, we have the function Length, the function Element and the procedure Replace_Element. No one has complained that there is no direct indexing syntax for them, and they have not been treated as containers so far. I agree with Jean-Pierre that this can wait to see whether there is a perceived need (none so far). **************************************************************** From: Randy Brukardt Sent: Thursday, February 10, 2011 2:12 AM ... > 1) This is all syntactic sugar with no new functionality. I know "it's > the sugar that helps the medicine go down", but I don't think we heard > much complaints that the string packages were too complicated to be > usable, or anything like that. Well, I HAVE heard that. But what we can do easily won't help any of the real problems with these packages enough to make it worth the effort. We would need to tackle the string literal problem in order to do that, but we're definitely not going to do that this time. (Which only leaves users with the function "+" (S : String) return Unbounded_String renames Ada.Strings.Unbounded.To_Unbounded_String; "solution", it's considered too ugly to put into the standard package. Why NO solution is preferable escapes me. If I don't do the above, my programs look something like: Header := new Line_Rec'(Number => 1, Value => Ada.Strings.Unbounded.To_Unbounded_String ("============"), Next => null); which is not great for readability...) **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, February 10, 2011 3:15 AM Are you saying that the ban on use clauses hampers readability? I can't agree any more ;-). And I have also no problem with the renaming as "+", which was intended by JDI. YMMV ... **************************************************************** From: Robert Dewar Sent: Thursday, February 10, 2011 5:22 AM Well to me Ada.Strings.Unbounded.To_Unbounded is plain horrible compared to just To_Unbounded. This package is designed to be used without use clauses. People who ban use clauses shoot themselves in the foot in a case like this, no sympathy :-) BTW, I really think it is a pity that we did not add a unary operator intended for conversion, the use of unary "+" is a bit bogus (always fascinating to me how operators bring anti-use folks to their senses, almost no one wants to write Algebra."+"(A, B) :-) :-)) **************************************************************** From: Randy Brukardt Sent: Thursday, February 10, 2011 1:28 PM You both (maybe intentionally) missed my point. I'll use the renames here if there are more than one or two uses of "To_Unbounded_String". This is the same conditions under which I would consider using a use clause. So this sort of thing only shows up in the "rare usage" scenario. In any case, I don't find: Header := new Line_Rec'(Number => 1, Value => To_Unbounded_String ("============"), Next => null); to be any improvement. It is still way too long and wordy (for something that is logically an identity function). Also note that it is not unusual to have both Ada.Strings.Fixed and Ada.Strings.Unbounded in the same code; you can't "use" both or you'll never figure out resolution errors. (I've been there!) > BTW, I really think it is a pity that we did not add a unary operator > intended for conversion, the use of unary "+" is a bit bogus (always > fascinating to me how operators bring anti-use folks to their senses, > almost no one wants to write Algebra."+"(A, B) :-) :-)) Sorry, I sometimes write exactly that, for two reasons: (1) If there is only one operator, it takes longer to find a nearby declarative part and figure out the name of the appropriate type than it does to just write this in prefix notation. (2) If there are resolution problems, this is good way to tell the compiler exactly what you mean. Trial-and-error is often the only way to fix these things, because the typical compiler error messages aren't much help for fixing non-resolving expressions involving operations (there are just too many of them; the typical program that I debug has more than 50 "=" defined and the compiler simply can't guess effectively as to the intended meaning). It also should be noted that I have much less objection to use clauses when they are applied to overloadable entities. It's the non-overloadable entities that cause all of the trouble, requiring you to put *in* as much dot notation elsewhere as you take out locally. "use all type" will definitely let me use "use" clauses more often. Unfortunately, that doesn't help Ada.Strings.Unbounded much -- as previously noted, I think To_Unbounded_String is ridiculous with or without a use clause. **************************************************************** From: Randy Brukardt Sent: Tuesday, March 8, 2011 5:21 PM My notes for the last meeting has: Randy asks about the change from Pure to Preelaborate. Tucker wonders if we could just change Controlled to Pure (also dropping Remote_Types). No one can think of a real problem with that. There is "state" involved, but that state is managed solely by the implementation (not by the user), and there is no way to depend upon it. (A Pure Finalize routine could not depend on global variables, which might be harder to write, but tough.) I've been pondering this change, because I was unconvinced that there wouldn't be a problem. I've now convinced myself that there can be no problem with this change, and I wanted to record the reasons behind that. (And if someone knows different, please speak up now!) One topic that we touched on briefly at the meeting (but didn't get recorded) is the issue of distribution. Pure packages imply that types declared within them can be used as remote types. That cannot cause a (new) problem in this case, as package Finalization already has the Remote_Types categorization. So there is nothing to worry about there. The main thing that concerned me is that a constant controlled object does not need to act as a constant, as a writable version is provided to the Initialize/Adjust/Finalize routines. The wording changes of AI05-0054-2 stress this fact. This matters because Pure disallows library-level variables; but in general, constant controlled objects work the same as a variable. For instance, we would have trouble if a constant object of a type like the following could be declared at the library-level in a Pure package: type Ouch is new Ada.Finalization.Controlled with record Self : access Ouch; end record; procedure Initialize (Obj : in out Ouch) is begin Obj.Self := Obj'Access; end; The assignment creates a variable reference to the object itself and saves it within the object. Later routines could then use this variable reference to modify the object -- thus creating state. This is trouble because it could happen in some inner call, not necessarily within Initialize itself. Luckily, Pure packages also have to be preelaborable. A constant object declared at the library-level of a Pure package also has to have Preelaborable Initialization (PI). And a type with a non-null Initialize routine is defined to not have PI. However, constants have to be initialized, so Initialize isn't likely to be called anyway. But we could create the same problem with Adjust. That is, if we could declare an object: Owww : constant Ouch := ; such that Adjust is called, we would have the same problem. [Aside channelling Steve: Note that the preelaboration rules might in fact make this illegal simply because of the call to Adjust (which is clearly a subprogram). This illustrates the rather nasty nature of the preelaboration rules, in that they require the compiler to predict the future. (Steve has noted this problem in the past.) Note that 10.2.1(5) says "unless its elaboration performs any of the following actions:". But that is a dynamic semantics action which cannot be completely predicted at compile-time. It also mixes dynamic rules (which are typically privacy breaking) with legality rules (which are typically not privacy breaking). I have to wonder whether: C : constant Integer := (if True then 1 else Some_Function(Some_Object)); is preelaborable. Reading the letter of the wording, it would appear that it is preelaborable (the elaboration of C will not call Some_Function nor evaluate the name of Some_Object). Do we really want this?? Do we want it if True is replaced by some static expression? Should preelaborability depend on values elsewhere in the program? Enquiring minds don't really want to know. ;-) Anyway, we'll skip whether this rule applies because it doesn't matter.] However, for this to happen, we have to write something to replace which is both allowed by preelaboration and which would force a call to Adjust. If is replaced by an aggregate, no call to Adjust is made. The only other possibilities for a tagged type are an object or a function call, and these are illegal by 10.2.1(8) and 10.2.1(7), respectively. Thus, we can't create a writable reference via Adjust. We probably can create such a reference via Finalize, but that is too late to be interesting (it's hard to imagine how that could be used to create library-level state during the normal execution of the package). Thus, I conclude that a constant controlled object used in a Pure package acts as a constant through its entire life until Finalize is called. (There probably is a similar issue involving immutably limited types; but since that isn't new I'm not going to analyze it, and in any case, it doesn't hide inside of subprograms.) ---- One additional issue is whether this adds any difficulties in using or implementing the container Reference and Iterator types. On usability: Neither of these types will have PI, so these objects cannot be used at library level in Pure or Preelaborable packages. But that is OK, as we don't want default-initialized stand-alone objects of these types in the first place, and initialized ones will be illegal because of the function call needed to get the values. This implies that container dereferencing and indexing will not be available at library-level in such packages. But that is OK, because any library-level container necessarily has to be empty (can't execute the procedure calls to put anything into it). So there ought not be any such references. On implementability: The Reference object typically will contain a pointer to the container and the discriminant that points at the element. Initialize will raise Program_Error (we don't want default-initialized Reference objects), and Finalize will reset the tampering bit in the container. None of this requires any package-level objects. The Iterator object typically will contain a pointer to the container and a cursor for a position within the container. The routines to call are determined by the interface, so calls are made with dispatching calls. No package-level objects appear to be needed. In summary, I don't think there is a problem with this. ---- One tiny aside: the AARM note about the bounded forms says: Implementation Note: Package Containers.Bounded_Vectors cannot depend on package Ada.Finalization (because that package has Preelaborate categorization). This note is still wrong and still needs to be removed (as noted in the AI). The reason is different, but the effect is the same. The AARM note that I added after the "needs finalization" bullet still is needed as well (to note that the Vector type cannot itself be controlled if the element type does not have a controlled part). **************************************************************** From: Ed Schonberg Sent: Tuesday, March 15, 2011 2:00 PM Here is a revised version of Dijkstra's shortest path algorithm. It is simpler than the previous version, (it even uses anonymous array types where appropriate) and only makes use once of the new iterator form. It compiles properly with the partial implementation of iterators in GNAT, and the code incorporates the suggestions made at last meeting, including the precondition that the source node is connected to the given graph. a proper postcondition seems out of reach: describing the set of possible paths would be longer than the algorithm itself! [Editor's note: This is version /07 of the AI.] **************************************************************** From: Tucker Taft Sent: Tuesday, March 15, 2011 2:45 PM The line below could be simplified to "for N in Node loop" for what it's worth. Actually seems a little clearer to me to leave off the 'First/'Last, since you use simply "Node" as the index subtype when declaring the arrays. > for N in Node'First .. Node'Last loop **************************************************************** From: Robert Dewar Sent: Tuesday, March 15, 2011 2:53 PM > Here is a revised version of Dijkstra's shortest path algorithm. It > is simpler than the previous version, (it even uses anonymous array > types where appropriate) and only makes use once of the new iterator > form. It compiles properly with the partial implementation of > iterators in GNAT, and the code incorporates the suggestions made at > last meeting, including the precondition that the source node is > connected to the given graph. a proper postcondition seems out of > reach: describing the set of possible paths would be longer than the > algorithm itself! Where are power sets when you need them? :-) :-) **************************************************************** From: Ed Schonberg Sent: Tuesday, March 15, 2011 2:59 PM Of course, this was left-over from an earlier version. Will update **************************************************************** From: Randy Brukardt Sent: Wednesday, March 16, 2011 9:07 PM I've made this change to the AI. I also noticed that for E of G. Element (Next) loop could be written for E of G (Next) loop to take advantage of the Constant_Indexing facility. (I realize you probably removed that in order to compile it with GNAT today.) That seems important to tie into the descriptive text that I preserved from my older example. So I made this change as well. **************************************************************** From: Edmond Schonberg Sent: Wednesday, March 16, 2011 9:15 PM That's right, I wanted to make sure that I had a version that compiled. I'm happy to have the more Ada2012 version of course. **************************************************************** From: Tucker Taft Sent: Monday, May 2, 2011 9:44 PM [The appropriate part of a longer message - Editor] AI05-0212-1/10 Accessors and Iterators for Ada.Containers [The only change is to use Has_Element rather than No_Element in the iterator instances.] Approve ___X___ Disapprove ______ Abstain _______ Comments: This aspect_specification does not match the recommended formatting: type Reference_Type (Element : not null access Element_Type) is private with Implicit_Dereference => Element It should be simply: type Reference_Type (Element : not null access Element_Type) is private with Implicit_Dereference => Element It also means one less line in the RM for each such declaration. It is a bit unclear whether the following paragraph (and similar ones) are intended to be in the RM, or if it is a "meta" comment in the AI: This function (combined with the Constant_Indexing and Implicit_Dereference aspects) provides a convenient way to get read access to the individual elements of a container starting with a cursor. If it is to be in the RM, then "get read access" would be better expressed as "gain read access" I believe. Also, it is tedious to see this same paragraph over and over. Can't we just have a single user NOTE somewhere explaining the purpose of these operations? Can we simplify this repeated wording: "Iterate returns an object that can be used in a loop_statement to loop with a loop parameter designating each node in Container"? How about just saying "Iterate returns a container element iterator for Container" or something like that? **************************************************************** From: Randy Brukardt Sent: Monday, May 2, 2011 10:32 PM [The appropriate part of a longer message - Editor] > AI05-0212-1/10 Accessors and Iterators for Ada.Containers > [The only change is to use Has_Element rather than No_Element in > the iterator instances.] > Approve ___X___ Disapprove ______ Abstain _______ > Comments: ... > It is a bit unclear whether the following paragraph (and similar ones) are > intended to be in the RM, or if it is a "meta" comment in the AI: > This function (combined with the Constant_Indexing and Implicit_Dereference > aspects) provides a convenient way to get read access to the individual > elements of a container starting with a cursor. > If it is to be in the RM, then "get read access" would be better expressed > as "gain read access" I believe. Also, it is tedious to see this same > paragraph over and over. Can't we just have a single user NOTE somewhere > explaining the purpose of these operations? The intent is that the container definitions are expected to be mostly referred to in a reference fashion. Thus we have generally put each note (AARM and User) in each clause (since we don't expect people to read the Vectors in order to understand the Lists). And it was intended to put this text in the RM, because otherwise the purpose of these functions is very mysterious. And remember that there are only two of these per container; the repetition is much more obvious in this AI where they are all lumped together. It might make sense to try to combine the note for both routines (so it doesn't have to be repeated on the second one), but an obvious way to do that doesn't jump out at me (since the indexing aspects differ for the two routines). > Can we simplify this repeated wording: "Iterate returns an object that can > be used in a loop_statement to loop with a loop parameter designating each > node in Container"? How about just saying "Iterate returns a container element > iterator for Container" or something like that? This wording was designed to be as close as possible to the existing Iterator, thus we get the part about "designating each node in Container". The rest of it is just to make that part make sense. I definitely don't want to say *less* about that here, especially as this is the only text that specifies what cursors are returned, and in what order. That had better be specified somewhere! If you have a better suggestion that includes that text (which I don't want to have to reorganize or reword, because that is very likely to be full of errors), I'd be glad to hear it. **************************************************************** From: Tucker Taft Sent: Tuesday, May 3, 2011 7:44 AM [The appropriate part of a longer message - Editor] >> Can we simplify this repeated wording: "Iterate returns an object that can >> be used in a loop_statement to loop with a loop parameter designating each >> node in Container"? How about just saying "Iterate returns a container element >> iterator for Container" or something like that? > > This wording was designed to be as close as possible to the existing > Iterator, thus we get the part about "designating each node in Container". > The rest of it is just to make that part make sense. I definitely > don't want to say *less* about that here, especially as this is the > only text that specifies what cursors are returned, and in what order. > That had better be specified somewhere! If you have a better > suggestion that includes that text (which I don't want to have to > reorganize or reword, because that is very likely to be full of errors), I'd be glad > to hear it. I was suggesting using the term "container element iterator" which is defined in the Iterator AI. I see no reason to repeat the definition of an container element iterator, though of course a "see 5.5.1" might be helpful. **************************************************************** From: Randy Brukardt Sent: Tuesday, May 3, 2011 5:33 PM [The appropriate part of a longer message - Editor] > >> ... AI05-0212-1/10 Accessors and Iterators for Ada.Containers ... > >> Can we simplify this repeated wording: "Iterate returns an object > >> that can be used in a loop_statement to loop with a loop parameter > >> designating each node in Container"? How about just saying "Iterate > >> returns a container element > >> iterator for Container" or something like that? > > > > This wording was designed to be as close as possible to the existing > > Iterator, thus we get the part about "designating each node in Container". > > The rest of it is just to make that part make sense. I definitely > > don't want to say *less* about that here, especially as this is the > > only text that specifies what cursors are returned, and in what order. > > That had better be specified somewhere! If you have a better > > suggestion that includes that text (which I don't want to have to > > reorganize or reword, because that is very likely to be full of > > errors), I'd be glad to hear it. > > I was suggesting using the term "container element iterator" > which is defined in the Iterator AI. I see no reason to repeat the > definition of an container element iterator, though of course a "see > 5.5.1" might be helpful. Well, we have to define which elements are returned and in what order. 5.5.1 and 5.5.2 provides no guidance there (and it can't, since these are general concepts). Remember that the actual type of the iterator is not defined here and thus the routines used to implement it aren't known either. (There is nothing requiring that the iterator Next does the same thing as the container Next, and that's rather important.) A second point is that this is a function returning an "iterator object". That object can be used in the first form of iterator (I'm reading the definitions in 5.5.1 and 5.5.2.) Some of these functions are also used to provide the object for the default "container element iterator" (for the third form), but that is a secondary purpose. (And not all of them provide "container element iterator"s at all.) Remember that the first form allows one to iterate through the cursors of a container, the third form allows one to iterate through the elements of a container. You can get elements given a cursor, but you can't get cursors given elements, so the first form is more general. And this text is talking about the first form (if you are using the third form, you would not call this function explicitly). So I think your suggestion is just plain wrong. I can imagine simplifying this text somehow, but it doesn't have anything to do with a "container element iterator". The text currently is: Iterate returns an object that can be used in a loop_statement to loop with a loop parameter designating each node in Container, starting with the first node and moving the cursor as per the Next function if the reserved word *reverse* is not used in the iterator_specification, and starting with the last node and moving the cursor as per the Previous function otherwise. We could try to eliminate some small parts of this using more terminology from 139-2, but we need most of it. In particular, we could say that the function returns an "iterator object (see 5.5.1)", when used in a "generalized iterator (see 5.5.2)", and describe the semantics for "forward iterators" and "reverse iterators". That would look something like: Iterate returns an iterator object (see 5.5.1) that can be used in a generalized iterator (see 5.5.2) causing the loop parameter to designate each node in Container, starting with the first node and moving the cursor as per the Next function if used as a forward iterator, and starting with the last node and moving the cursor as per the Previous function if used as a reverse iterator. I'm not sure this is a huge improvement, as we still have to mention the loop_statement in the tampering check text which follows this. But it does use the defined terminology. (I didn't use that originally because I thought you were going to get rid of a lot of it, as much of it never seems to be used. Maybe you're just trying to justify that work. ;-) **************************************************************** From: Tucker Taft Sent: Tuesday, May 3, 2011 6:59 PM > Iterate returns an iterator object (see 5.5.1) that can be used in a > generalized iterator (see 5.5.2) causing the loop parameter to > designate each node in Container, starting with the first node and > moving the cursor as per the Next function if used as a forward > iterator, and starting with the last node and moving the cursor as per > the Previous function if used as a reverse iterator. I don't think we need to say "that can be used in a generalized iterator causing the loop parameter" since that is all implicit in the definition of iterator object. How about something like this: Iterate returns a reversible iterator object (see 5.5.1) representing the sequence of all nodes of the Container, with First, Next, Last, and Previous corresponding to the same-named operations of this package. **************************************************************** From: Randy Brukardt Sent: Wednesday, May 4, 2011 2:47 PM That would work for the most of the basic iterators (modulo dealing with the iterators that aren't reversible), but it wouldn't work for the new ones that include a starting position. We don't have any existing functions that would return that would return that start position, so talking about First and Last would be very confusing. It also wouldn't work for the full tree ones, which just say "depth-first order"; there are no existing functions that will directly produce that order (you need a combination and a stack of some sort [or recursion, which can't be used in the Iterator object for obvious reasons]). I was trying to write wording that would change the existing iterator text as little as possible, rather than discarding it completely and starting over (with the certain set of bugs that will be introduced). Maybe that wasn't a worthy goal, but I don't see how to fit all of the iterators into the form you are proposing. (The AI has six different templates for Iterator text, I need to be able to change them all.) **************************************************************** From: Tucker Taft Sent: Wednesday, May 4, 2011 3:01 PM It just feels that we should be able to say what an iterator represents without a whole paragraph, especially if they are going to be used regularly. An iterator represents a sequence of elements. If "reverse" is present, then the sequence is processed from last to first, otherwise it is processed from first to last, but it is still just a single sequence. I would like us to be able to describe that sequence (e.g. is a depth-first walk of the nodes) and not say a whole lot more. We shouldn't have to mention First, Next, Last, and Previous at all ideally. We should just define the sequence, and have everything else be common semantics, which we define in just one place. **************************************************************** From: Randy Brukardt Sent: Thursday, May 5, 2011 2:07 PM > It just feels that we should be able to say what an iterator > represents without a whole paragraph, especially if they are going to > be used regularly. An iterator represents a sequence of elements. If > "reverse" > is present, then the sequence is processed from last to first, > otherwise it is processed from first to last, but it is still just a > single sequence. You are overspecifying what an iterator itself does. All it says is that a series of cursors will be presented in some order with some starting cursor. That order is defined by the specific iterator type, and all I'm trying to do is ensure that that order is well-defined. In particular, there is absolutely no requirement that an iterator "processes from first to last". It does whatever it is defined to do, limited only by the cleverness of the implementers! There is no concept of "first to last" for a depth first order, and it would be fine to have an iterator that processed in a random order. > I would like us to be able to describe that sequence (e.g. is a > depth-first walk of the nodes) and not say a whole lot more. That's fine and that's what I'm proposing. > We shouldn't have to mention First, Next, Last, and Previous at all > ideally. And we wouldn't for the trees, because the order is defined somewhere else. Your problem here is that you are looking at the wording for the Vector and the List, which is the most complex of all, and thinking that is the way they all will look. Neither Vectors nor Lists have much defined about ordering; pretty much everything is written in terms of using Next to find the next element. Clearly, we could rewrite all of this text to talk about starting at the first node and proceeding by successors -- but is that really helping anything?? And if we change this, we also have to change the existing iterator description (it needs to be consistent - it should be crystal clear that you can convert one to the other). This seems to me to be an exercise in churn; nothing will be shorter or more understandable, it will just be different. > We should just define the sequence, > and have everything else be common semantics, which we define in just > one place. Well, that doesn't make any sense, because the sequence itself would have to defined individually for each container, that hardly is "one place". And if we did define the sequence separately from the iterators, that is only going to save about one sentence total in the RM. Is that really worth it?? **************************************************************** From: Randy Brukardt Sent: Thursday, July 28, 2011 1:43 AM I was explaining how empty elements work to Christoph Grein and noticed that the bounded error for the reading of empty elements was not updated to include the creation of a reference to an empty element. Since Reference is just a better version of Update_Element, and Constant_Reference is just a better version of Query_Element, we ought to be using the same rules for both (and Update_Element and Query_Element are in this bounded error). This seems absolutely necessary, as in the indefinite container case, the element has no tag/bounds/discriminants. How one could create a reference to something with no bounds or tag or discriminants escapes me. And assigning something into a reference to such an object and magically have an object with the right bounds/tag/discriminants appear doesn't seem any more possible. It's annoying that something like: Cont(1) := 10; is a bounded error if the element (1) is an empty element, but that is already true for the Ada 2005 containers (using Update_Element) and it doesn't seem possible to avoid it without putting major constraints on the implementation (or making the indefinite version much more different). Of course, it is easy to avoid creating an empty element (don't use Insert_Space), so this is not a major hardship. So, Reference and Constant_Reference should be added to the list of routines in A.18.2(240/2). P.S. Reading an empty element is a bounded error so that it isn't required to have an "Is_Empty" bit with every element of a definite type. That seemed like overkill, especially for small elements like characters. If no exception is raised, the element must have a normal (but possibly invalid) value. **************************************************************** From: Randy Brukardt Sent: Thursday, October 13, 2011 12:20 AM Brad had in his review: > 14) A.18.2(230.2/3, 230.4/3) "will generate a loop parameter" > How can Iterate "generate" a loop parameter. > A loop parameter is a declaration. To generate one, it requires > someone to type the declaration in a text editor. I think "generate" > is the wrong choice of word. [He then goes on to repeat this comment a dozen more times, just in case I missed it the first time. :-)] This is really talking about the different values that the loop parameter will take on; the iterator "generates" those values. That's still the best word I can think of to describe what's going on. I do agree that "loop parameter" by itself is confusing, it really talking about the values that the parameter will have. So let's start with the original wording: Iterate returns a reversible iterator object that will generate a loop parameter designating each node in Container, ... [the "..." here describes the order the values are provided, and it is different for each iterator function; I want to concentrate on the common part so I don't just move the bump around.] The easiest fix is just to insert "value": Iterate returns a reversible iterator object that will generate a loop parameter {value} designating each node in Container, ... or reordering: Iterate returns a reversible iterator object that will generate a {value of the} loop parameter designating each node in Container, ... This is still a bit dicey, in that when the default iterator is used in the "of" form, the loop parameter is the element, not the cursor designating, but given the equivalence of the forms it should be OK. So, is this enough or is there some other change that would fix this problem?? **************************************************************** From: Tucker Taft Sent: Thursday, October 13, 2011 12:31 AM I agree with "... will generate a value of the loop parameter" or perhaps "... will generate a value for the loop parameter ..." **************************************************************** From: Brad Moore Sent: Thursday, October 13, 2011 8:05 AM I am also happy with either of these though I think "for" is marginally better than "of". ****************************************************************