comp.lang.ada
 help / color / mirror / Atom feed
* Re: class-wide objects
  1998-12-07  0:00 ` Matthew Heaney
@ 1998-12-06  0:00   ` David Botton
  1998-12-09  0:00   ` Francois Godme
  1 sibling, 0 replies; 11+ messages in thread
From: David Botton @ 1998-12-06  0:00 UTC (permalink / raw)


Thanks to Matthew Heaney, the article is also available on the Ada Source
Code Treasury at:

http://www.botton.com/ada/alg/activeiter.html

David Botton


Matthew Heaney wrote in message ...
>Francois Godme <fgodme@magic.fr> writes:
>The complete text of my example is at the Patterns archive at the ACM,
>under the Nov 1998 link.  The subject is "Iterator and Factory Method
>Patterns Combined."
>
><http://www.acm.org/archives/patterns.html>
>







^ permalink raw reply	[flat|nested] 11+ messages in thread

* class-wide objects
@ 1998-12-07  0:00 Francois Godme
  1998-12-07  0:00 ` Tucker Taft
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Francois Godme @ 1998-12-07  0:00 UTC (permalink / raw)


Sometimes, pointers are used in an application only because the
programming language does not offer good support for dynamically sized
objects.

Ada has very good support for dynamically sized objects. Global objects
and stack objects can be sized as late as the elaboration of their
declaration. For global objects, it means size is fixed during
elaboration of the program,  for stack objects, it means size is fixed
during entry into the procedure or function which declares them. Stack
allocation has lots of advantages: it's fast and it's not prone to
memory fragmentation and memory leak because it works on an all or
nothing scheme.

In the following examples, one can see that Ada supports well dynamic
objects because array attributes and
reading of record discriminant allow to gain information on the actual
parameter. With this information, first, some subtypes are defined, then
objects or types can be built on top of them.

procedure P1 (S : in String;
              N : in Natural) is

   Local : String (S'Range); -- anonymous subtype

   subtype T_Local_String is String (S'Range);

   Local_Array : array (1 .. N) of T_Local_String;

begin
   ...
end P1;

procedure P2 (S : in Square; -- Square is defined Page 3-35 RM Ada83
              N : in Natural) is

   Local : Square (S.Side); -- Side is the discriminant of type Square

   subtype T_Local_Square is Square (S.Side);

   Local_Array : array (1 .. N) of T_Local_Square;

begin
   ...
end P2;

Ada95 has introduced tagged objects and class-wide objects. Class-wide
object types are here to achieve polymorphism. They serve as formal
parameters or to define access types. A class-wide object is a
dynamically sized object. It has the size of the actual tagged object
parameter passed to the formal parameter.

Since the introduction of class-wide objects, support for dynamically
sized objects, as far as I know and understand Ada95, has dropped down.
Correct me if I'm wrong. Ada95 support for dynamically sized objects is
now incomplete to allow pointer-free code because Ada95 doesn't provide
a mechanism to gain information on the actual tagged object suitable to
define a subtype.

Ada95 allows a local variable to be constrained by the actual tagged
parameter but in an incomplete way:

procedure P3 (S : in Point'Class;
              N : in Natural) is

   Local : Point'Class := S; -- But what if type point is limited ?

begin
   ...
end P3;

An attribute to get the subtype of the actual tagged parameter would
solve both problems. May be we could combine
the two attributes 'Class and 'Tag together:

procedure P4 (S : in Point'Class;
              N : in Natural) is

   Local : Point'Class (S'Tag); -- allocates an object with the tag of S

   subtype T_Local_Point is Point'Class (S'Tag);

   Local_Array : array (1 .. N) of T_Local_Point;

begin
   ...
end P4;

What do you think ? Am I right or am I missing something ? Could the
GNAT team provide an implementation defined attribute for this ?

Francois Godme






^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-07  0:00 class-wide objects Francois Godme
  1998-12-07  0:00 ` Tucker Taft
  1998-12-07  0:00 ` Matthew Heaney
@ 1998-12-07  0:00 ` Tom Moran
  2 siblings, 0 replies; 11+ messages in thread
From: Tom Moran @ 1998-12-07  0:00 UTC (permalink / raw)


>Could the
>GNAT team provide an implementation defined attribute for this ?
 There have been several suggestions recently to 'use the Gnat pragma
xxx' to solve problems.  One of the goals of Ada is portability, and
suggestions to limit programs to work on Vendor X's system only,
should be taken with a recognition of the costs of non-portability. Of
course, if something is a good addition to Ada, it would be good for
all vendors to implement it - the same way - and then it can be used
in portable programs..




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-07  0:00 class-wide objects Francois Godme
  1998-12-07  0:00 ` Tucker Taft
@ 1998-12-07  0:00 ` Matthew Heaney
  1998-12-06  0:00   ` David Botton
  1998-12-09  0:00   ` Francois Godme
  1998-12-07  0:00 ` Tom Moran
  2 siblings, 2 replies; 11+ messages in thread
From: Matthew Heaney @ 1998-12-07  0:00 UTC (permalink / raw)


Francois Godme <fgodme@magic.fr> writes:

> Since the introduction of class-wide objects, support for dynamically
> sized objects, as far as I know and understand Ada95, has dropped down.
> Correct me if I'm wrong. Ada95 support for dynamically sized objects is
> now incomplete to allow pointer-free code because Ada95 doesn't provide
> a mechanism to gain information on the actual tagged object suitable to
> define a subtype.
>
> Ada95 allows a local variable to be constrained by the actual tagged
> parameter but in an incomplete way:
> 
> procedure P3 (S : in Point'Class;
>               N : in Natural) is
> 
>    Local : Point'Class := S; -- But what if type point is limited ?
> 
> begin
>    ...
> end P3;


But the example you give is not specific to Ada95.  Consider this
untagged type:

package Points is

   type Point is limited private;
...
end Points;

procedure Op (P : in Point) is

   Local : Point := P;  -- illegal, because P is limited

begin

Of course, the type as declared isn't indefinite, so you could do this:

procedure Op (P : in Point) is

   Local : Point;
begin
   Local := P;  -- illegal

but that too is illegal, because you just moved assignment into the
executable region.

The fact that something is tagged makes no difference.  It is
limitedness and indefiniteness that is the cause of your problem.

To solve you problem, you have a couple of choices:

1) Make the type non-limited.  This gives you assignment.

This is exactly the approach I took in my example of combining active
iteration with a factory method.  For example:

  procedure Copy 
    (From : in     Stack_Type'Class;
     To   : in out Stack_Type) is

     Iterator : Iterator_Type'Class := 
       Start_At_Bottom (From);
  begin

The iterator object has a class-wide type, and is therefore indefinite,
which means it requires initialization during its elaboration.  But this
is only possible because all types in the iterator class are non-limited.
            
The complete text of my example is at the Patterns archive at the ACM,
under the Nov 1998 link.  The subject is "Iterator and Factory Method
Patterns Combined."

<http://www.acm.org/archives/patterns.html>

From the text of the example:

"The declaration of a class-wide object requires that the object be given
an initial value, because a class-wide type is indefinite.  (In our case,
the initial value is supplied by the factory method constructor.)  This
means we need assignment for the type, and therefore the iterator must
be declared as non-limited."


2) Put the object on the heap.  

Your type will probably have to have some sort of dispatching clone method:

generic
  ...
package Stacks is

   type Stack_Type is tagged limited private;

   type Stack_Access is access all Stack_Type'Class;

   function Copy (Stack : Stack_Type) return Stack_Access;
...
end Stacks;

which you'd use something like:

procedure Op (Stack : in Stack_Type'Class) is

  Copy_Of_Stack : Stack_Access := Copy (Stack);

begin


This is ugly, because you have to use heap to make the copy.  If you
really need to copy class-wide objects, then you should try to make the
type non-limited, as in 1) above.

This is not an Ada95 problem.  It is an Ada problem.  The problem is
that Ada doesn't separate the notions of initialization and assignment.
Functions that return a value of the type are used as constructors,
instead of having a dedicated constructor operation.

Normally, this isn't a problem, but it is when you want to copy a
limited (and indefinite) type.

But... if you're trying to copy a type that's limited, then what's the
point of making it limited?  Making something limited means you don't
want assignment.

I've advocated adding true constructors to the language, something like:

  type Stack_Type is tagged limited private;

  constructor Copy (Stack : Stack_Type) return Stack_Type;

that you'd use like this:

procedure Op (Stack : in Stack_Type'Class) is

   Copy_Of_Stack : Stack_Type'Class'Copy (Stack);
begin

This way, you can initialize a limited and indefinite object during its
elaboration.

Others have suggested a more conservative approach, allowing functions
to be used in the declarative region only:

procedure Op (Stack : in Stack_Type'Class) is

   Copy_Of_Stack : Stack_Type'Class := Copy (Stack);
begin


> An attribute to get the subtype of the actual tagged parameter would
> solve both problems. May be we could combine
> the two attributes 'Class and 'Tag together:
> 
> procedure P4 (S : in Point'Class;
>               N : in Natural) is
> 
>    Local : Point'Class (S'Tag); -- allocates an object with the tag of S
> 
>    subtype T_Local_Point is Point'Class (S'Tag);
> 
>    Local_Array : array (1 .. N) of T_Local_Point;
> 
> begin
>    ...
> end P4;
> 
> What do you think ? Am I right or am I missing something ? Could the
> GNAT team provide an implementation defined attribute for this ?

I don't think the GNAT team is in the business of extending or even
fixing the language.  (Although you could argue T'Unrestricted_Access
does just that.)

If you want to initialize an indefinite type (during its elaboration),
then make the type non-limited.

If you want a sequence of indefinite objects, then don't use an array.
Use a more abstract, linear data structure as a queue, in combination
with an iterator that provides sequential (or even indexed) access to
the items.





^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-07  0:00 class-wide objects Francois Godme
@ 1998-12-07  0:00 ` Tucker Taft
  1998-12-07  0:00   ` Francois Godme
  1998-12-07  0:00 ` Matthew Heaney
  1998-12-07  0:00 ` Tom Moran
  2 siblings, 1 reply; 11+ messages in thread
From: Tucker Taft @ 1998-12-07  0:00 UTC (permalink / raw)


Francois Godme (fgodme@magic.fr) wrote:

: ...
: An attribute to get the subtype of the actual tagged parameter would
: solve both problems. May be we could combine
: the two attributes 'Class and 'Tag together:

: procedure P4 (S : in Point'Class;
:               N : in Natural) is

:    Local : Point'Class (S'Tag); -- allocates an object with the tag of S

:    subtype T_Local_Point is Point'Class (S'Tag);

:    Local_Array : array (1 .. N) of T_Local_Point;

: begin
:    ...
: end P4;

: What do you think ? Am I right or am I missing something ? Could the
: GNAT team provide an implementation defined attribute for this ?

Just providing the tag is not enough, because the type might
have discriminants as well, which must be specified at the
point of declaration.  One could imagine some new attribute
like "'Subtype" and then write:

    Local : Point'Class(S'Subtype);

In fact, something like this was proposed during the Ada 9X
revision process, but it didn't make the "cut" among the
various language revision proposals.

: Francois Godme
--
-Tucker Taft   stt@inmet.com   http://www.inmet.com/~stt/
Intermetrics, Inc.  Burlington, MA  USA
An AverStar Company




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-07  0:00 ` Tucker Taft
@ 1998-12-07  0:00   ` Francois Godme
  0 siblings, 0 replies; 11+ messages in thread
From: Francois Godme @ 1998-12-07  0:00 UTC (permalink / raw)


Tucker Taft wrote:

>
> Just providing the tag is not enough, because the type might
> have discriminants as well, which must be specified at the
> point of declaration.  One could imagine some new attribute
> like "'Subtype" and then write:
>
>     Local : Point'Class(S'Subtype);
>
>

How hard will it be to implement such attribute ? Are the required elements
not already all in place but not connected together? May be all compilers are
just a step away of providing this attribute.






^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-07  0:00 ` Matthew Heaney
  1998-12-06  0:00   ` David Botton
@ 1998-12-09  0:00   ` Francois Godme
  1998-12-10  0:00     ` Matthew Heaney
  1 sibling, 1 reply; 11+ messages in thread
From: Francois Godme @ 1998-12-09  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> Your type will probably have to have some sort of dispatching clone method:
>
> generic
>   ...
> package Stacks is
>
>    type Stack_Type is tagged limited private;
>
>    type Stack_Access is access all Stack_Type'Class;
>
>    function Copy (Stack : Stack_Type) return Stack_Access;
> ...
> end Stacks;
>
> which you'd use something like:
>
> procedure Op (Stack : in Stack_Type'Class) is
>
>   Copy_Of_Stack : Stack_Access := Copy (Stack);
>
> begin

The clone method is inventive but it is a very odd method in the sense
that when you inherit it, it is always wrong for you. So, you have to
redefine it.

Usually, when you redefine a method, you call the inherited method on
the superclass part of you and finish the job yourself for the added
part. Here, the inherited method is not reusable because it clones
objects of your superclass not of yourself. You are the only one able
to clone yourself as your father knows nothing of you.

You are not forced to override this method by the language like the
language would do for an abstract method, so you can forget to do
it. You are on your own.

It's like a genetic disease that affects all generation. It can be
cured easily in one generation but the next generation will be
affected the same way. The disease cannot be stopped. It reappears
from generation to generation. Some generation are sane, some are
are sterile (they cannot reproduce themselves, they reproduce as their
parents.)

The cure is always the same. It can be as simple as an Emacs Macro.

   function Copy (Stack : Stack_Type) return Stack_Access is
      use type Ada.Tags.Tag;
   begin
      -- tries to be kind with sick children
      if Stack_Type'Class (Stack)'Tag /= Stack_Type'Tag then
         raise ...; -- Is that kindness ? I think it's better than return
null.
                    -- The family takes on responsabilities for its sick
                    -- children.

      else
  return new Stack_Type;
      end if;
   end Copy;

The attribute I was asking for does almost the same thing as the clone method
but without the associated problems.






^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-10  0:00     ` Matthew Heaney
@ 1998-12-10  0:00       ` Francois Godme
  1998-12-11  0:00       ` Stephen Leake
  1 sibling, 0 replies; 11+ messages in thread
From: Francois Godme @ 1998-12-10  0:00 UTC (permalink / raw)


Matthew Heaney wrote:

>
> Then just make it non-primitive:
>

But then in this case, you cannot write anymore the following code:

 >
> > procedure Op (Stack : in Stack_Type'Class) is
> >
> >   Copy_Of_Stack : Stack_Access := Copy (Stack);
> >
> > begin
>





^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-09  0:00   ` Francois Godme
@ 1998-12-10  0:00     ` Matthew Heaney
  1998-12-10  0:00       ` Francois Godme
  1998-12-11  0:00       ` Stephen Leake
  0 siblings, 2 replies; 11+ messages in thread
From: Matthew Heaney @ 1998-12-10  0:00 UTC (permalink / raw)


Francois Godme <fgodme@magic.fr> writes:

> Matthew Heaney <matthew_heaney@acm.org> writes:
> 
> > Your type will probably have to have some sort of dispatching clone method:
> >
> > generic
> >   ...
> > package Stacks is
> >
> >    type Stack_Type is tagged limited private;
> >
> >    type Stack_Access is access all Stack_Type'Class;
> >
> >    function Copy (Stack : Stack_Type) return Stack_Access;
> > ...
> > end Stacks;
> >
> > which you'd use something like:
> >
> > procedure Op (Stack : in Stack_Type'Class) is
> >
> >   Copy_Of_Stack : Stack_Access := Copy (Stack);
> >
> > begin
> 
> The clone method is inventive but it is a very odd method in the sense
> that when you inherit it, it is always wrong for you. So, you have to
> redefine it.

Then just make it non-primitive:

generic
   ...
package Stacks is

   type Stack_Type is ...
...
   package Constructors is

      function  Copy (Stack : Stack_Type) return Stack_Access;

   end;
...
end Stacks;


Because Copy is non-primitive, it won't get inherited during derivation.

 
> Usually, when you redefine a method, you call the inherited method on
> the superclass part of you and finish the job yourself for the added
> part. Here, the inherited method is not reusable because it clones
> objects of your superclass not of yourself. You are the only one able
> to clone yourself as your father knows nothing of you.

In general, constructors shouldn't be primitive.

> You are not forced to override this method by the language like the
> language would do for an abstract method, so you can forget to do
> it. You are on your own.

Unless you make the operation non-primitive.  
 
> It's like a genetic disease that affects all generation. It can be
> cured easily in one generation but the next generation will be
> affected the same way. The disease cannot be stopped. It reappears
> from generation to generation. Some generation are sane, some are
> are sterile (they cannot reproduce themselves, they reproduce as their
> parents.)

There is no problem, if the operation isn't primitive.


> The cure is always the same. It can be as simple as an Emacs Macro.
> 
>    function Copy (Stack : Stack_Type) return Stack_Access is
>       use type Ada.Tags.Tag;
>    begin
>       -- tries to be kind with sick children
>       if Stack_Type'Class (Stack)'Tag /= Stack_Type'Tag then
>          raise ...; -- Is that kindness ? I think it's better than return
> null.
>                     -- The family takes on responsabilities for its sick
>                     -- children.
> 
>       else
>   return new Stack_Type;
>       end if;
>    end Copy;
> 
> The attribute I was asking for does almost the same thing as the clone method
> but without the associated problems.


There are no problems with the approach I suggest above.




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-10  0:00     ` Matthew Heaney
  1998-12-10  0:00       ` Francois Godme
@ 1998-12-11  0:00       ` Stephen Leake
  1998-12-12  0:00         ` Francois Godme
  1 sibling, 1 reply; 11+ messages in thread
From: Stephen Leake @ 1998-12-11  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> Then just make it non-primitive:
> 
> generic
>    ...
> package Stacks is
> 
>    type Stack_Type is ...
> ...
>    package Constructors is
> 
>       function  Copy (Stack : Stack_Type) return Stack_Access;
> 
>    end;
> ...
> end Stacks;
> 
> 
> Because Copy is non-primitive, it won't get inherited during derivation.
> 

Another way to make an operation non-primitive is to use 'class:

function Copy (Stack : Stack_Type'class) return Stack_Access;

I've been flip-flopping about which way is "best". Using 'class
sometimes implies re-dispatching in the body, but not always. If the
non-primitive operations belong in a group (like Constructors), then
the local package seems neat. But for other operations, a local
package is just noise, and 'class seems preferable.

-- Stephe




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: class-wide objects
  1998-12-11  0:00       ` Stephen Leake
@ 1998-12-12  0:00         ` Francois Godme
  0 siblings, 0 replies; 11+ messages in thread
From: Francois Godme @ 1998-12-12  0:00 UTC (permalink / raw)


Stephen Leake wrote:

> Another way to make an operation non-primitive is to use 'class:
>
> function Copy (Stack : Stack_Type'class) return Stack_Access;
>
> I've been flip-flopping about which way is "best". Using 'class
> sometimes implies re-dispatching in the body, but not always. If the
> non-primitive operations belong in a group (like Constructors), then
> the local package seems neat. But for other operations, a local
> package is just noise, and 'class seems preferable.
>
> -- Stephe

I agree entirely. I do the same as you do.





^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~1998-12-12  0:00 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-12-07  0:00 class-wide objects Francois Godme
1998-12-07  0:00 ` Tucker Taft
1998-12-07  0:00   ` Francois Godme
1998-12-07  0:00 ` Matthew Heaney
1998-12-06  0:00   ` David Botton
1998-12-09  0:00   ` Francois Godme
1998-12-10  0:00     ` Matthew Heaney
1998-12-10  0:00       ` Francois Godme
1998-12-11  0:00       ` Stephen Leake
1998-12-12  0:00         ` Francois Godme
1998-12-07  0:00 ` Tom Moran

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox