comp.lang.ada
 help / color / mirror / Atom feed
* Deallocating list of polymorphic objects?
@ 2006-11-30 23:40 Michael Rohan
  2006-12-01  0:05 ` Robert A Duff
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Michael Rohan @ 2006-11-30 23:40 UTC (permalink / raw)


Hi Folks,

I would like to construct a list of polymorphic objects that,
as part of the list's finalization, deallocates the objects on
the list.  Basically, I have a vector of pointers to Object'Class.
The objects are added to the list via procedures defined for
the list, e.g., append an integer, append a floating point.
These append procedures allocate objects derived from the
base Object type for the type being appended, e.g.,
Integer_Object, which is private to the list package.

Since I want the deallocation to be dispatching, it needs to take an
access parameter which is then converted to a pointer for the object
being deallocated, e.g., an Integer_Pointer, and then passed to an
Unchecked_Deallocation procedure.

The code below (formatted to minimize the size of this posting) does
what I expect:

   $ gnatmake -gnat05 list_test
   gcc -c -gnat05 list_test.adb
   gcc -c -gnat05 lists.adb
   gnatbind -x list_test.ali
   gnatlink list_test.ali
   $ ./list_test
   Finalizing a list
   Integer object:  1
   Float object: -1.00000E+00
   Integer object:  2
   Float object: -2.00000E+00

However, I have a feeling there is something "bad" about this type of
deallocation, probably related to storage pool but I'm not familiar
enough with storage pools to be sure.

Would anyone care to comment on how safe/unsafe this deallocation
scheme is?

Take care,
Michael

---
list_test.adb
---------------------------------------------------------------------------
with Lists;
procedure List_Test is
   L : Lists.List_Type;
begin
   Lists.Append (L, 1);
   Lists.Append (L, -1.0);
   Lists.Append (L, 2);
   Lists.Append (L, -2.0);
end List_Test;

---
lists.ads
---------------------------------------------------------------------------
with Ada.Finalization;
with Ada.Containers.Vectors;
package Lists is
   type List_Type is limited private;
   procedure Append (L : in out List_Type; I : Integer);
   procedure Append (L : in out List_Type; F : Float);
private
   type Object is abstract tagged null record;
   type Object_Pointer is access all Object'Class;
   procedure Print (Pointer : access Object) is abstract;
   procedure Deallocate (Pointer : access Object) is abstract;
   package List_Vector_Package is
      new Ada.Containers.Vectors (Index_Type => Natural,
                                  Element_Type => Object_Pointer);
   type List_Type is new Ada.Finalization.Limited_Controlled with
record
      Contents : List_Vector_Package.Vector;
   end record;
   overriding procedure Finalize (L : in out List_Type);
end Lists;

---
lists.adb
---------------------------------------------------------------------------
with Ada.Text_IO;
with Ada.Unchecked_Deallocation;
package body Lists is
   use Ada.Containers;
   use List_Vector_Package;

   package Integer_Objects is
      type Integer_Object is new Object with record
         I : Integer;
      end record;
      overriding procedure Print (Pointer : access Integer_Object);
      overriding procedure Deallocate (Pointer : access
Integer_Object);
   end Integer_Objects;

   package Float_Objects is
      type Float_Object is new Object with record
         F : Float;
      end record;
      overriding procedure Print (Pointer : access Float_Object);
      overriding procedure Deallocate (Pointer : access Float_Object);
   end Float_Objects;

   use Float_Objects;
   use Integer_Objects;

   package body Integer_Objects is
      type Integer_Pointer is access all Integer_Object;
      procedure Free is
         new Ada.Unchecked_Deallocation (Integer_Object,
Integer_Pointer);
      procedure Print (Pointer : access Integer_Object) is
      begin
         Ada.Text_IO.Put_Line ("Integer object: " & Pointer.I'Img);
      end Print;
      procedure Deallocate (Pointer : access Integer_Object) is
         I_Pointer : Integer_Pointer := Integer_Pointer (Pointer);
      begin
         Print (Pointer);
         Free (I_Pointer);
      end Deallocate;
   end Integer_Objects;

   package body Float_Objects is
      type Float_Pointer is access all Float_Object;
      procedure Free is
         new Ada.Unchecked_Deallocation (Float_Object, Float_Pointer);
      procedure Print (Pointer : access Float_Object) is
      begin
         Ada.Text_IO.Put_Line ("Float object: " & Pointer.F'Img);
      end Print;
      procedure Deallocate (Pointer : access Float_Object) is
         I_Pointer : Float_Pointer := Float_Pointer (Pointer);
      begin
         Print (Pointer);
         Free (I_Pointer);
      end Deallocate;
   end Float_Objects;

   procedure Append (L : in out List_Type; I : Integer) is
   begin
      Append (L.Contents, new Integer_Object'(I => I));
   end Append;
   procedure Append (L : in out List_Type; F : Float) is
   begin
      Append (L.Contents, new Float_Object'(F => F));
   end Append;
   procedure Finalize (L : in out List_Type) is
      Pointer : Object_Pointer;
   begin
      Ada.Text_IO.Put_Line ("Finalizing a list");
      if L.Contents.Length > 0 then
         for I in 0 .. Integer (L.Contents.Length - 1) loop
            Pointer := Element (L.Contents, I);
            Deallocate (Pointer);
         end loop;
      end if;
   end Finalize;
end Lists;




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

* Re: Deallocating list of polymorphic objects?
  2006-11-30 23:40 Deallocating list of polymorphic objects? Michael Rohan
@ 2006-12-01  0:05 ` Robert A Duff
  2006-12-01  6:41   ` Simon Wright
  2006-12-01  1:24 ` Randy Brukardt
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Robert A Duff @ 2006-12-01  0:05 UTC (permalink / raw)


"Michael Rohan" <mrohan@ACM.ORG> writes:

> Since I want the deallocation to be dispatching, it needs to take an
> access parameter which is then converted to a pointer for the object
> being deallocated, e.g., an Integer_Pointer, and then passed to an
> Unchecked_Deallocation procedure.

You don't need to do all that by hand.  It's OK to pass
access-to-classwide to Unchecked_Deallocation.  It will do
the necessary dispatching internally.

But, to be safe, you should ensure that the result type of each "new" is
the same as the type passed to Unchecked_Deallocation.

- Bob



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

* Re: Deallocating list of polymorphic objects?
  2006-11-30 23:40 Deallocating list of polymorphic objects? Michael Rohan
  2006-12-01  0:05 ` Robert A Duff
@ 2006-12-01  1:24 ` Randy Brukardt
  2006-12-01  8:57   ` Maciej Sobczak
  2006-12-01  3:52 ` Matthew Heaney
  2006-12-01  4:11 ` Matthew Heaney
  3 siblings, 1 reply; 13+ messages in thread
From: Randy Brukardt @ 2006-12-01  1:24 UTC (permalink / raw)


"Michael Rohan" <mrohan@ACM.ORG> wrote in message
news:1164930027.758923.119740@h54g2000cwb.googlegroups.com...
...
> I would like to construct a list of polymorphic objects that,
> as part of the list's finalization, deallocates the objects on
> the list.  Basically, I have a vector of pointers to Object'Class.
> The objects are added to the list via procedures defined for
> the list, e.g., append an integer, append a floating point.
> These append procedures allocate objects derived from the
> base Object type for the type being appended, e.g.,
> Integer_Object, which is private to the list package.
>
> Since I want the deallocation to be dispatching, it needs to take an
> access parameter which is then converted to a pointer for the object
> being deallocated, e.g., an Integer_Pointer, and then passed to an
> Unchecked_Deallocation procedure.

To deallocate the elements, then just doing it should work fine:

   procedure Free is new Ada.Unchecked_Deallocation (Object'Class,
Object_Pointer);

Unchecked_Deallocation of the Objects will call Finalize on them, so that
any internal cleanup can be done. (That's presuming that Object is also
derived from Controlled, but IMHO that should be true of virtually all
complex types in new Ada code. Remember that Object can still be abstract
even if derived.) You could also uses a separate "Ready-me-for-Deallocation"
dispatching routine, but that is neither as safe nor fool-proof as just
letting Ada do it: there are special rules in the language that insure that
Finalize is always called at least once.

The important thing here is that (using the terminology of the Ada 2007
predefined containers) the container is responsible for deallocating the
elements as a whole, but any internal cleanup is the responsibility of the
elements themselves. It's not possible (in general) to have objects that
deallocate themselves -- but that's actually a good thing: an object should
be responsible for cleaning its contents up, but only the client can know
how that object is going to be used, and thus how the memory should be
deallocated. Otherwise you have unnecessary coupling between the object and
its clients: the object type cannot be reliably used to declare objects on
the stack (or in the predefined containers, or anywhere that non-standard
storage pools are used, etc.).

Summary: The objects and the list are separate abstractions and should be
kept separate. The list should allocate and deallocation elements (objects);
the objects themselves should do any internal cleanup needed.

                               Randy Brukardt.





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

* Re: Deallocating list of polymorphic objects?
  2006-11-30 23:40 Deallocating list of polymorphic objects? Michael Rohan
  2006-12-01  0:05 ` Robert A Duff
  2006-12-01  1:24 ` Randy Brukardt
@ 2006-12-01  3:52 ` Matthew Heaney
  2006-12-01  4:11 ` Matthew Heaney
  3 siblings, 0 replies; 13+ messages in thread
From: Matthew Heaney @ 2006-12-01  3:52 UTC (permalink / raw)


"Michael Rohan" <mrohan@ACM.ORG> writes:

> I would like to construct a list of polymorphic objects that,
> as part of the list's finalization, deallocates the objects on
> the list.  Basically, I have a vector of pointers to Object'Class.

The easiest way to do that is using the indefinite form:

with Ada.Containers.Indefinite_Vectors;

package Object_Vectors is
  new Ada.Containers.Indefinite_Vectors (Object'Class);

As others have pointed out, the Ada run-time properly handles deallocation of
objects having a class-wide type, so there's nothing special you need to do.



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

* Re: Deallocating list of polymorphic objects?
  2006-11-30 23:40 Deallocating list of polymorphic objects? Michael Rohan
                   ` (2 preceding siblings ...)
  2006-12-01  3:52 ` Matthew Heaney
@ 2006-12-01  4:11 ` Matthew Heaney
  2006-12-01  6:12   ` Michael Rohan
  3 siblings, 1 reply; 13+ messages in thread
From: Matthew Heaney @ 2006-12-01  4:11 UTC (permalink / raw)


"Michael Rohan" <mrohan@ACM.ORG> writes:

> I would like to construct a list of polymorphic objects that,
> as part of the list's finalization, deallocates the objects on
> the list.  Basically, I have a vector of pointers to Object'Class.
> The objects are added to the list via procedures defined for
> the list, e.g., append an integer, append a floating point.
> These append procedures allocate objects derived from the
> base Object type for the type being appended, e.g.,
> Integer_Object, which is private to the list package.


Here's one way to do it:

--STX
with Ada.Containers.Indefinite_Vectors;
pragma Elaborate_All (Ada.Containers.Indefinite_Vectors);

package Lists is

   type List_Type is tagged limited private;

   procedure Append (L : in out List_Type; I : Integer);
   procedure Append (L : in out List_Type; F : Float);

private

   type Object is interface;
   procedure Print (O : Object) is abstract;

   package List_Vectors is
      new Ada.Containers.Indefinite_Vectors (Natural, Object'Class);

   type List_Type is tagged limited record
      V : List_Vectors.Vector;
   end record;


   type Integer_Object is new Object with record
      I : Integer;
   end record;

   procedure Print (O : Integer_Object);

   type Float_Object is new Object with record
      F : Float;
   end record;

   procedure Print (O : Float_Object);

end Lists;


package body Lists is

   procedure Append (L : in out List_Type; I : Integer) is
   begin
      L.V.Append (Integer_Object'(I => I));
   end;

   procedure Append (L : in out List_Type; F : Float) is
   begin
      L.V.Append (Float_Object'(F => F));
   end;

   procedure Print (O : Integer_Object) is
   begin
      null;
   end;

   procedure Print (O : Float_Object) is
   begin
      null;
   end;

end Lists;



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

* Re: Deallocating list of polymorphic objects?
  2006-12-01  4:11 ` Matthew Heaney
@ 2006-12-01  6:12   ` Michael Rohan
  2006-12-01 12:40     ` Matthew Heaney
  0 siblings, 1 reply; 13+ messages in thread
From: Michael Rohan @ 2006-12-01  6:12 UTC (permalink / raw)


Matthew Heaney wrote:
> "Michael Rohan" <mrohan@ACM.ORG> writes:
>
> > I would like to construct a list of polymorphic objects that,
> > as part of the list's finalization, deallocates the objects on
> > the list.  Basically, I have a vector of pointers to Object'Class.
> > The objects are added to the list via procedures defined for
> > the list, e.g., append an integer, append a floating point.
> > These append procedures allocate objects derived from the
> > base Object type for the type being appended, e.g.,
> > Integer_Object, which is private to the list package.
>
>
> Here's one way to do it:
>
> --STX
> with Ada.Containers.Indefinite_Vectors;
> pragma Elaborate_All (Ada.Containers.Indefinite_Vectors);
>
> package Lists is
>
>    type List_Type is tagged limited private;
>
>    procedure Append (L : in out List_Type; I : Integer);
>    procedure Append (L : in out List_Type; F : Float);
>
> private
>
>    type Object is interface;
>    procedure Print (O : Object) is abstract;
>
>    package List_Vectors is
>       new Ada.Containers.Indefinite_Vectors (Natural, Object'Class);
>
>    type List_Type is tagged limited record
>       V : List_Vectors.Vector;
>    end record;
>
>
>    type Integer_Object is new Object with record
>       I : Integer;
>    end record;
>
>    procedure Print (O : Integer_Object);
>
>    type Float_Object is new Object with record
>       F : Float;
>    end record;
>
>    procedure Print (O : Float_Object);
>
> end Lists;
>
>
> package body Lists is
>
>    procedure Append (L : in out List_Type; I : Integer) is
>    begin
>       L.V.Append (Integer_Object'(I => I));
>    end;
>
>    procedure Append (L : in out List_Type; F : Float) is
>    begin
>       L.V.Append (Float_Object'(F => F));
>    end;
>
>    procedure Print (O : Integer_Object) is
>    begin
>       null;
>    end;
>
>    procedure Print (O : Float_Object) is
>    begin
>       null;
>    end;
>
> end Lists;

Hi,

This looks really tidy and clean.

Thank you,
Michael.




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

* Re: Deallocating list of polymorphic objects?
  2006-12-01  0:05 ` Robert A Duff
@ 2006-12-01  6:41   ` Simon Wright
  0 siblings, 0 replies; 13+ messages in thread
From: Simon Wright @ 2006-12-01  6:41 UTC (permalink / raw)


Robert A Duff <bobduff@shell01.TheWorld.com> writes:

> But, to be safe, you should ensure that the result type of each
> "new" is the same as the type passed to Unchecked_Deallocation.

In the Ada95 context I've come across this most (a procedure takes
access-to-classwide, callers allocate locally) I started off thinking
of something like

  type Bar is tagged private;
  type Bar_P is access Bar'Class;
  type Foo is new Bar with private;
  type Foo_P is access Foo;
  ...
  F : Foo_P := new Foo'(...);
  BC : Bar_P := F.all'Access;
  ...
  Free (BC);

but remembering Bill Taylor's remarks about splattering your code with
".all'Access", and so as to avoid declarations like Foo_P, ended up
with

  type Bar is tagged private;
  type Bar_P is access Bar'Class;
  type Foo is new Bar with private;
  ...
  BC : Bar_P := new Foo'(...);
  F : Foo renames Foo (BC.all);
  ...
  Free (BC);



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

* Re: Deallocating list of polymorphic objects?
  2006-12-01  1:24 ` Randy Brukardt
@ 2006-12-01  8:57   ` Maciej Sobczak
  2006-12-01 12:33     ` Matthew Heaney
  0 siblings, 1 reply; 13+ messages in thread
From: Maciej Sobczak @ 2006-12-01  8:57 UTC (permalink / raw)


Randy Brukardt wrote:

> there are special rules in the language that insure that
> Finalize is always called at least once.

Can it be called more than once?


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Deallocating list of polymorphic objects?
  2006-12-01  8:57   ` Maciej Sobczak
@ 2006-12-01 12:33     ` Matthew Heaney
  2006-12-01 13:05       ` Maciej Sobczak
  0 siblings, 1 reply; 13+ messages in thread
From: Matthew Heaney @ 2006-12-01 12:33 UTC (permalink / raw)


Maciej Sobczak <no.spam@no.spam.com> writes:

> Can it be called more than once?

Yes.  When you write Finalize you must write it in such as way as to ensure
that it can be safely called a second time.



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

* Re: Deallocating list of polymorphic objects?
  2006-12-01  6:12   ` Michael Rohan
@ 2006-12-01 12:40     ` Matthew Heaney
  0 siblings, 0 replies; 13+ messages in thread
From: Matthew Heaney @ 2006-12-01 12:40 UTC (permalink / raw)


"Michael Rohan" <mrohan@ACM.ORG> writes:

> This looks really tidy and clean.

Yes, the indefinite container forms do come in handy sometimes.  In this case
it allows you to have a container of heterogenenous items.

Note that I declared the List_Type as tagged so you could use the new
distinguished-receiver syntax:

with Lists;
procedure List_Test is
   L : Lists.List_Type;
begin
   L.Append (1);
   L.Append (-1.0);
   L.Append (2);
   L.Append (-2.0);
end List_Test;

Even more tidy!




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

* Re: Deallocating list of polymorphic objects?
  2006-12-01 12:33     ` Matthew Heaney
@ 2006-12-01 13:05       ` Maciej Sobczak
  2006-12-01 14:56         ` Matthew Heaney
  0 siblings, 1 reply; 13+ messages in thread
From: Maciej Sobczak @ 2006-12-01 13:05 UTC (permalink / raw)


Matthew Heaney wrote:
> Maciej Sobczak <no.spam@no.spam.com> writes:
> 
>> Can it be called more than once?
> 
> Yes.  When you write Finalize you must write it in such as way as to ensure
> that it can be safely called a second time.

Could you please throw some paragraph numbers from AARM that are 
relevant to this? I would like to take a closer look at this subject.
The assertion that Finalize has to be safe w.r.t. multiple calls is a 
very important one and I don't seem to remember it being mentioned 
anywhere (including The book).


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Deallocating list of polymorphic objects?
  2006-12-01 13:05       ` Maciej Sobczak
@ 2006-12-01 14:56         ` Matthew Heaney
  2006-12-01 19:03           ` Georg Bauhaus
  0 siblings, 1 reply; 13+ messages in thread
From: Matthew Heaney @ 2006-12-01 14:56 UTC (permalink / raw)



Maciej Sobczak wrote:
>
> Could you please throw some paragraph numbers from AARM that are
> relevant to this? I would like to take a closer look at this subject.
> The assertion that Finalize has to be safe w.r.t. multiple calls is a
> very important one and I don't seem to remember it being mentioned
> anywhere (including The book).

I just used the search again here:

http://www.adaic.com/standards/05aarm/html/AA-SRCH.html

to search for a page with all of the words "finalize" and "twice" and
this page dropped out:

http://www.adaic.com/standards/05aarm/html/AA-7-6-1.html

See RM05 7.6.1, Note 22.

You could also use google groups to search CLA for posts with
"finalize" and "twice" and you'll get other hits.  This subject came up
a lot after Ada95 was released.




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

* Re: Deallocating list of polymorphic objects?
  2006-12-01 14:56         ` Matthew Heaney
@ 2006-12-01 19:03           ` Georg Bauhaus
  0 siblings, 0 replies; 13+ messages in thread
From: Georg Bauhaus @ 2006-12-01 19:03 UTC (permalink / raw)


On Fri, 2006-12-01 at 06:56 -0800, Matthew Heaney wrote:
> Maciej Sobczak wrote:
> >
> > Could you please throw some paragraph numbers from AARM that are
> > relevant to this? ...
> http://www.adaic.com/standards/05aarm/html/AA-7-6-1.html
> 
> See RM05 7.6.1, Note 22.

If you don't find a text book recommendation offending,
Cohen's Ada as a Second Language has many pointers. One
is an index entry, entitled 
"finalization invoked twice for the same object",
which leads to Controlled types, and also to deferred
abortion.


  -- Georg 





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

end of thread, other threads:[~2006-12-01 19:03 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-11-30 23:40 Deallocating list of polymorphic objects? Michael Rohan
2006-12-01  0:05 ` Robert A Duff
2006-12-01  6:41   ` Simon Wright
2006-12-01  1:24 ` Randy Brukardt
2006-12-01  8:57   ` Maciej Sobczak
2006-12-01 12:33     ` Matthew Heaney
2006-12-01 13:05       ` Maciej Sobczak
2006-12-01 14:56         ` Matthew Heaney
2006-12-01 19:03           ` Georg Bauhaus
2006-12-01  3:52 ` Matthew Heaney
2006-12-01  4:11 ` Matthew Heaney
2006-12-01  6:12   ` Michael Rohan
2006-12-01 12:40     ` Matthew Heaney

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