comp.lang.ada
 help / color / mirror / Atom feed
* Destructor question
@ 1998-12-02  0:00 Rusnak
  1998-12-03  0:00 ` Jeff Carter
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Rusnak @ 1998-12-02  0:00 UTC (permalink / raw)


How are destrcutor methords properly implemented in Ada on tagged
records?  I have the following setup:

    Given a tagged record type:

       type Instance is abstract tagged
           record
              ...
          end record;

      type Object is access all Instance;

      type Class_Object is access all Instance'Class;

  and a package which keeps a container of "Class_Object"s.

   At the end of the day, I need to deallocate all the objects in the
container.
    The only way I could envision doing this is to write a Dispose
procedure which takes an access to an instance and dispatches to the
    approriate package (the one where the corresponding subclass of
Instance is actually defined) to do the deallocation.
    The Dispose procedure would dispose of the internals of the object
(if necessary) and then dispose of the object itself.  The procedure
    therefore needs to take an "in out Object" type:

    procedure Dispose(The_Object: in out Object) is abstract;

    I can't dispatch on this procedure, however, since it explicitly
takes an Object type and passing in a Class_Object type causes a
compilation
    error.  I can'e use an anonymous access type, since these are only
"in" parameters, and the access value of The_Object should be set to
null
    after deallocation.


Any suggestions are greatly appreciated.

My e-mail id is "jrusnak "on the domain "netgate.net".

Thanks
-John







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

* Re: Destructor question
  1998-12-02  0:00 Destructor question Rusnak
@ 1998-12-03  0:00 ` Jeff Carter
  1998-12-03  0:00   ` Rusnak
  1998-12-06  0:00 ` david.c.hoos.sr
  1998-12-06  0:00 ` Matthew Heaney
  2 siblings, 1 reply; 12+ messages in thread
From: Jeff Carter @ 1998-12-03  0:00 UTC (permalink / raw)


Rusnak wrote:
> 
> How are destrcutor methords properly implemented in Ada on tagged
> records?  I have the following setup:
> ...

Assuming

"destrcutor" should be "destructor"

and

"methords"   should be "methods"

then the answer to this question is that Ada does not have methods, much
less destructor methods. Ada has subprograms and entries.

This is a MicroStuff Support answer: technically correct but useless.
Here's a useful answer:

Your question seems to be about what Ada calls "finalization", which
refers to operations on objects of a type when the value of the object
becomes inaccessible (prior to assignment or when the object ceases to
exist). I refer you to the standard package Ada.Finalization for the
details of finalization in Ada (ARM 7.6).

-- 
Jeff Carter
E-mail: carter commercial-at innocon [period | full stop] com
"Perfidious English mouse-dropping hoarders."
Monty Python & the Holy Grail




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

* Re: Destructor question
  1998-12-03  0:00 ` Jeff Carter
@ 1998-12-03  0:00   ` Rusnak
  1998-12-04  0:00     ` Robert I. Eachus
                       ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Rusnak @ 1998-12-03  0:00 UTC (permalink / raw)


Jeff Carter wrote:

> Rusnak wrote:
> >
> > How are destrcutor methords properly implemented in Ada on tagged
> > records?  I have the following setup:
> > ...
>
> Assuming
>
> "destrcutor" should be "destructor"
>
> and
>
> "methords"   should be "methods"
>
> then the answer to this question is that Ada does not have methods, much
> less destructor methods. Ada has subprograms and entries.
>
> This is a MicroStuff Support answer: technically correct but useless.
> Here's a useful answer:
>
> Your question seems to be about what Ada calls "finalization", which
> refers to operations on objects of a type when the value of the object
> becomes inaccessible (prior to assignment or when the object ceases to
> exist). I refer you to the standard package Ada.Finalization for the
> details of finalization in Ada (ARM 7.6).
>
> --
> Jeff Carter
> E-mail: carter commercial-at innocon [period | full stop] com
> "Perfidious English mouse-dropping hoarders."
> Monty Python & the Holy Grail

  Sorry about the spelling mistakes on earlier post.  The key terms
"method" and "attribute" are object-oriented terms (which I believe are now
accepted as part of UML but I can be wrong about that) and are not
associated with any particular language.  I would agree that Ada class
objects do not have a "this" pointer, but tagged records do support
dispatching operations which are essentially "methods" of the class.

Now to the more important question:  I assume that IF I am going to use the
Ada.Finalization package, that all my tagged records should be based off of
the Controlled type within that package.  I would may also have to override
the Finalize procedure since a derived class could allocate memorey for new
attributes of the tagged record.  The Finalize, Initialize, and Ajust
procedures do NOT take an access type however.  What I  have is an array of
access values to a "'Class" type, and I need to deallocate the memory
associated with each of the access values.  One cannot write an
Unchecked_Deallocation for a "'Class" object (this makes sense).  What I
need is to write is a dispaching procedure (i.e. a mehotd of a class) like
the following:

    Given a type Instance which is an abstract tagged record and a type
Object which is of type access all Instance and a type Class_Object which
is of type access all Instance'Class,
I would like to define a procedure:

    procedure Deallocate(The_Object : in out Object) is abstract;

which is dispatching.  The compiler certainly lets me declare such a
procedure, but I can never use it, since it cannot accept a  parameter of
type Class_Object to get it to dispatch.  Another solution is possible, but
the above solution (if "implementable") would be the cleanest  and least
intriusive way to go.

On another note,  if i override a proceudre in a derived class, how can I
"chain" it to its super class (i.e.,  call  within the procedure the super
class procedure which it overrides)?  I could certainly see the use in
chaining  the Initialize procedure of the Controlled class mentioned
above.  Explicit casting doesn't seem to work, and casting is not a very
desirable solution anyway.

-John






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

* Re: Destructor question
  1998-12-03  0:00   ` Rusnak
@ 1998-12-04  0:00     ` Robert I. Eachus
  1998-12-06  0:00       ` Matthew Heaney
  1998-12-06  0:00     ` Matthew Heaney
  1998-12-07  0:00     ` Jeff Carter
  2 siblings, 1 reply; 12+ messages in thread
From: Robert I. Eachus @ 1998-12-04  0:00 UTC (permalink / raw)


In article <3666F7F1.9B107D38@nowhere.com> Rusnak <bogus@nowhere.com> writes:

 >     Given a type Instance which is an abstract tagged record and a type
 > Object which is of type access all Instance and a type Class_Object which
 > is of type access all Instance'Class,
 > I would like to define a procedure:

 >     procedure Deallocate(The_Object : in out Object) is abstract;

 > which is dispatching.  The compiler certainly lets me declare such a
 > procedure, but I can never use it, since it cannot accept a  parameter of
 > type Class_Object to get it to dispatch.  Another solution is possible, but
 > the above solution (if "implementable") would be the cleanest  and least
 > intriusive way to go.

     Did you get my e-mail note on this?  Anyway what you have do to
is to have an operation for the "root" type that takes the access type
as a parameter:

    procedure Deallocate(The_Access : in out Object_Class);

    Next, you need, and this need not be visible, to have a two
parameter version that is dispatching on Object:

    procedure Deallocate(The_Access : in out Object_Class;
                         The_Object : in Object);

    The bodies are fairly straightforward:


    procedure Deallocate(The_Access : in out Object_Class) is
    begin Deallocate(The_Access, The_Access.all); end Deallocate;
    -- This dispatches on the second operand.

    procedure Deallocate(The_Access : in out Object_Class;
                         The_Object : in Object) is
      function Free is new Unchecked_Deallocation(Object, Object_Class); 
    begin
      Deallocate(The_Access.all); -- you may not need this if using Controlled
      Free(The_Access);
    end Deallocate;

    -- There is a LOT of magic going on here.  Since this procedure is a
    -- primitive operation of type Object, it will be derived when you
    -- create subclasses of Object.  Note that the parameter
    -- "The_Object" is never used, it is just there to get the
    -- dispatching.  The instance(s) of Unchecked_Deallocation are not
    -- class-wide, so they work.  The call to Deallocate is not
    -- dispatching, but that's all right, the dispatching has already
    -- been done.  The only use of 'Class is in the declaration of
    -- Object_Class, but that is fine, the call to the two parameter
    -- Deallocate from the one parameter version dispatches because
    -- the type of The_Access.all is class-wide.

    -- Finally, you only need to declare the non-class-wide single
    -- parameter version of Deallocate, if you are not using Finalize.

 > On another note,  if i override a proceudre in a derived class, how can I
 > "chain" it to its super class (i.e.,  call  within the procedure the super
 > class procedure which it overrides)?  I could certainly see the use in
 > chaining  the Initialize procedure of the Controlled class mentioned
 > above.  Explicit casting doesn't seem to work, and casting is not a very
 > desirable solution anyway.

   Why doesn't explicit casting seem to work?  Read about "view
conversions" to see that type conversions can do exactly what you
want.  But in general you shouldn't need to do such "casting" to call
the operation on the parent type.  Just make the type extensions
Controlled instead of (or in addition to) the entire class when you
need to do something then call the Adjust, Initialize, or Finalize for
the parent.



--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

* Re: Destructor question
  1998-12-02  0:00 Destructor question Rusnak
  1998-12-03  0:00 ` Jeff Carter
@ 1998-12-06  0:00 ` david.c.hoos.sr
  1998-12-06  0:00   ` Matthew Heaney
  1998-12-08  0:00   ` Robert I. Eachus
  1998-12-06  0:00 ` Matthew Heaney
  2 siblings, 2 replies; 12+ messages in thread
From: david.c.hoos.sr @ 1998-12-06  0:00 UTC (permalink / raw)


In article <3665B85D.A2663591@nowhere.com>,
  Rusnak <bogus@nowhere.com> wrote:
> How are destrcutor methords properly implemented in Ada on tagged
> records?  I have the following setup:
<snip>

Here is my solution to the problem, based on Robert Eachus's post to this
thread.

I do, however, find some points of Robert's posting at variance with what I
was able to make work, viz.:

    1.  Robert said "Next, you need, and this need not be visible, to have a
        two parameter version that is dispatching on Object"  I do not see how
        this can be made invisible, since it needs to be a primitive operation
        of the type.  Perhaps someone can enlightne me on this.

    2.  Robert said "Note that the parameter "The_Object" is never used, it is
        just there to get the dispatching."  This would be true if it were not
        for your requirement that you be able to deallocate internal
        dynamically-allocated components.

At any rate, here is a sample program which is a complete and working
implementation of Robert's ideas.  I hope it helps.  The exercise certainly
clarified the solution for me.

David C. Hoos, Sr.

with Ada.Tags;
with Ada.Text_Io;
with Ada.Unchecked_Deallocation;
procedure Test_Rusnak is

   package Rusnak is

      type Instance is abstract tagged null record;

      type Object_Class is access all Instance'Class;

      -- This procedure can never be called, since no instance of an
      -- abstract type can exist, but it must exist, otherwise the
      -- single-parameter Deallocate will not compile.
      procedure Deallocate
        (The_Access : in out Object_Class;
         The_Object : in out Instance);

      -- This is the class-wide deallocation procedure which is called to
      -- deallocate any objet of a type derived from Instance.  It makes a
      -- dispatching call to the appropriate two-parameter Deallocate
      -- procedure which must be a primitive operation for all types derived
      -- from Instance.
      procedure Deallocate (The_Access : in out Object_Class);

      -- This is the type of a component of type Derived.
      type String_Access is access all String;

      -- This type is an example of a concrete type derived from Instance
      -- which has internal dynamically-allocated objects which need to be
      -- deallocated when an object of the type is deallocated.
      type Derived is new Instance with record
         Data_Access: String_Access;
      end record;

      -- This is the two-parameter Deallocate procedure, a primitive operation
      -- of type Derived.  It must be declared at this point -- i.e. before
      -- Derived is frozen.
      procedure Deallocate
        (The_Access : in out Object_Class;
         The_Object : in out Derived);

   end Rusnak;

   package body Rusnak is

      procedure Deallocate
        (The_Access : in out Object_Class;
         The_Object : in out instance) is
      begin
         null;
      end Deallocate;

      -- This type serves only to provide the required parameter type for an
      -- instantiation of Ada.Unchecked_Deallocation.  It must be decalred
      -- here instead of inside the Deallocate procedure, in order that it
      -- have the same access level as the type which it accesses.
      type Derived_Class is access all Derived;

  -- This procedure is declared outside the Deallocate procedure from which 
-- it is called, only because it is for a type which would likely be used  --
for components of more than one type derived from Instance.  procedure Free
is new Ada.Unchecked_Deallocation  (Object => string,  Name =>
String_access);

      procedure Deallocate
        (The_Access : in out Object_Class;
         The_Object : in out Derived) is
         procedure Free is new Ada.Unchecked_Deallocation
           (Object => derived,
            Name => derived_Class);
      begin
         Ada.Text_Io.Put_Line ("Deallocating ""derived"" object");
         Free (The_Object.Data_Access);
         Free (Derived_Class (The_Access));
      end Deallocate;

      procedure Deallocate (The_Access : in out Object_Class) is
      begin
         Ada.Text_Io.Put_Line
           ("Dispatching to deallocator for concrete type """ &
            Ada.Tags.External_Tag (The_Access.all'Tag) & """.");
         Deallocate (The_Access => The_Access, The_Object => The_Access.all);
      end Deallocate;

   end Rusnak;

   The_Derived_Object_access : Rusnak.Object_class :=
    new Rusnak.Derived'(Data_Access => new String'("this is the data"));

begin

   Ada.Text_Io.Put_Line
     (Rusnak.Derived (The_Derived_Object_Access.all).Data_Access.all);
   Rusnak.Deallocate (The_Derived_Object_Access);
   Ada.Text_Io.Put_Line("The next statement will raise Constraint_Error");
   Ada.Text_Io.Put_Line
     (Rusnak.Derived (The_Derived_Object_Access.all).Data_Access.all);

end Test_Rusnak;



-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    




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

* Re: Destructor question
  1998-12-02  0:00 Destructor question Rusnak
  1998-12-03  0:00 ` Jeff Carter
  1998-12-06  0:00 ` david.c.hoos.sr
@ 1998-12-06  0:00 ` Matthew Heaney
  2 siblings, 0 replies; 12+ messages in thread
From: Matthew Heaney @ 1998-12-06  0:00 UTC (permalink / raw)


Rusnak <bogus@nowhere.com> writes:

> How are destrcutor methords properly implemented in Ada on tagged
> records?  I have the following setup:
> 
>     Given a tagged record type:
> 
>        type Instance is abstract tagged
>            record
>               ...
>           end record;
> 
>       type Object is access all Instance;
> 
>       type Class_Object is access all Instance'Class;
> 
>   and a package which keeps a container of "Class_Object"s.
> 
>    At the end of the day, I need to deallocate all the objects in the
> container.

Hers's an idea:

generic
   type Item_Type is tagged private;
package Containers is

   type Container_Type is limited private;

   procedure Add 
     (Item : in     Item_Type'Class;
      To   : in out Container_Type);
      
...
private
 
  type Item_List is <list of pointers to Item_Type'Class>;

  type Container_Type is
     new Ada.Finalization.Controlled with record
       Items : Item_List;
     end record;

  procedure Finalize (Container : in out Container_Type);

end Containers;

package body Containers is

  procedure Finalize (Container : in out Container_Type) is
  begin
    <remove all items in Container.Items>
  end;

...
end Containers;




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

* Re: Destructor question
  1998-12-03  0:00   ` Rusnak
  1998-12-04  0:00     ` Robert I. Eachus
@ 1998-12-06  0:00     ` Matthew Heaney
  1998-12-07  0:00     ` Jeff Carter
  2 siblings, 0 replies; 12+ messages in thread
From: Matthew Heaney @ 1998-12-06  0:00 UTC (permalink / raw)


I give an example of how to implement a dispatching constructor in my
Nov 1998 post (with the subject "No subject", unfortunately) to the ACM
patterns archive.

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

The post itself is titled Yet Another Visitor, and shows how to
implement a tagged type with a private operation ("method") that
dispatches on the tag of the item, and frees the storage associated with
that object.

The type Composite_Equipment is very much like your container type: it's
a container that contains class-wide objects.

If you have trouble locating the post in the archive, send me your real
email address and I'll send it to you.

Matt



Rusnak <bogus@nowhere.com> writes:

> Now to the more important question:  I assume that IF I am going to use the
> Ada.Finalization package, that all my tagged records should be based off of
> the Controlled type within that package.  I would may also have to override
> the Finalize procedure since a derived class could allocate memorey for new
> attributes of the tagged record.  The Finalize, Initialize, and Ajust
> procedures do NOT take an access type however.  What I  have is an array of
> access values to a "'Class" type, and I need to deallocate the memory
> associated with each of the access values.  One cannot write an
> Unchecked_Deallocation for a "'Class" object (this makes sense).  What I
> need is to write is a dispaching procedure (i.e. a mehotd of a class) like
> the following:
> 
>     Given a type Instance which is an abstract tagged record and a type
> Object which is of type access all Instance and a type Class_Object which
> is of type access all Instance'Class,
> I would like to define a procedure:
> 
>     procedure Deallocate(The_Object : in out Object) is abstract;
> 
> which is dispatching.  The compiler certainly lets me declare such a
> procedure, but I can never use it, since it cannot accept a  parameter of
> type Class_Object to get it to dispatch.  Another solution is possible, but
> the above solution (if "implementable") would be the cleanest  and least
> intriusive way to go.
> 
> On another note,  if i override a proceudre in a derived class, how can I
> "chain" it to its super class (i.e.,  call  within the procedure the super
> class procedure which it overrides)?  I could certainly see the use in
> chaining  the Initialize procedure of the Controlled class mentioned
> above.  Explicit casting doesn't seem to work, and casting is not a very
> desirable solution anyway.
> 
> -John




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

* Re: Destructor question
  1998-12-06  0:00 ` david.c.hoos.sr
@ 1998-12-06  0:00   ` Matthew Heaney
  1998-12-08  0:00   ` Robert I. Eachus
  1 sibling, 0 replies; 12+ messages in thread
From: Matthew Heaney @ 1998-12-06  0:00 UTC (permalink / raw)


david.c.hoos.sr@ada95.com writes:

> Here is my solution to the problem, based on Robert Eachus's post to
> this thread.
> 
> I do, however, find some points of Robert's posting at variance with
> what I was able to make work, viz.:
> 
>     1.  Robert said "Next, you need, and this need not be visible, to
>     have a two parameter version that is dispatching on Object" I do
>     not see how this can be made invisible, since it needs to be a
>     primitive operation of the type.  Perhaps someone can enlightne me
>     on this.

Primitive operations aren't necessarily declared in the public region
of the spec.  For example, in my post about the Visitor pattern, the
free operation is primitive, yet private:

package Equipment is

   pragma Pure;

   type Root_Equipment (<>) is abstract tagged limited private;

private

   type Root_Equipment is
     abstract tagged limited null record;

   procedure Do_Free
     (Equipment : access Root_Equipment);

end Equipment;


Because operation Do_Free is declared in the private region, is only
available within the subsystem of packages rooted at Equipment.

Typically, such operations are named Do_<something>, and are used to
implement public operations (primitive and class-wide).

In the visitor example, Composite_Equipment (an abstract type)
implements Do_Free by calling Do_Free on each of its items, which
dispatches according to the tag of the item.  Thus, Do_Free is a
dispatching deconstructor:


   procedure Do_Free
     (Composite : access Composite_Equipment) is

      procedure Free_Item
        (Item : access Root_Equipment'Class) is
      begin
         Do_Free (Item);  <-- !!! dispatching deconstructor
      end;

      procedure Free_Items is
        new For_Every_Item (Free_Item);

   begin

      Free_Items (Composite);

   end Do_Free;


Cabinet, a non-abstract type which derives from Composite_Equipment,
calls its parent's Do_Free (to actually delete the items), then deletes
itself:

   procedure Free
     (Cabinet : in out Cabinet_Access) is
   begin

      if Cabinet = null then
         return;
      end if;

      Do_Free (Composite_Equipment (Cabinet.all)'Access);

      Deallocate (Cabinet);

   end Free;


Free is the public operation that does deletion.  It is implemented by
calling Do_Free.

Note that this technique is slightly different from the solution
suggested by Bob Eachus.

The complete post (with compilable code) is located in the Nov 1998 link
at the ACM patterns list archive.

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





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

* Re: Destructor question
  1998-12-04  0:00     ` Robert I. Eachus
@ 1998-12-06  0:00       ` Matthew Heaney
  1998-12-08  0:00         ` Robert I. Eachus
  0 siblings, 1 reply; 12+ messages in thread
From: Matthew Heaney @ 1998-12-06  0:00 UTC (permalink / raw)


eachus@spectre.mitre.org (Robert I. Eachus) writes:

>      Did you get my e-mail note on this?  Anyway what you have do to
> is to have an operation for the "root" type that takes the access type
> as a parameter:
> 
>     procedure Deallocate(The_Access : in out Object_Class);
> 
>     Next, you need, and this need not be visible, to have a two
> parameter version that is dispatching on Object:
> 
>     procedure Deallocate(The_Access : in out Object_Class;
>                          The_Object : in Object);


An alternative implementation uses a (private) subprogram that takes an
access parameter:

   procedure Do_Deallocate (The_Object : access Object);

This of course dispatches on the tag of The_Object.  It more-or-less
combines the two parameters of Bob's solution into one parameter.

> 
>     The bodies are fairly straightforward:
> 
> 
>     procedure Deallocate(The_Access : in out Object_Class) is
>     begin Deallocate(The_Access, The_Access.all); end Deallocate;
>     -- This dispatches on the second operand.
>
>     procedure Deallocate(The_Access : in out Object_Class;
>                          The_Object : in Object) is
>       function Free is new Unchecked_Deallocation(Object, Object_Class); 
>     begin
>       Deallocate(The_Access.all); -- may not need this if using Controlled
>       Free(The_Access);
>     end Deallocate;


The bodies of the alternative implementation are:

   procedure Deallocate (The_Access : in out Object_Class) is
   begin
      if The_Access /= null then
        Do_Deallocate (The_Access);
        The_Access := null;
      end if;
   end Deallocate;


   procedure Do_Deallocate (The_Object : access Object) is

      OA : Object_Access := Object_Access (The_Object);
   begin
      Free (OA);
   end;

where type Object_Access is an access type that designates the specific
type Object.  Analogous implementations would be required for each type
in the hierarchy.  (I prefer to instantiate Unchecked_Deallocation using
a specific type, rather than a class-wide one.)

A complete example using this technique may be found at the patterns
archive at the ACM:

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

Look under month Nov 1998, and the post labeled "No subject."  It
discusses an implementation of the Visitor pattern that uses the memory
management technique described above.

From the original post (not from Bob Eachus):

>  > On another note,  if i override a proceudre in a derived class, how can I
>  > "chain" it to its super class (i.e.,  call  within the procedure the super
>  > class procedure which it overrides)?  I could certainly see the use in
>  > chaining  the Initialize procedure of the Controlled class mentioned
>  > above.  Explicit casting doesn't seem to work, and casting is not a very
>  > desirable solution anyway.

Sometimes you need to cast.  Downcasts from a class-wide type to a
specific one are safe.






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

* Re: Destructor question
  1998-12-03  0:00   ` Rusnak
  1998-12-04  0:00     ` Robert I. Eachus
  1998-12-06  0:00     ` Matthew Heaney
@ 1998-12-07  0:00     ` Jeff Carter
  2 siblings, 0 replies; 12+ messages in thread
From: Jeff Carter @ 1998-12-07  0:00 UTC (permalink / raw)


Rusnak wrote:
> ...
>   Sorry about the spelling mistakes on earlier post.  The key terms
> "method" and "attribute" are object-oriented terms (which I believe are now
> accepted as part of UML but I can be wrong about that) and are not
> associated with any particular language.  I would agree that Ada class
> objects do not have a "this" pointer, but tagged records do support
> dispatching operations which are essentially "methods" of the class.

We all make typos, but when we make assumptions about another's typos,
we should make them clear, in case those assumptions are wrong.

There are many who feel a need for new names for concepts that have been
well defined for 30 years under other names, but Ada (ISO/IEC 8652:1995)
is not among them, nor am I. I call a type a type and a subprogram a
subprogram.

More to the point, when discussing Ada, it is helpful to use Ada terms.

> 
> Now to the more important question:  I assume that IF I am going to use the
> Ada.Finalization package, that all my tagged records should be based off of
> the Controlled type within that package.  I would may also have to override
> the Finalize procedure since a derived class could allocate memorey for new
> attributes of the tagged record.  The Finalize, Initialize, and Ajust
> procedures do NOT take an access type however.  What I  have is an array of
> access values to a "'Class" type, and I need to deallocate the memory
> associated with each of the access values.  One cannot write an
> Unchecked_Deallocation for a "'Class" object (this makes sense).  What I

This compiles fine (GNAT 3.10p1/Win95):

with Ada.Finalization;

use Ada;
package Class_Test is
   type T is abstract tagged private;
   
   type T_Ptr is private;
private -- Class_Test
   type T is abstract tagged null record;
   
   type T_Access is access all T'Class;
   
   type T_Ptr is new Finalization.Controlled with record
      Ptr : T_Access;
   end record;
   
   procedure Finalize (Object : in out T_Ptr);
end Class_Test;

with Ada.Unchecked_Deallocation;

use Ada;
package body Class_Test is
   procedure Finalize (Object : in out T_Ptr) is
      procedure Free is new Unchecked_Deallocation
         (Object => T'Class, Name => T_Access);
   begin -- Finalize
      Free (Object.Ptr);
   end Finalize;
end Class_Test;

so possibly you can do what you want directly.

You would need some operations to obtain and manipulate values of type
T_Ptr (these operations might dispatch). You could then declare and
operate on an array of T_Ptr components; when an array object goes out
of scope, its components would be deallocated by finalization.

Note that Ada's design goals include supporting the creation of
readable, easily understood code. The use of type extension and
dispatching results in code that is less readable and more difficult to
understand than code with the same functionality implemented using
composition. Since anything implemented with type extension and
dispatching can be implemented using composition, the only reason to use
type extension and dispatching is to obtain finalization.

-- 
Jeff Carter
E-mail: carter commercial-at innocon [period | full stop] com
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail




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

* Re: Destructor question
  1998-12-06  0:00       ` Matthew Heaney
@ 1998-12-08  0:00         ` Robert I. Eachus
  0 siblings, 0 replies; 12+ messages in thread
From: Robert I. Eachus @ 1998-12-08  0:00 UTC (permalink / raw)


In article <m3k90559po.fsf@mheaney.ni.net> Matthew Heaney <matthew_heaney@acm.org> writes:

  > An alternative implementation uses a (private) subprogram that takes an
  > access parameter:

  >    procedure Do_Deallocate (The_Object : access Object);

  > This of course dispatches on the tag of The_Object.  It more-or-less
  > combines the two parameters of Bob's solution into one parameter.

    This is essentially a style issue, but for didactic purposes, I
wanted to avoid the clash between access parameters and
Unchecked_Deallocation.  Matt's example deals with this correctly, but
it is probably more difficult for someone learning Ada to understand.


  > I prefer to instantiate Unchecked_Deallocation using a specific
  > type, rather than a class-wide one.

    I understand, I think, what Matt meant to say here from context,
but it is tough to put in correct words.  "I prefer to instantiate
Unchecked_Deallocation where neither the object type nor the
designated type in the formal access parameter is classwide," may be
more correct, but it is certainly much harder on the eyes. The
difference between Matt's implementation and mine here is in the type
designated by the access type, and I just chose to use the one in the
original example.  However, I strongly agree that you do not want to
instantiate Unchecked_Deallocation with a classwide object type.
Language lawyers can argue about when this is required to work by the
RM but allowed just to not collect the storage (or just do a lot of
otherwise unnecessary checking) and when it is allowed to erroneusly
collect the wrong amount of storage, but you are much better off not
putting the gun to the implementors head.

   (The problem is that if you instantiate using a classwide type, the
compiler won't know when the sizes of the objects of a given subtype
are static, and when thunks or some other dynamic method is necessary
to determine the amount of space to free.  So a "correct"
implementation will potentially require creating thunks for all
possible target subtypes.  If you instantiate for a specific type,
even if it is inside a dispatching operation, then the compiler can
generate decent code for each instance.  Of course, the implementation
of the standard storage pool may include a size in each object, but
that doesn't help with user defined pools.)

--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

* Re: Destructor question
  1998-12-06  0:00 ` david.c.hoos.sr
  1998-12-06  0:00   ` Matthew Heaney
@ 1998-12-08  0:00   ` Robert I. Eachus
  1 sibling, 0 replies; 12+ messages in thread
From: Robert I. Eachus @ 1998-12-08  0:00 UTC (permalink / raw)


In article <74d0i5$itj$1@nnrp1.dejanews.com> david.c.hoos.sr@ada95.com writes:

  > 1.  Robert said "Next, you need, and this need not be visible, to have a
  >     two parameter version that is dispatching on Object"  I do not see how
  >     this can be made invisible, since it needs to be a primitive operation
  >      of the type.  Perhaps someone can enlightne me on this.

   Matt Heane answered this--dispatching operations can be declared
in the private part if they are not part of the exported interface.
But they still have to be declared before the type is frozen (see
3.9.2(13)), and there are certain other limitations. (See 3.9.3(10).)

  > 2.  Robert said "Note that the parameter "The_Object" is never used, it is
  >     just there to get the dispatching."  This would be true if it were not
  >     for your requirement that you be able to deallocate internal
  >     dynamically-allocated components.

   It can be used, but I recommend against it.  In my example, I only
used the access parameter, but if you need to do anything to the
object, I strongly recommend using ".all" applied to the access
parameter.  If someone writes a call where the object type is wrong,
you will get an error at run-time.  But if the object is of the
correct type but not the target of the access parameter, you will have
a very hard to find bug.  It is possible to put an explict check in,
but it is much easier just to avoid using the value of the parameter,
or as Matt suggested, use a single access parameter.  (But then you
have to make a copy to pass to Unchecked_Deallocation, then explicitly
set the outer parameter to null.)
--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

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

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-12-02  0:00 Destructor question Rusnak
1998-12-03  0:00 ` Jeff Carter
1998-12-03  0:00   ` Rusnak
1998-12-04  0:00     ` Robert I. Eachus
1998-12-06  0:00       ` Matthew Heaney
1998-12-08  0:00         ` Robert I. Eachus
1998-12-06  0:00     ` Matthew Heaney
1998-12-07  0:00     ` Jeff Carter
1998-12-06  0:00 ` david.c.hoos.sr
1998-12-06  0:00   ` Matthew Heaney
1998-12-08  0:00   ` Robert I. Eachus
1998-12-06  0:00 ` Matthew Heaney

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