Erik Stens
Erik Stens

Reputation: 1807

In Ada, why doesn't a child get instantiated with the generic parent and why do I have to make it generic as well?

I've got a parent package with some generic interface. I now want to create several implementations of this interface, each in a different file. I thought I could simply make these packages children to the package that contains the interface, instantiate the generic and then directly access the child packages, but this gives me an error that the child packages must be generic as well.

This lead me to the following implementation:

parent.ads:

generic
   type T is private;

package Parent is

   type I_Generic is interface;
   type Any_Generic is access all I_Generic'Class;

   function Get (This : in out I_Generic) return T is abstract;
   procedure Set (This : in out I_Generic; Value : T) is abstract;

end Parent;

parent-child.ads:

generic
package Parent.Child is

   --  long spec

   type Impl is new I_Generic with private;
   type Impl_Access is access all Impl;
   
   overriding function Get (This : in out Impl) return T;
   overriding procedure Set (This : in out Impl; Value : T);

private

   type Impl is new I_Generic with
      record
         Data : T;
      end record;

end Parent.Child;

parent-child.adb:

package body Parent.Child is

   --  long body
   
   overriding
   function Get (This : in out Impl) return T is
   begin
      return This.Data;
   end Get;

   overriding
   procedure Set (This : in out Impl; Value : T) is
   begin
      This.Data := Value;
   end Set;

end Parent.Child;

tester.adb:

with Ada.Text_IO;
with Parent;
with Parent.Child;

package body Tester is

   package Parent_Inst is new Parent (T => Integer);
   package Child_Inst is new Parent_Inst.Child;
   
   procedure Test is
      Instance : constant Child_Inst.Impl_Access := new Child_Inst.Impl;
      Polymorphic : constant Parent_Inst.Any_Generic := Parent_Inst.Any_Generic (Instance);
   begin
      Instance.Set (42);
      Ada.Text_IO.Put_Line (Polymorphic.Get'Img);
   end Test;

end Tester;

Result:

42

Why do I need to make the child package generic and then make an instance of it first? Why can't I simply use Instance : Parent_Inst.Child.Impl_Access := new Parent_Inst.Child.Impl;?

Is there any way that I can do this cleaner? Perhaps I'm overlooking a different solution to my requirement that is simpler and doesn't have this problem? Or is this simply the way to achieve what I'm describing and should I just accept the verbosity of the extra package instantiation? In my own code I now have to make several package instantiations for each of the interface implementation packages, which results in many extra lines of code.

Upvotes: 5

Views: 476

Answers (1)

flyx
flyx

Reputation: 39668

The child packages must be generic because of LRM 10.1.1, 17/3:

A child of a generic library package shall either be itself a generic unit or be a renaming of some other child of the same generic unit.

This is necessary because the child package can access the value of the generic parameter of the parent unit, which does not exist unless the parent unit is instantiated.

Now in Ada, generic instantiation is explicit and each instantiation instantiates exactly one generic unit. In your case,

package Parent_Inst is new Parent (T => Integer);

instantiates the Parent package. It does not instantiate Parent.Child because that is a separate generic unit. Therefore, you do need to instantiate Child separately.

You can write a helper package that instantiates all at once, e.g.

generic
   type T is private;
package Everything is
   package Parent_Inst is new Parent (T);
   package Child_Inst is new Parent_Inst.Child;
end Everything;

And then just instantiate Everything where you need the instances.

Upvotes: 6

Related Questions