【问题标题】:In Ada, why doesn't a child get instantiated with the generic parent and why do I have to make it generic as well?在 Ada 中,为什么不使用通用父级实例化子级,为什么我也必须使其通用?
【发布时间】:2021-01-05 22:21:03
【问题描述】:

我有一个带有一些通用接口的父包。我现在想创建这个接口的几个实现,每个实现都在不同的文件中。我以为我可以简单地将这些包作为包含接口的包的子包,实例化泛型,然后直接访问子包,但这给了我一个错误,即子包也必须是泛型的。

这导致我进行以下实现:

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;

结果:

42

为什么我需要先将子包设为通用,然后再创建它的实例?为什么我不能简单地使用Instance : Parent_Inst.Child.Impl_Access := new Parent_Inst.Child.Impl;

有什么方法可以让我更清洁吗?也许我忽略了一个不同的解决方案来满足我的要求,它更简单并且没有这个问题?或者这仅仅是实现我所描述的方式,我应该接受额外包实例化的冗长吗?在我自己的代码中,我现在必须为每个接口实现包创建几个包实例化,这会导致很多额外的代码行。

【问题讨论】:

  • 子包是通用的似乎很自然,因为它明确使用通用参数T
  • 我同意,但是因为我已经在创建一个已经填写了相同参数 T 的父包的实例,所以我想知道为什么我仍然需要创建子包的实例,而不任何附加参数,以便能够访问子包内的对象。难道没有某种方法可以在一个实例中实现所有这些吗?我可以想象如果实现是嵌套包我不会有这个问题,但是我必须将所有实现放在父级中。我希望它们是单独的文件。
  • 我认为 Ada 允许在多个单独的文件中实现包。见How do I use “separate" keyword

标签: generics package instance ada


【解决方案1】:

由于LRM 10.1.1, 17/3,子包必须是通用的:

一个通用库包的子级要么本身就是一个通用单元,要么是同一通用单元的某个其他子级的重命名。

这是必要的,因为子包可以访问父单元的通用参数的值,除非父单元被实例化,否则该参数不存在。

现在在 Ada 中,泛型实例化是显式的,并且每个实例化恰好实例化一个泛型单元。在你的情况下,

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

实例化Parent 包。它确实实例化Parent.Child,因为那是一个单独的通用单元。因此,您确实需要单独实例化Child

您可以编写一个一次性实例化所有的帮助程序包,例如

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;

然后只需在需要实例的地方实例化 Everything

【讨论】:

  • 这很好地解释了为什么我需要创建单独的实例,谢谢!此外,该帮助程序包解决了我的冗长问题。我确实必须在此处添加具体的实现,但由于这些在 99% 的情况下都是相同的,所以它让一切变得更干净,再次感谢 :)
猜你喜欢
  • 1970-01-01
  • 2015-12-29
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2021-08-09
  • 2021-05-06
  • 2021-12-22
  • 2015-03-09
相关资源
最近更新 更多