comp.lang.ada
 help / color / mirror / Atom feed
* Active Iteration - Update
@ 1998-05-15  0:00 Matthew Heaney
  1998-05-17  0:00 ` Steve Whalen
  0 siblings, 1 reply; 3+ messages in thread
From: Matthew Heaney @ 1998-05-15  0:00 UTC (permalink / raw)




I figured out a way to do iteratoration using a dispatching iterator
(handle) constructor, and at the same time hide away the calls to
New_Iterator and Free.

I originally posed something like this:

   function New_Iterator 
     (Stack : access Root_Stack) return Iterator_Access;

My issue with this solution is that the client has to remember to
explicitly Free the object returned by that constructor, as in:

procedure Do_Something (S : access Stack'Class) is

   IA : Iterator_Access := New_Iterator (S);

   Iter : Root_Iterator'Class renames IA.all;
begin
   while not Is_Done (Iter) loop
      ... Get_Item (Iter) ...
      <do something with item>
      Advance (Iter);
   end loop;

   Free (IA);
end Do_Something;

There are two reasons why I don't like this:

1) The client has to call a function manually, to get an iterator
indirectly, ie through an access object.

2) The client has to remember to manually call Free, to reclaim storage
associated with the iterator object.

Item (2) is especially pernicious, because 

a) the client can forget to call Free, thus creating a memory leak

b) there could be an unhandled exception raised prior to the call to Free,
thus creating a memory leak

We can do better.

The idea is to wrap the iterator access object inside a Controlled object,
which I've called Iterator_Handle.  That way  Finalize gets called
automatically, and thus no memory leak can occur.

The iterator handle is implemented as a Controlled record containing an
access object that designates an iterator.  As before, the iterator is
constructed by calling a dispatching function that returns the iterator
access type.  The iterator handle implements its operations in terms of the
corresponding operations of the actual iterator.

The iterator handle, during its (automatic) Finalization, calls a
dispatching Deallocate operation to reclaim the storage associated with the
iterator.

The improved version of the classwide operation above is:

procedure Do_Something (S : access Stack'Class) is

   Handle : Iterator_Handle (S);
begin

   while not Is_Done (Handle) loop
      ... Get_Item (Handle) ...
      <do something with item>
      Advance (Handle);
   end loop;

end Do_Something;

Note that there is no call to New, nor is there a call to Free.

Hope that helps,
Matt

P.S. The sources below compile and run using GNAT 3.10p, and are in a
format suitable for use with gnatchop.


-- STX
with Ada.Finalization;

generic

   type Stack_Item is private;
   with function "=" (L, R : Stack_Item) return Boolean is <>;

package Stacks is

   type Root_Stack is abstract tagged null record;

   type Stack_Item_Access is access all Stack_Item;
   for Stack_Item_Access'Storage_Size use 0;

   procedure Push
     (Item : in     Stack_Item;
      On   : in out Root_Stack) is abstract;

   function Get_Top
     (Stack : Root_Stack) return Stack_Item is abstract;

   function Get_Top
     (Stack : access Root_Stack) return Stack_Item_Access is abstract;


   type Iterator_Handle
     (Stack : access Root_Stack'Class) is limited private;

   function Is_Done
     (Handle : Iterator_Handle) return Boolean;

   function Get_Item
     (Handle : Iterator_Handle) return Stack_Item_Access;

   procedure Advance
     (Handle : Iterator_Handle);

   procedure Reset
     (Handle : Iterator_Handle);

private

   type Root_Iterator is
     abstract tagged limited null record;

   function Is_Done
     (Iterator : Root_Iterator) return Boolean is abstract;

   function Get_Item
     (Iterator : Root_Iterator) return Stack_Item_Access is abstract;

   procedure Advance
     (Iterator : in out Root_Iterator) is abstract;

   procedure Reset
     (Iterator : in out Root_Iterator) is abstract;

   procedure Deallocate
     (Iterator : access Root_Iterator) is abstract;


   type Iterator_Access is
      access all Root_Iterator'Class;

   function New_Iterator
     (Stack : access Root_Stack) return Iterator_Access is abstract;

   type Iterator_Handle (Stack : access Root_Stack'Class) is
     new Ada.Finalization.Limited_Controlled with record
        Iter : Iterator_Access := New_Iterator (Stack);
     end record;

   procedure Finalize
     (Handle : in out Iterator_Handle);

end Stacks;


with Ada.Text_IO; use Ada.Text_IO;

package body Stacks is

   function Is_Done
     (Handle : Iterator_Handle) return Boolean is
   begin
      return Is_Done (Handle.Iter.all);
   end;


   function Get_Item
     (Handle : Iterator_Handle) return Stack_Item_Access is
   begin
      return Get_Item (Handle.Iter.all);
   end;


   procedure Advance
     (Handle : Iterator_Handle) is
   begin
      Advance (Handle.Iter.all);
   end;


   procedure Reset
     (Handle : Iterator_Handle) is
   begin
      Reset (Handle.Iter.all);
   end;

   procedure Finalize
     (Handle : in out Iterator_Handle) is
   begin
      Deallocate (Handle.Iter);
   end;


end Stacks;


generic
   Max_Depth : in Positive;
package Stacks.Bounded_G is

   type Bounded_Stack is
     new Root_Stack with private;

   function "="
     (L, R : Bounded_Stack) return Boolean;


   procedure Push
     (Item : in     Stack_Item;
      On   : in out Bounded_Stack);

   function Get_Top
     (Stack : Bounded_Stack) return Stack_Item;

   function Get_Top
     (Stack : access Bounded_Stack) return Stack_Item_Access;


   type Bounded_Iterator
     (Stack : access Bounded_Stack'Class) is
      limited private;

   function Is_Done
     (Iterator : Bounded_Iterator) return Boolean;

   function Get_Item
     (Iterator : Bounded_Iterator) return Stack_Item_Access;

   procedure Advance (Iterator : in out Bounded_Iterator);

   procedure Reset (Iterator : in out Bounded_Iterator);


private

   type Stack_Item_Array is
      array (Positive range <>) of aliased Stack_Item;

   function "=" (L, R : Stack_Item_Array) return Boolean is abstract;

   subtype Items_Range is Positive range 1 .. Max_Depth;

   type Bounded_Stack is
     new Root_Stack with record
        Items : Stack_Item_Array (Items_Range);
        Depth : Natural range 0 .. Max_Depth := 0;
     end record;


   type Bounded_Iterator
     (Stack : access Bounded_Stack'Class) is
     new Root_Iterator with record
        Index : Natural := Stack.Depth;
     end record;


   function New_Iterator
     (Stack : access Bounded_Stack) return Iterator_Access;

   procedure Deallocate
     (Iterator : access Bounded_Iterator);


end Stacks.Bounded_G;


with Ada.Unchecked_Deallocation;
with Ada.Text_IO;

package body Stacks.Bounded_G is

   function "="
     (L, R : Bounded_Stack) return Boolean is
   begin
      if L.Depth /= R.Depth then
         return False;
      end if;

      for I in 1 .. L.Depth loop
         if L.Items (I) /= R.Items (I) then
            return False;
         end if;
      end loop;

      return True;
   end "=";


   procedure Push
     (Item : in     Stack_Item;
      On   : in out Bounded_Stack) is

      Stack : Bounded_Stack renames On;
      Depth : Natural renames Stack.Depth;
   begin
      Depth := Depth + 1;
      Stack.Items (Depth) := Item;
   end Push;


   function Get_Top
     (Stack : Bounded_Stack) return Stack_Item is
   begin
      return Stack.Items (Stack.Depth);
   end Get_Top;


   function Get_Top
     (Stack : access Bounded_Stack) return Stack_Item_Access is
   begin
      return Stack.Items (Stack.Depth)'Access;
   end Get_Top;


   function Is_Done
     (Iterator : Bounded_Iterator) return Boolean is
   begin
      return Iterator.Index = 0;
   end;


   function Get_Item
     (Iterator : Bounded_Iterator) return Stack_Item_Access is

      Stack : Bounded_Stack'Class renames Iterator.Stack.all;
   begin
      return Stack.Items (Iterator.Index)'Access;
   end;


   procedure Advance (Iterator : in out Bounded_Iterator) is
   begin
      Iterator.Index := Iterator.Index - 1;
   end;


   procedure Reset (Iterator : in out Bounded_Iterator) is
   begin
      Iterator.Index := Iterator.Stack.Depth;
   end;


   function New_Iterator
     (Stack : access Bounded_Stack) return Iterator_Access is
   begin
     return new Bounded_Iterator (Stack);
   end;


   procedure Free is
     new Ada.Unchecked_Deallocation
     (Root_Iterator'Class, Iterator_Access);

   procedure Deallocate (Iterator : access Bounded_Iterator) is
      IA : Iterator_Access := Iterator_Access (Iterator);
   begin
      Free (IA);
      Ada.Text_IO.Put_Line ("reclaimed memory for iterator handle");
   end;


end Stacks.Bounded_G;

generic
   with function Image (Item : Stack_Item) return String;
package Stacks.Stack_IO_G is

   procedure Dump (Stack : access Root_Stack'Class);

end Stacks.Stack_IO_G;


with Ada.Text_IO;  use Ada.Text_IO;

package body Stacks.Stack_IO_G is

   procedure Dump (Stack : access Root_Stack'Class) is

      Iterator : Iterator_Handle (Stack);
   begin
      while not Is_Done (Iterator) loop
         Put (Image (Get_Item (Iterator).all));
         Advance (Iterator);
      end loop;

      New_Line;
   end Dump;

end Stacks.Stack_IO_G;


with Stacks;
pragma Elaborate_All (Stacks);

package Integer_Stacks is new Stacks (Integer);


with Stacks.Bounded_G;
pragma Elaborate_All (Stacks.Bounded_G);

package Integer_Stacks.Bounded is
  new Integer_Stacks.Bounded_G (Max_Depth => 10);


with Stacks.Stack_IO_G;
pragma Elaborate_All (Stacks.Stack_IO_G);

package Integer_Stacks.Stack_IO is
   new Integer_Stacks.Stack_IO_G (Integer'Image);


with Integer_Stacks.Bounded;   use Integer_Stacks.Bounded;
with Integer_Stacks.Stack_IO;  use Integer_Stacks.Stack_IO;

procedure Test_Stacks is

   Stack : aliased Bounded_Stack;

begin

   Push (9, On => Stack);
   Push (8, On => Stack);
   Push (7, On => Stack);
   Push (6, On => Stack);
   Push (5, On => Stack);

   Dump (Stack'Access);

end;




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

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

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-05-15  0:00 Active Iteration - Update Matthew Heaney
1998-05-17  0:00 ` Steve Whalen
1998-05-17  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