I'm not sure that being able to iterate over the contents of a stack is part of the normal use case for stacks! That aside, this is the sort of code you can write using generalized iteration (ARM 5.5.2):
with Ada.Text_IO;
with Stacks;
procedure Iteration is
use Ada.Text_IO;
S : Stacks.Stack (10);
begin
Stacks.Push (S, 5);
Stacks.Push (S, 3);
Stacks.Push (S, 8);
for C in Stacks.Iterate (S) loop
Put_Line (Stacks.Element (C)'Img); --'
end loop;
end Iteration;
This is a possible spec for Stacks
. Note that System
is only used in the private part.
with Ada.Iterator_Interfaces;
private with System;
package Stacks is
These are part of the fundamental Stack abstraction. Note that if you wanted to be able to write an indexed access (My_Stack (42)
or the loop for E of My_Stack loop...
) things get much more complicated. For a start, Stack
would have to be tagged.
type Stack (Count : Natural) is private;
procedure Push (To : in out Stack; Item : Integer);
function Element (S : Stack; Index : Positive) return Integer;
The remainder of the public part is to support generalized iteration.
type Cursor is private;
function Has_Element (Pos : Cursor) return Boolean;
function Element (C : Cursor) return Integer;
package Stack_Iterators
is new Ada.Iterator_Interfaces (Cursor, Has_Element);
function Iterate (S : Stack)
return Stack_Iterators.Forward_Iterator’Class; --'
private --'
type Integer_Array is array (Positive range <>) of Integer;
type Stack (Count : Natural) is record
List : Integer_Array (1 .. Count);
Top : Natural := 0;
end record;
function Element (S : Stack; Index : Positive) return Integer
is (S.List (Index));
Cursor
needs a reference to the object it's a cursor for. I've used System.Address
to avoid using access types and needing to make Stack
s aliased
.
type Cursor is record
The_Stack : System.Address;
List_Index : Positive := 1;
end record;
end Stacks;
The package body ...
with System.Address_To_Access_Conversions;
package body Stacks is
procedure Push (To : in out Stack; Item : Integer) is
begin
To.Top := To.Top + 1;
To.List (To.Top) := Item;
end Push;
We're going to have to convert Stack
addresses to pointers-to-Stack
s.
package Address_Conversions
is new System.Address_To_Access_Conversions (Stack);
function Has_Element (Pos : Cursor) return Boolean is
S : Stack renames Address_Conversions.To_Pointer (Pos.The_Stack).all;
begin
return Pos.List_Index <= S.Top;
end Has_Element;
function Element (C : Cursor) return Integer is
S : Stack renames Address_Conversions.To_Pointer (C.The_Stack).all;
begin
return S.List (C.List_Index);
end Element;
Now for the actual iterator; I've only done the forward version.
type Iterator is new Stack_Iterators.Forward_Iterator with record
The_Stack : System.Address;
end record;
overriding function First (Object : Iterator) return Cursor;
overriding function Next (Object : Iterator; Pos : Cursor) return Cursor;
function Iterate (S : Stack)
return Stack_Iterators.Forward_Iterator'Class is --'
begin
-- I seem to be relying on S being passed by reference.
-- This would be OK anyway if Stack was tagged; but it is a
-- reasonable bet that the compiler will pass a large object
-- by reference.
return It : Iterator do
It.The_Stack := S’Address; --'
end return; --'
end Iterate;
function First (Object : Iterator) return Cursor is
begin
return C : Cursor do
C.The_Stack := Object.The_Stack;
C.List_Index := 1;
end return;
end First;
function Next (Object : Iterator; Pos : Cursor) return Cursor is
pragma Unreferenced (Object);
begin
return C : Cursor do
C.The_Stack := Pos.The_Stack;
C.List_Index := Pos.List_Index + 1;
end return;
end Next;
end Stacks;