I'm facing a compilation error where I'm not sure about what is going on.
I provide a minimal reproducibable example; if I implement the Initialize procedure, everything is ok and no errors are thrown. The same occurs if class Derived_t has no Hashed_Maps instantiation component, which it's full view is a controlled type and I don't know if it could be related.
with Ada.Finalization;
private with Ada.Containers.Hashed_Maps;
private with Ada.Unchecked_Deallocation;
package root is
type Root_t is limited interface;
type Derived_t is abstract limited new Ada.Finalization.Limited_Controlled and
Root_t with private;
overriding
procedure Initialize (this : in out Derived_t) is abstract; --if null implementation is provided, no error is raised
overriding
procedure Finalize (this : in out Derived_t);
private
type array_t is array (Positive range <>) of Positive;
type arrayPtr_t is access array_t;
function fHash (key : Positive) return Ada.Containers.Hash_Type is
(Ada.Containers.Hash_Type(key));
package HashedDict_pck is new Ada.Containers.Hashed_Maps
(Key_Type => Positive,
Element_Type => Positive,
Hash => fHash,
Equivalent_Keys => "=");
--The compilation error is raised before name Derived_t
type Derived_t is abstract limited new Ada.Finalization.Limited_Controlled and
Root_t with
record
arrayMember : arrayPtr_t;
dict : HashedDict_pck.map;
end record;
procedure pFreeArray is new Ada.Unchecked_Deallocation
(array_t, arrayPtr_t);
end root;
package body root is
--------------
-- Finalize --
--------------
overriding procedure Finalize (this : in out Derived_t) is
begin
this.dict.Clear;
pFreeArray(this.arrayMember);
end Finalize;
end root;
The compilation error occurs at line 33 column 8, just before entity name Derived_t in the private part: call to abstract procedure must be dispatching.
I know that controlled types are usually implemented privately, to hide the procedures to clients; I decided to make it public to enforce implementers of concrete classes to implement the Initialize procedure.
An implementer would do the following to implement a concrete class:
package root.concrete is
type Concrete_t is new Derived_t with private;
overriding
procedure Initialize(this : in out Concrete_t); --Implementation will initialize Derived_t arrayMember
--other Concrete_t public primitives
private
type Concrete_t is new Derived_t with
record
--Some stuff
end record;
end root.concrete;
I think that, as I declare the map as a member record, and as the map is a controlled type, it needs to initialise its enclosing record but, as the Initialize is abstract, the compiler don't know what to call. Am I right?
I'll privatize the use of controlled types with a null default implementation of the Initialize to fix it, anyway an implementer shall implement the procedure for a concrete class to construct it correctly.
I think that, as I declare the map as a member record, and as the map is a controlled type, it needs to initialise its enclosing record but, as the Initialize is abstract, the compiler don't know what to call. Am I right?
Mostly on the right track. IIUC, what you'll need to do is an extension aggregate -- like this:
overriding
procedure Initialize(this : in out Concrete_t) is
Function Default return HashedDict_pck.map is separate;
Begin
This := Concrete_t'( Derived_t'(dict => Default, arrayMember => <>) with others => <> );
End Initialize;
Another possibility is to use a function to construct your initial value and use that.
Count : Positive:= 1;
Procedure Nested_Initialize(This : out Concrete_t) is
Function Initialize return Concrete_t is
Begin
Return Result : Concrete_t:=
Concrete_t'( Derived_t'(others => <>) with others => <> ) do
Result.Value:= Count;
Count:= Positive'Succ( Count );
End return;
End Initialize;
Begin
This:= Initialize;
End Nested_Initialize;