Is it possible to access child package declarations from a parent package ?
-- parent.ads
package Parent is
procedure F(A : Child_Type);
end Parent;
-- parent-child.ads
package Parent.Child is
type Child_Type is (A, B, C);
end Parent.Child;
The nested version works fine :
-- parent.ads
package Parent is
package Child is
type Child_Type is (A, B, C);
end Child;
use Child;
procedure F(A : Child_Type);
end Parent;
And maybe there is another way to do this since I think it is not possible using child packages...
In general, no; the second example works because the specification of Child is known when F is declared in Parent. In light of your previous question on this topic, it may be that you want a clean way to separate multiple implementations of a common specification. This related Q&A discusses two approaches: one using inheritance and the other using a library-based mechanism at compile-time.
I think what you are looking for is a private child package, this generally behaves in the same way as your nested example, but you cannot access it outside of its parent body.
Therefore :
private package Parent.Child is
type Child_Type is (A,B,C);
end Parent.Child;
...
package Parent is
procedure F;
end Parent;
...
with Ada.Text_Io;
with Parent.Child;
package body Parent is
procedure F is
begin
for A in Parent.Child.Child_Type'Range loop
Ada.Text_Io.Put_Line (Parent.Child.Child_Type'Image (A));
end loop;
end F;
end Parent;
Is ok to compile, but remember if you with the child in the parent spec (like you do with the parameter to F), you will get a circular dependency as children require their parents to exist first !
Therefore it really depends on what you want to be public to both the parent and the child whether this is an actual solution to your problem.
Julio, Types declared in a spec file (mytypes.ads)
package Mytypes is
type Fruit is (Apple, Pear, Pineapple, Banana, Poison_Apple);
subtype Safe_Fruit is Fruit range Apple .. Banana;
end Mytypes;
...
Withed it in several others :
with Mytypes;
package Parent is
function Permission (F : in Mytypes.Fruit) return Boolean;
end Parent;
...
package body Parent is
function Permission (F : in Mytypes.Fruit) return Boolean is
begin
return F in Mytypes.Safe_Fruit;
end Permission;
end Parent;
...
package Parent.Child is
procedure Eat (F : in Mytypes.Fruit);
end Parent.Child;
...
with Ada.Text_Io;
package body Parent.Child is
procedure Eat (F : in Mytypes.Fruit) is
begin
if Parent.Permission (F) then
Ada.Text_Io.Put_Line ("Eating " & Mytypes.Fruit'Image (F));
else
Ada.Text_Io.Put_Line ("Forbidden to eat " & Mytypes.Fruit'Image (F));
end if;
end Eat;
end Parent.Child;
...
with Mytypes;
with Parent.Child;
procedure Main is
begin
for I in Mytypes.Fruit'Range loop
Parent.Child.Eat (I);
end loop;
end Main;
It Compiles:
$ gnatmake main.adb
gcc-4.4 -c parent-child.adb
gnatbind -x main.ali
gnatlink main.ali
It Runs :
$ ./main
Eating APPLE
Eating PEAR
Eating PINEAPPLE
Eating BANANA
Forbidden to eat POISON_APPLE
Is this what you tried ?
Related
I'm trying to hide some aspects of a library to the users and reading the RM for type conversions, I can't understand why the following code fails.
A user will instantiate root.child.concrete.concrete_t and then will call to root.p_userInterface passing that instance, but a compilation error is being thrown on root.adb:
invalid tagged conversion, not compatible with type "child_t'class" defined at root-child.ads:4
root.ads:
limited with root.child;
package root is
procedure p_userInterface (obj : in out root.child.child_t'Class);
private
type root_t is abstract tagged null record;
procedure p_primitive (this : in out root_t) is abstract;
end root;
root.adb:
with root.child;
package body root is
procedure p_userInterface (obj : in out root.child.child_t'Class) is
begin
-- error: invalid tagged conversion, not compatible with type "child_t'class" defined at root-child.ads:4
root_t'Class(obj).p_primitive;
end p_userInterface;
end root;
root-child.ads:
package root.child is
type child_t is abstract tagged private;
function f_getComponent(this : in child_t) return Integer;
private
type child_t is abstract new root_t with
record
component : Integer;
end record;
overriding
procedure p_primitive (this : in out child_t) is abstract;
end root.child;
root-child.adb:
package body root.child is
function f_getComponent(this : in child_t) return Integer is
begin
return this.component;
end f_getComponent;
end root.child;
root-child-concrete.ads:
package root.child.concrete is
type concrete_t is new child_t with private;
procedure p_setAnotherComponent (this : in out concrete_t;
c : Boolean);
function f_getAnotherComponent (this : concrete_t) return Boolean;
private
type concrete_t is new child_t with
record
anotherComponent : Boolean;
end record;
overriding
procedure p_primitive (this : in out concrete_t);
end root.child.concrete;
root-child-concrete.adb:
package body root.child.concrete is
procedure p_primitive (this : in out concrete_t) is
begin
-- for example
this.anotherComponent := True;
end p_primitive;
procedure p_setAnotherComponent (this : in out concrete_t;
c : Boolean) is
begin
this.anotherComponent := c;
end p_setAnotherComponent;
function f_getAnotherComponent (this : concrete_t) return Boolean is
begin
return this.anotherComponent;
end f_getAnotherComponent;
end root.child.concrete;
Why? I understand that the implementation of root is not able to see the private part of its root.child child package, thus is not able to see that type child_t is extending root_t privately. Am I right?
Can I achieve something like this differently? I would like to hide p_primitive to the users because is to perform internal things, but I would like to dispatch it internally if it's possible.
I think I have finally achieved it. Here is the solution that works for me, only on changed units, commenting out the old parts, as I can't use html strike tag to cross out code:
root.ads:
--limited with root.child;
package root is
type root_t is abstract tagged null record;
procedure p_userInterface (obj : in out root_t'Class); --root.child.child_t'Class);
private
--type root_t is abstract tagged null record;
--procedure p_primitive (this : in out root_t) is abstract;
type hiddenRoot_t is abstract new root_t with null record;
procedure p_primitive (this : in out hiddenRoot_t) is abstract;
end root;
root.adb:
--with root.child; It was not needed before neither
package body root is
procedure p_userInterface (obj : in out root_t'Class) is --root.child.child_t'Class) is
begin
--root_t'Class(obj).p_primitive; -- invalid tagged conversion, not compatible with type "child_t'class"
hiddenRoot_t'Class(obj).p_primitive;
end p_userInterface;
end root;
root-child.ads:
package root.child is
--type child_t is abstract tagged private;
type child_t is abstract new root_t with private;
function f_getComponent(this : in child_t) return Integer;
private
--type child_t is abstract new root_t with
type child_t is abstract new hiddenRoot_t with
record
component : Integer;
end record;
overriding
procedure p_primitive (this : in out child_t) is abstract;
end root.child;
This way I'm able to dispatch privately to the implementation of p_primitive of a concrete_t type, and on a main I can ensure that an instance of concrete_t has no visibility of p_primitive.
Any further comment to this solution will be appreciated, I do not know if I'm missing something important, but seems to work. I have no idea if it is the correct solution.
I hope that you can advise me. I'm trying to use ADA 95's Object Oriented Features for the first time, and I want two derived classes, cyclicgroup and polyggroup, to call the put() method belonging to their base class,
abstractGroup. But instead of tracing up the class hierarchy as I expected, the compiler just tells me that I have a type mismatch in the call to put(). How do I tell the compiler to recognize the connection between the objects?
Here are the 5 files, with all the extraneous stuff removed, and the attempted compile:
grpdriver2.adb:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;
with groupstuff2;
with subgrphandler2;
procedure grpdriver2 is
cycg: groupStuff2.cyclicgroup;
polyg: groupStuff2.polygonGroup;
begin
cycg := groupstuff2.createCyclicGroup( 10);
subgrphandler2.put(cycg); -- line 13
------------------------------------------------------------------------------------------------------------------
polyg := groupstuff2.createPolygonGroup( 10);
subgrphandler2.put(polyg); -- line 18
end grpdriver2;
.................................................................................................................
groupstuff2.ads:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;
package groupstuff2 is
type abstractGroup is tagged record
x: integer;
end record;
type cyclicGroup is new abstractGroup with record
y: integer;
end record;
function createCyclicGroup( size: in integer) return cyclicGroup ;
----------------------------------------
type polygonGroup is new abstractGroup with record
null;
end record;
function createPolygonGroup( size: in integer) return polygonGroup ;
end groupstuff2;
.......................................................................................
groupstuff2.adb:
package body groupstuff2 is
procedure put( g: in abstractGroup) is
x: integer;
begin
x := 1;
end put;
function createCyclicGroup( size: in integer) return cyclicGroup is
cycg: cyclicGroup;
begin
cycg.x := size;
return cycg;
end createCyclicGroup;
function createPolygonGroup( size: in integer) return polygonGroup is
polyg: polygonGroup;
begin
polyg.x := size;
return polyg;
end createPolygonGroup;
end groupstuff2;
..............................................................................
subgrphandler2.ads:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;
with groupstuff2;
package subgrphandler2 is
procedure put( g: in groupStuff2.abstractGroup);
end subgrphandler2;
...........................................................................................
subgrphandler2.adb:
package body subgrphandler2 is
procedure put( g: in groupStuff2.AbstractGroup) is
begin
put("THIS IS A PUT STATMENT");
end put;
end subgrphandler2;
COMPILE ATTEMPT:
C:\GNAT\2018\bin\ceblang>gnatmake grpdriver2
gcc -c grpdriver2.adb
grpdriver2.adb:13:36: expected type "abstractGroup" defined at groupstuff2.ads:7
grpdriver2.adb:13:36: found type "cyclicGroup" defined at groupstuff2.ads:16
grpdriver2.adb:18:36: expected type "abstractGroup" defined at groupstuff2.ads:7
grpdriver2.adb:18:36: found type "polygonGroup" defined at groupstuff2.ads:25
gnatmake: "grpdriver2.adb" compilation error
I cannot compile Ada95 code as I'm using GNAT CE 2018 (which only supports Ada 2012), but it seems that you need to add the Class attribute to the type of the argument of put in subgrphandler2 to make it accept a class wide type (i.e. groupStuff2.AbstractGroup, and all of its extensions (inherited types)).
You also might want to make groupStuff2.AbstractGroup actually abstract by adding the abstract keyword to its definition (see below).
This (reformatted code) compiles on GNAT CE 2018 in Ada 2012 mode:
group_driver_2.adb
with Group_Stuff_2;
with Sub_Group_Handler_2;
procedure Group_Driver_2 is
Cycg : Group_Stuff_2.Cyclic_Group;
Polyg : Group_Stuff_2.Polygon_Group;
begin
Cycg := Group_Stuff_2.Create_Cyclic_Group (10);
Sub_Group_Handler_2.Put (Cycg);
Polyg := Group_Stuff_2.Create_Polygon_Group (10);
Sub_Group_Handler_2.Put (Polyg);
end Group_Driver_2;
group_stuff_2.ads
package Group_Stuff_2 is
type Abstract_Group is abstract tagged
record
X: Integer;
end record;
-- Cyclic_Group
type Cyclic_Group is new Abstract_Group with
record
Y: Integer;
end record;
function Create_Cyclic_Group
(Size: in Integer) return Cyclic_Group;
-- Polygon_Group
type Polygon_Group is new Abstract_Group with null record;
function Create_Polygon_Group
(Size: in Integer) return Polygon_Group ;
end Group_Stuff_2;
group_stuff_2.adb
package body Group_Stuff_2 is
-------------------------
-- Create_Cyclic_Group --
-------------------------
function Create_Cyclic_Group
(Size : in Integer) return Cyclic_Group
is
Cycg : Cyclic_Group;
begin
Cycg.X := Size;
return Cycg;
end Create_Cyclic_Group;
--------------------------
-- Create_Polygon_Group --
--------------------------
function Create_Polygon_Group
(Size : in Integer) return Polygon_Group
is
Polyg: Polygon_Group;
begin
Polyg.X := Size;
return Polyg;
end Create_Polygon_Group;
end Group_Stuff_2;
sub_group_handler_2.ads
with Group_Stuff_2;
package Sub_Group_Handler_2 is
procedure Put (G : in Group_Stuff_2.Abstract_Group'Class);
end Sub_Group_Handler_2;
sub_group_handler_2.adb
with Ada.Text_IO; use Ada.Text_IO;
package body Sub_Group_Handler_2 is
procedure Put (G: in Group_Stuff_2.Abstract_Group'Class) is
begin
Put_Line ("Value of X is" & Integer'Image (G.X));
end Put;
end Sub_Group_Handler_2;
I'm trying to understand how object oriented works in Ada. I have found a situation that I'm unable to resolve.
I know how to use class wide types for enabling polymorphism, and I know how to override a parent class' method from a derived one.
The thing I don't know how to do is to override a procedure that receives a class wide type as an argument, because I always obtain compilation errors. I explain this deeply below:
What I have tried
Type 1
package Pack1
type Type1 is tagged
record
i : Integer := 20;
end record;
function get_number(self : Type1) return Integer;
procedure do_something(self : Type1'class);
end Pack1;
----------------------------------------------------
package body Pack1 is
function get_number(self : Type1) return Integer is
begin
return 200;
end get_number;
procedure do_something(self : Type1'class) is
begin
Put_Line("Calling from Type1, " & (Integer'Image(self.i + self.get_number)));
end do_something;
end Pack1;
Type 2
package Pack2
type Type2 is new Type1 with
record
ii : Integer := 20;
end record;
overriding function get_number(self : Type2) return Integer;
overriding procedure do_something(self : Type2'class);
end Pack2;
----------------------------------------------------
package body Pack2 is
function get_number(self : Type2) return Integer is
begin
return 300;
end get_number;
procedure do_something(self : Type2'class) is
begin
Put_Line("Calling from Type2, " & (Integer'Image(self.i + self.ii + self.get_number)));
end do_something;
end Pack2;
Main
procedure Main is
t1 : Type1;
t2 : Type2;
begin
t1.do_something;
t2.do_something;
end Main;
Obtained error
I obtain an error during compilation time:
possible interpretation at Type1.ads
possible interpretation at Type2.ads
Expected output
I'm expecting to obtain the following, when I can compile the code:
Calling from Type1, 220
Calling from Type2, 350
How can I achieve the behavior I want?
Subprograms taking class-wide arguments are not primitive operations of the parent of the class, and can thus not be inherited.
If a subprogram takes a class-wide argument, the point is that its implementation is written in term of operations defined for the parent of the class. If you want to change its behaviour for a derived type, you do it by overriding the relevant primitive operations of the derived type.
Specifications:
package A is
type Values is range 0 .. 999;
type Instance is tagged private;
subtype Class is Instance'Class; --'
function Name (Item : in Instance) return String;
function Get_Number (Item : in Instance) return Values;
function Get_Sum (Item : in Instance) return Values;
private
type Instance is tagged
record
First : Values := 20;
end record;
end A;
with A;
package B is
subtype Parent is A.Instance;
type Instance is new Parent with private;
subtype Class is Instance'Class; --'
overriding
function Name (Item : in Instance) return String;
overriding
function Get_Number (Item : in Instance) return A.Values;
overriding
function Get_Sum (Item : in Instance) return A.Values;
private
type Instance is new Parent with
record
Second : A.Values := 20;
end record;
end B;
with Ada.Text_IO;
with A;
procedure Do_Something (Item : in A.Class);
Implementations:
package body A is
function Name (Item : in Instance) return String is ("Class A");
function Get_Number (Item : in Instance) return Values is (200);
function Get_Sum (Item : in Instance) return Values is (Item.First);
end A;
package body B is
use all type A.Values;
overriding
function Name (Item : in Instance) return String is ("Class B");
overriding
function Get_Number (Item : in Instance) return A.Values is (300);
overriding
function Get_Sum (Item : in Instance) return A.Values is (Parent (Item).Get_Sum + Item.Second);
end B;
procedure Do_Something (Item : in A.Class) is
use all type A.Values;
begin
Ada.Text_IO.Put_Line
("Calling from " & Item.Name & ", " & A.Values'Image (Item.Get_Number + Item.Get_Sum));
end Do_Something;
And finally a demonstrator:
with A;
with B;
with Do_Something;
procedure Inheritance_Demo_2018_06_13 is
O : A.Instance;
P : B.Instance;
begin
Do_Something (O);
Do_Something (P);
end Inheritance_Demo_2018_06_13;
As Jacob said in this answer, you can’t override Do_Something because it’s not primitive, because its controlling parameter is classwide.
If you remove Pack2.Do_Something altogether, your program will compile. However, the output is
$ ./main
Calling from Type1, 220
Calling from Type1, 320
which is getting closer to what you want.
A better solution would be to eliminate ’Class in Pack2.Do_Something, which makes it a primitive (dispatchable) operation.
I still don’t get the result you want:
$ ./main
Calling from Type1, 220
Calling from Type2, 340
Perhaps you meant to initialise Pack2.Type2.ii to 30?
(By the way, the code you posted doesn’t compile. Please make it easier for us to help you by submitting compilable examples!)
The problem is you are trying to use class types a bit too early. You want your Do_Something procedures to take an input of Type1 and Type2, not Type1'Class or Type2'Class. Then you can call those procedures from within another one that takes a class type parameter (which will give you your polymorphism).
Jacob Sparre Andersen showed you this in his answer, but I wanted to gen up something closer to your original code as an extra reference.
Below is a test program based on your original (compiled in the jdoodle online compiler) that shows the various ways to call the function polymorphically.
Code:
with Ada.Text_IO; use Ada.Text_IO;
procedure jdoodle is
package Pack1 is
type Type1 is tagged
record
i : Integer := 20;
end record;
type Type1_Class_Access is access all Type1'Class;
function get_number(self : Type1) return Integer;
procedure do_something(self : Type1); -- note the change here
end Pack1;
----------------------------------------------------
package body Pack1 is
function get_number(self : Type1) return Integer is
begin
return 200;
end get_number;
procedure do_something(self : Type1) is -- note the change here
begin
Put_Line("Calling from Type1, " & (Integer'Image(self.i + self.get_number)));
end do_something;
end Pack1;
package Pack2 is
use Pack1;
type Type2 is new Type1 with
record
ii : Integer := 20;
end record;
overriding function get_number(self : Type2) return Integer;
overriding procedure do_something(self : Type2); -- note the change here
end Pack2;
----------------------------------------------------
package body Pack2 is
function get_number(self : Type2) return Integer is
begin
return 300;
end get_number;
procedure do_something(self : Type2) is
begin
Put_Line("Calling from Type2, " & (Integer'Image(self.i + self.ii + self.get_number)));
end do_something;
end Pack2;
t1 : aliased Pack1.Type1;
t2 : aliased Pack2.Type2;
p1 : Pack1.Type1'Class := Pack1.Type1'(others => <>);
p2 : Pack1.Type1'Class := Pack2.Type2'(others => <>);
procedure Do_Something(Object : Pack1.Type1'Class) is
begin
Object.Do_Something; -- polymorphically calls Do_Something
end Do_Something;
type Class_Array is array(Integer range <>) of Pack1.Type1_Class_Access;
a : Class_Array(1..2) := (1 => t1'Access, 2 => t2'Access);
begin
-- Non Polymorphic calls
t1.do_something;
t2.do_something;
-- Polymorphic variable calls
-- both variables are of type Pack1.Type1'Class
p1.do_something;
p2.do_something;
-- Polymorphic procedure calls
-- the input type of the procedure is Pack1.Type1'Class
Do_Something(t1);
Do_Something(t2);
-- Polymorphic array of class access variable calls
for e of a loop
e.Do_Something;
end loop;
for e of a loop
Do_Something(e.all);
end loop;
end jdoodle;
Output:
Calling from Type1, 220
Calling from Type2, 340
Calling from Type1, 220
Calling from Type2, 340
Calling from Type1, 220
Calling from Type2, 340
Calling from Type1, 220
Calling from Type2, 340
Calling from Type1, 220
Calling from Type2, 340
I am trying to create a package that has Dynamic dispatching using access types. I have achieved Dynamic Dispatching using Class Types using This Q/A as a guide.
I keep getting a compilation error that says: cannot call abstract subprogram. This makes me think that the compiler either doesnt recognize the specialized subprogram, or doesnt recognize the type as a specialized type. But both seem right to me... I dont get it.
main.2.ada
with Ada.Text_IO;
with Animal.Cat;
procedure Main is
Tabby : aliased Animal.Cat.Cat_t;
Animal_Ref : Animal.Any_Animal_Ptr := Tabby'Unchecked_Access;
Result : Boolean;
begin
Animal.Stroke_Fur (Animal => Animal_Ref.all);
Result := Animal.Is_Happy(Ptr => Animal_Ref);
Ada.Text_IO.Put_Line ("Happy Animal = " & Boolean'Image (Result));
end Main;
animal.1.ada
package Animal is
type base_t is abstract tagged limited null record;
type Animal_t is abstract new
base_t with private;
type Any_Animal_Ptr is access all Animal_t'Class;
----
procedure Stroke_Fur (Animal : in out Animal_t) is abstract;
----
function Is_Happy (Ptr : in Any_Animal_Ptr) return boolean is abstract;
private
type Animal_t is abstract new base_t with
record
Index : integer;
end record;
end Animal;
animal.cat.1.ada
package Animal.Cat is
type Cat_t is new Animal.Animal_t with private;
type Cat_Ptr is access all Cat_t;
----
procedure Stroke_Fur (Cat : in out Cat_t);
----
function Is_Happy (Ptr : in Cat_Ptr) return Boolean;
private
type Cat_t is new Animal.Animal_t with
record
Purr : Boolean := False;
end record;
end Animal.Cat;
animal.cat.2.ada
package body Animal.Cat is
----
procedure Stroke_Fur (Cat : in out Cat_t) is
begin
Cat.Purr := True;
end Stroke_Fur;
----
function Is_Happy (Ptr : in Cat_Ptr) return Boolean is
begin
return Ptr.Purr;
end Is_Happy;
end Animal.Cat;
Error
main.2.ada:13:21: cannot call abstract subprogram "Is_Happy"
function Is_Happy (Ptr : in Any_Animal_Ptr) return boolean is abstract;
isn’t a primitive operation (ARM3.2.3(2)) of Animal_t, and so it isn’t dispatching; it’s merely an abstract subprogram (which therefore can’t be called).
Why would you want to make a non-dispatching subprogram abstract? Perhaps you might have a type Metres, in which case the predefined ”*” is inappropriate (multiplying two distances in metres returns an area, not a distance) and you might want to make it abstract to prevent inadvertent misuse.
Anyway, you can declare a primitive operation of Animal_t using accesses as
function Is_Happy (Ptr : access Animal_t) return boolean is abstract;
and then for cats
function Is_Happy (Ptr : access Cat_t) return Boolean;
(or even
overriding
function Is_Happy (Ptr : access Cat_t) return Boolean;
if you want the compiler to check that it really is overriding).
By the way, if you are using Ada 2005 or later you can use the prefixed notation to write the calls as
Animal_Ref.Stroke_Fur;
Result := Animal_Ref.Is_Happy;
which is prettier.
Ada 2012 user defined iterator
This feature allows user to make a custom iterator. Instead of writing Get (List, Index) one could write List (Index) and instead of writing for Index in 1 .. List.Max one could write for Index in List'Range. I am not sure if List'Range is possible if list is not an array, maybe there is another syntax for user defined iterator.
How to use Ada 2012 user defined iterators?
Preferably: how to implement user defined iterator in the example?
Example
This is a Stack or LIFO example. Next step is to hide type Stack members and implement user defined iterator for type Stack List.
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Main is
use Ada.Text_IO;
use Ada.Integer_Text_IO;
type Integer_Array is array (Integer range <>) of Integer;
type Stack (Count : Natural) is record
List : Integer_Array (1 .. Count);
Top : Natural := 0;
end record;
procedure Push (Item : Integer; Result : in out Stack) is
begin
Result.Top := Result.Top + 1;
Result.List (Result.Top) := Item;
end;
S : Stack (10);
begin
Push (5, S);
Push (3, S);
Push (8, S);
for I in S.List'First .. S.Top loop
Put (S.List (I), 2);
New_Line;
end loop;
--for I in S'Range loop
--Put (S (I), 2);
--New_Line;
--end loop;
end;
The use of S'Range is not available for containers, only for standard arrays. This cannot be changed.
You likely meant to say for C in S.Iterate loop which calls the function Iterate and returns cursors for each step of the loop. At that point, you need to use Element (C) to get access to the actual element (or perhaps more efficiently Reference (C).
A third version is for E of S loop which returns directly the element. You do not have access to the corresponding cursor. This is in general the preferred way to write the loop, except perhaps when iterating over the whole contents of a map, since there is no way to access the key, only the value.
For more information on how to add support for these loops in your own data structures, you could take a look at the two gems published by AdaCore:
http://www.adacore.com/adaanswers/gems/gem-127-iterators-in-ada-2012-part-1/
http://www.adacore.com/adaanswers/gems/gem-128-iterators-in-ada-2012-part-2/
which should provide all the information you need, I hope.
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 Stacks 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-Stacks.
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;
You use the iterators in for loops like this:
for C in S.Iterator loop
Put (S (C));
New_Line;
end loop;
Or in the more lazy version:
for E of S loop
Put (E);
New_Line;
end loop;
Implementing iterators is a rather long story, and I refer you to the Ada 2012 rationale (which #trashgod also mentioned) for a detailed explanation.