Version 1.1 of ai05s/ai05-0069-1.txt
!standard A.18(0/2) 07-10-24 AI05-0069-1/01
!class Amendment 07-10-24
!status work item 07-10-24
!status received 07-10-02
!priority Medium
!difficulty Hard
!subject Singleton container
!summary
(See proposal.)
!problem
It not possible in Ada to declare an object of an indefinite type that can hold
any value of that type -- the language requires an initialization value and for
the object to be constrained by that value. Worse, components of indefinite types
are illegal.
However, it is often useful to have objects and components of indefinite types.
One example is the classwide type at the root of a tree of types. It's not uncommon
to have operations that produce values of such a type, and to want to store those
values as components of a type.
This can be done using access types and explicit storage management. However, this
is error-prone. Moreover, we've defined containers to eliminate the need for explicit
memory management of common data structures.
Thus it is appropriate to have have a container to handle this operation.
!proposal
Define a singleton container as follows:
generic
type Element_Type (<>) is private;
package Ada.Containers.Holders is
--
--
--
--
type Holder is tagged private;
function To_Holder (New_Item : Element_Type) return Holder;
--
function Is_Empty (Container : Holder) return Boolean;
--
--
procedure Clear (Container : Holder);
--
function Element (Container : Holder) return Element_Type;
--
--
procedure Replace_Element (Container : in out Holder;
New_Item : in Element_Type);
--
--
--
procedure Query_Element (Container : in Holder;
Process : not null access procedure (Element : in Element_Type));
--
--
--
--
procedure Update_Element (Container : in Holder;
Process : not null access procedure (Element : in out Element_Type));
--
--
--
--
procedure Move (Target : in out Holder; Source : in out Holder);
--
--
--
--
private
--
end Ada.Containers.Holders;
!wording
** TBD ** (Most of the needed wording is given as comments above; the definition of
tampering is given below.)
A subprogram is said to tamper with elements of a holder object H if:
* It clears the element contained by H, that is, it calls the Clear procedure with H
as a parameter;
* It replaces the element contained by H, that is, it calls the Replace_Element
procedure with H as a parameter;
* It calls the Move procedure with H as a parameter;
* It finalizes H.
!discussion
Note that it isn't necessary to define a definite version of this container; that would
just be a complicated way to write "A : Element_Type".
!example
** TBD **
!ACATS test
ACATS C-Test(s) are necessary for this package.
!appendix
From: Tucker Taft
Date: Tuesday, October 2, 2007 8:06 PM
As part of my homework for ASIS SI-24, the semantic
model, I am trying to work out a version using tagged
types rather than untagged discriminated types.
One thing we agreed would be necessary is some kind
of singleton container that can be used to store
class-wide values returned from various functions.
I have constructed such a container, which I chose
to call a "holder." I have attached the proposed
spec and body, which I tried to make similar to
indefinite_vectors, etc. There seems no particular
value for a holder of "definite" elements, so I
didn't bother to define both a "definite" version
and an "indefinite" version. This one handles
either.
As you may remember, the key problem I saw with using
tagged types for the ASIS semantic model is that the operations
will often be designed to return a class-wide object, but then
the caller may want to be able to store this into
a preexisting local variable, or the field of some
record. That doesn't work for a class-wide type.
However, a "holder" can be used for this purpose.
Comments welcome. My intent is to define a Holder type
for each of the main kinds of views/entities.
The user could of course declare more if they
wanted finer control.
---
with Ada.Streams;
with Ada.Finalization;
generic
type Element_Type (<>) is private;
package Ada_Containers_Holders is
-- This package provides a "holder" of a definite type that contains
-- a single value of an indefinite type.
-- This allows one to effectively declare an uninitialized variable
-- of an indefinite type.
type Holder is tagged private;
procedure Replace_Contents(
Container : in out Holder; Contents : Element_Type);
-- This assigns Contents into the Container, replacing
-- any preexisting content of Container.
generic
with procedure Update(Contents : in out Element_Type);
procedure Update_Contents(Container : in out Holder);
-- This calls the given procedure with the contents, if
-- any, of the Container. It is a no-op if the
-- container is empty.
procedure Move(Target : in out Holder; Source : in out Holder);
-- This moves the contents of Source into Target, leaving
-- Source empty, and replacing any preexisting content of Target.
-- This is a no-op if Target and Source are the same object.
function Contents(Container : Holder) return Element_Type;
-- This returns the element, if any, stored in the holder.
-- If no element is stored in the holder, Constraint_Error is raised.
function Is_Empty(Container : Holder) return Boolean;
-- This returns True if the holder is empty, and
-- False if it contains an element.
private
type Element_Ptr is access Element_Type;
type Holder is new Ada.Finalization.Controlled with record
Contents : Element_Ptr := null;
end record;
procedure Adjust(Container : in out Holder);
-- This makes a copy of Container.Contents.all
-- if Container.Contents non-null
procedure Finalize(Container : in out Holder);
-- This calls Unchecked_Deallocation on Container.Contents
function "="(Left, Right : Holder) return Boolean;
-- Make sure that equality works
-- Provide stream attributes
use Ada.Streams;
procedure Write(Stream : access Root_Stream_Type'Class;
Container : in Holder);
for Holder'Write use Write;
procedure Read(Stream : access Root_Stream_Type'Class;
Container : out Holder);
for Holder'Read use Read;
end Ada_Containers_Holders;
---
with Ada.Unchecked_Deallocation;
package body Ada_Containers_Holders is
-- This package provides a definite type that contains
-- a single value of an indefinite type.
------------- Local and private declarations --------------
procedure Free is new
Ada.Unchecked_Deallocation(Element_Type, Element_Ptr);
procedure Adjust(Container : in out Holder) is
-- This makes a copy of Container.Contents.all if
-- Container.Contents non-null
begin
if Container.Contents /= null then
Container.Contents := new Element_Type'(Container.Contents.all);
end if;
end Adjust;
procedure Finalize(Container : in out Holder) is
-- This calls Unchecked_Deallocation on Container.Contents
begin
Free(Container.Contents);
end Finalize;
function "="(Left, Right : Holder) return Boolean is
-- Make sure that equality works
begin
return Left.Contents = Right.Contents or else
(Left.Contents /= null and then Right.Contents /= null
and then Left.Contents.all = Right.Contents.all);
end "=";
-- Provide stream attributes
procedure Write(Stream : access Root_Stream_Type'Class;
Container : in Holder) is
-- Write a boolean indicating whether Holder contains
-- an element, and if True, write out the element.
Is_Present : constant Boolean := Container.Contents /= null;
begin
Boolean'Write(Stream, Is_Present);
if Is_Present then
Element_Type'Output(Stream, Container.Contents.all);
end if;
end Write;
procedure Read(Stream : access Root_Stream_Type'Class;
Container : out Holder) is
-- Read a boolean, and then if true, read the element
Is_Present : constant Boolean := Boolean'Input(Stream);
begin
Free(Container.Contents);
if Is_Present then
Container.Contents := new Element_Type'(Element_Type'Input(Stream));
end if;
end Read;
------------ Visible Subprograms ---------------
procedure Replace_Contents(
Container : in out Holder; Contents : Element_Type) is
-- This assigns Contents into the Container, replacing
-- any preexisting content of Container.
begin
Free(Container.Contents);
Container.Contents := new Element_Type'(Contents);
end Replace_Contents;
procedure Update_Contents(Container : in out Holder) is
-- This calls the given procedure with the contents, if
-- any, of the Container. It is a no-op if the
-- container is empty.
begin
if Container.Contents /= null then
Update(Container.Contents.all);
end if;
end Update_Contents;
procedure Move(Target : in out Holder; Source : in out Holder) is
-- This moves the contents of Source into Target, leaving
-- Source empty, and replacing any preexisting content of Target.
-- This is a no-op if Target and Source are the same object.
begin
if Target.Contents /= Source.Contents then
Free(Target.Contents);
Target.Contents := Source.Contents;
Source.Contents := null;
end if;
-- NOTE: If Target.Contents = Source.Contents then
-- either both are null, or Target and Source are
-- the same object (or so I claim).
end Move;
function Contents(Container : Holder) return Element_Type is
-- This returns the element, if any, stored in the holder.
-- If no element is stored in the holder, Constraint_Error is raised.
begin
return Container.Contents.all;
end Contents;
function Is_Empty(Container : Holder) return Boolean is
-- This returns True if the holder is empty, and
-- False if it contains an element.
begin
return Container.Contents = null;
end Is_Empty;
end Ada_Containers_Holders;
****************************************************************
From: Pascal Leroy
Date: Wednesday, October 3, 2007 12:53 AM
> Comments welcome. My intent is to define a Holder type for
> each of the main kinds of views/entities.
> The user could of course declare more if they wanted finer control.
It would seem to make sense to have a procedure Clear to empty the holder.
To some extent, Clear and Is_Empty go together.
****************************************************************
From: Jean-Pierre Rosen
Date: Wednesday, October 3, 2007 2:33 AM
> As part of my homework for ASIS SI-24, the semantic
> model, I am trying to work out a version using tagged
> types rather than untagged discriminated types.
> One thing we agreed would be necessary is some kind
> of singleton container that can be used to store
> class-wide values returned from various functions.
> I have constructed such a container, which I chose
> to call a "holder." I have attached the proposed
> spec and body, which I tried to make similar to
> indefinite_vectors, etc. There seems no particular
> value for a holder of "definite" elements, so I
> didn't bother to define both a "definite" version
> and an "indefinite" version. This one handles
> either.
Looks nice and useful. So useful actually that it has nothing special to
do with ASIS. Even if we take the opportunity to put this in ASIS05, is
there a problem in naming it ada.containers.holders ? This could
encourage implementations (even if they do not provide ASIS) to provide
this one as part of the standard library.
****************************************************************
From: Tucker Taft
Date: Wednesday, October 3, 2007 12:24 PM
It was my hope that it could be named
Ada.Containers.Holders in the expectation that
it would be the first of various additions to the
Containers hierarchy.
I think I'll add something like "Clear,"
but call it "Set_Empty" to make
its relationship to "Is_Empty" crystal "clear". ;-)
I was also considering changing the name of the formal
type to "Contents_Type" from "Element_Type," to
be consistent with the use of the function name "Contents"
rather than "Element".
****************************************************************
From: Tucker Taft
Date: Wednesday, October 3, 2007 12:41 PM
Actually, I guess "Clear" is used consistently
to mean Set_Empty in the other Containers, so
I should probably use that here as well.
****************************************************************
From: Pascal Leroy
Date: Wednesday, October 3, 2007 2:05 AM
> Actually, I guess "Clear" is used consistently to mean
> Set_Empty in the other Containers, so I should probably use
> that here as well.
I think consistency with the other containers would be a good thing. So I
prefer Clear to Set_Empty and Element_Type to Content_Type. No point in
changing names unless the semantics are clearly distinct.
****************************************************************
From: Randy Brukardt
Date: Wednesday, October 24, 2007 7:33 PM
For the record, this goes for all of the names (and wording!) in this container:
Update_Element, not Update_Contents;
Query_Element needs to exist;
Element, not Contents, to retrieve the element;
Replace_Element, not Replace_Contents;
and, finally, the operations need to be declared in the same order as in the
other containers (otherwise, John will beat me up during editorial review).
I'll make all of these changes in the AI, so we don't (I hope) have to argue
about that for hours.
****************************************************************
From: Randy Brukardt
Date: Wednesday, October 24, 2007 8:06 PM
Having written that, I found more:
Function wording starts with "Returns", not "This returns";
Lots of other wording issues (copy the Vector container wording
as closely as possible);
The 2nd parameter name for Replace_Element should be New_Item;
Query_Element and Update_Element should take an access-to-subprogram,
not be generic.
Possibly controversial:
Query_Element and Update_Element should raise Constraint_Error if the holder
is empty to be consistent with Element (Tucker called it "Contents"). For existing
containers, this is a Bounded_Error in all three cases, but none of the error
possibilities is no-op (which is what Tucker used for Update_Element). It seems
to me that defining this as a no-op is dangerous, because the update will not happen
without any indication as to why; the possibility of using the existing (random)
element only makes sense for definite containers.
We also need the tampering and propagation rules.
We should have a To_Holder function:
function To_Holder (New_Item : Element_Type) return Holder;
-- Returns a non-empty holder containing an element initialized to New_Item.
This is similar to the To_Vector function of a vector. (But this one is not as common as
the other operations.)
Yes, I looked at every routine in Vectors to see if it was relevant. That's probably
the only way to do this.
Amazing how many details there are even in such a simple container...
****************************************************************
Questions? Ask the ACAA Technical Agent