I want to create a DLL written in ADA that can be called from C++ or from ADA. I've compiled the DLL, and it exports my functions as well as Init and Final.
I now would like to call this DLL from Ada, using dynamic linking. The first half in the code below calls a C++ dll (without Init and Final). This works well. The second half tries to run Init before calling the DLL's function. But the line identified with lots of asterisks won't compile, instead I get error: missing operand.
Where am I going wrong?
with Ada.Text_IO; use Ada.Text_IO;
with Interfaces; use Interfaces;
with Interfaces.C; use Interfaces.c;
with System; use System;
with Ada.Unchecked_Conversion;
procedure SmallCode is
-- Definitions for dynamic DLL interface
type HANDLE is new Unsigned_32;
function LoadLibrary (lpFileName : char_array) return HANDLE;
pragma Import (stdcall, LoadLibrary, "LoadLibrary", "_LoadLibraryA#4"); -- Ada95 doesn't use #n
function GetProcAddress (hModule : HANDLE; lpProcName : char_array) return Address;
pragma Import (stdcall, GetProcAddress, "GetProcAddress", "_GetProcAddress");
--
-- The interface of the function we want to call. It is a pointer (access type) because
-- we will link it dynamically. The function is from AdaCallable.dll
type fnAdaCallable is access function(val : Integer_32) return Integer_32;
pragma Convention (Stdcall, fnAdaCallable);
function To_fnAdaCallable is new Ada.Unchecked_Conversion (Address, fnAdaCallable);
Pointer : Address;
function To_AdaCallable is new Ada.Unchecked_Conversion (Address, fnAdaCallable);
Pointer2 : Address;
type fnInit is access procedure;
pragma Convention (Stdcall, fnInit);
function To_fnInit is new Ada.Unchecked_Conversion (Address, fnInit);
PointerInit : Address;
type fnFinal is access procedure;
pragma Convention (Stdcall, fnFinal);
function To_fnFinal is new Ada.Unchecked_Conversion (Address, fnFinal);
PointerFinal : Address;
Library : HANDLE;
begin
Library := LoadLibrary (To_C ("AdaCallable.dll"));
if Library /= 0 then
Pointer := GetProcAddress(Library, To_C("_fnAdaCallable#4"));
if Pointer /= Null_Address then
declare
result : Integer_32;
begin
result := To_fnAdaCallable(Pointer) (74);
Put_Line("Returned result is " & Integer_32'Image(result));
end;
else
Put_Line("GetProcAddress returned Null_Address");
end if;
else
Put_Line("LoadLibrary returned 0");
end if;
Library := LoadLibrary (To_C ("libDllBuiltFromAda.dll"));
if Library /= 0 then
PointerInit := GetProcAddress (Library, To_C ("DllBuiltFromAdainit"));
if Pointer /= Null_Address then
Put_Line("Calling Init");
To_fnInit (PointerInit); -- ****************************************
Put_Line("Returned from Init");
Pointer2 := GetProcAddress(Library, To_C("AdaCallable#4"));
if Pointer2 /= Null_Address then
declare
result : Integer_32;
begin
result := To_AdaCallable(Pointer2) (74);
Put_Line("Returned result is " & Integer_32'Image(result));
end;
else
Put_Line("GetProcAddress returned Null_Address");
end if;
PointerFinal := GetProcAddress (Library, To_C ("DllBuiltFromAdafinal"));
if Pointer /= Null_Address then
Put_Line("Calling Final");
To_fnFinal (PointerFinal);
Put_Line("Returned from Final");
else
Put_Line ("GetProcAddress for final returned Null_Address");
end if;
else
Put_Line ("GetProcAddress for Init returned Null_Address");
end if;
else
Put_Line("LoadLibrary returned 0");
end if;
Put_Line ("Hello, World!");
end SmallCode;
Given
type Proc_P is access procedure (X : Integer);
P : Proc_P;
you can write
P (42);
as a shorthand for
P.all (42);
but if there’s no argument list to trigger the shortcut you have to be explicit: given
type Parameterless_Proc_P is access procedure;
Q : Parameterless_Proc_P;
you have to call the procedure by writing
Q.all;
By the way, in the line two before your asterisked line, I think you mean PointerInit.
Related
Hello folks and sorry if this is a duplicate but my specific issue I haven't seen answered anywhere yet.
I have an "Invalid Pointer Operation" when I try to free an ObjectList created at runtime at the following lines:
Prods := TItemProcedimento.Create(DM.FDQ).lerProdutos;
Prods.DisposeOf; // <- Invalid Pointer Operation at 2nd iteration
So, here's my classes:
unit uItemProcedimento;
interface
[...]
type
TItemProcedimento = class
[...]
public
constructor Create(DataSet: TFDQuery);
function lerProdutos: TObjectList<TItemProcedimento>;
[...]
constructor TItemProcedimento.Create(DataSet: TFDQuery);
begin
FDataSet := DataSet;
end;
function TItemProcedimento.lerProdutos: TObjectList<TItemProcedimento>;
begin
Result := TObjectList<TItemProcedimento>.Create;
try
FDataSet.Close;
FDataSet.SQL.Clear;
FDataSet.SQL.Add('SELECT *');
FDataSet.SQL.Add('FROM Produto p');
FDataSet.SQL.Add('JOIN ItensProcedimento IP on p.PRO_ID = IP.PRO_ID');
FDataSet.SQL.Add('ORDER BY p.PRO_Nome');
FDataSet.Open;
while not FDataSet.Eof do
begin
PRO_ID := FDataSet.FieldByName('PRO_ID').AsInteger;
PRO_Rendimento := FDataSet.FieldByName('PRO_Rendimento').AsInteger;
PRO_Nome := FDataSet.FieldByName('PRO_Nome').AsString;
PRO_Tipo := FDataSet.FieldByName('PRO_Tipo').AsInteger;
PRO_Custo := FDataSet.FieldByName('PRO_Custo').AsFloat;
PRO_Potencia := FDataSet.FieldByName('PRO_Potencia').AsFloat;
IPR_Uso := FDataSet.FieldByName('IPR_Uso').AsFloat;
Result.Add(self);
FDataSet.Next;
end;
finally
FDataSet.Close;
end;
The weirdest thing about this is that I have another class with the exact same behaviour and the method works without issues. Yes I am creating the object and immediately destroying it to test if I'm destroying it correctly.
In another class I have another example with the same code, but this time it works without errors
test := TMyClass.Create(DM.FDQ).lerTeste;
test.DisposeOf;
Why? What am I doing wrong? This test code is running before the current code btw, maybe its related?
UPDATE:
By applying the changes that Remy Lebeau suggested I managed to add properly the items to the list and dispose them so this particular part of the code has no leaks. But in another part of the code I have an ObjectList leak that I have no idea on how to fix.
Inside my class I have a property that is a TObjectList property, I have a method that checks if the list is assigned, if not, it creates it and returns it to whoever is calling the list.
[...]
type
TProcedimento = class
private
[...]
FPRC_Produtos: TObjectList<TItemProcedimento>;
public
[...]
function getPRC_Produtos: TObjectList<TItemProcedimento>;
function criaProcedimentos: TObjectList<TProcedimento>;
[...]
function TProcedimento.GetPRC_Produtos: TObjectList<TItemProcedimento>;
begin
if not Assigned(FPRC_Produtos) then
FPRC_Produtos:= TObjectList<TItemProcedimento>.Create;
result := FPRC_Produtos;
end;
function TProcedimento.criaProcedimentos: TObjectList<TProcedimento>;
var
IPR: TItemProcedimento;
Procedimento: TProcedimento;
ds: TFDQuery;
begin
result := TObjectList<TProcedimento>.Create;
ds := TFDQuery.Create(nil);
ds.Connection := FDataSet.Connection;
IPR := TItemProcedimento.Create(ds);
try
FDataSet.Close;
FDataSet.Open('SELECT * FROM Procedimento');
while not FDataSet.Eof do
begin
Procedimento := TProcedimento.Create(FDataSet);
Procedimento.PRC_ID := FDataSet.FieldByName('PRC_ID').AsInteger;
Procedimento.PRC_Nome := FDataSet.FieldByName('PRC_Nome').AsString;
Procedimento.PRC_Duracao := FDataSet.FieldByName('PRC_Duracao')
.AsDateTime;
Procedimento.PRC_Preco := FDataSet.FieldByName('PRC_Preco').AsCurrency;
Procedimento.PRC_Custo := FDataSet.FieldByName('PRC_Custo').AsCurrency;
Procedimento.PRC_Consumo := FDataSet.FieldByName('PRC_Consumo').AsFloat;
Procedimento.FPRC_Produtos := IPR.getItensProcedimento(FPRC_ID);
result.Add(Procedimento);
FDataSet.Next;
end;
finally
FDataSet.Close;
IPR.DisposeOf;
ds.DisposeOf;
end;
end;
I then use this property in a for-in loop to feed a list with the procs in my database
procedure TKBForm1.CarregaProcedimento;
var
Procedimento: TProcedimento;
Procs: TObjectList<TProcedimento>;
[...]
begin
Procs := TProcedimento.Create(DM.FDQ).criaProcedimentos;
try
LV_Procedimento.Items.Clear;
LV_Procedimento.BeginUpdate;
for Procedimento in Procs do
begin
with LV_Procedimento.Items.Add do
[...]
finally
Procs.DisposeOf;
Procedimento.GetPRC_Produtos.DisposeOf;
end;
end;
But there's still leaks happening after this part runs:
73 - 88 bytes: TProcedimento x 1, TItemProcedimento x 2
How do I fix this?
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.