Generics Ada:泛型、不完整类型和自引用结构的困难

Generics Ada:泛型、不完整类型和自引用结构的困难,generics,package,packages,ada,Generics,Package,Packages,Ada,在Ada中,这样做很简单: type ITEM_RECORD; type ITEM_ACCESS is access ITEM_RECORD; type ITEM_RECORD Is record ITEM: item_type; Next: item_access; Pred: item_access; end record; 简单,对吗?现在,如果我想让ITEM_ACCESS成为在通用包中声明的智能/安全指针,该怎么办?我直觉上是这样做的,只要它起作用: type ITE

在Ada中,这样做很简单:

type ITEM_RECORD;
type ITEM_ACCESS is access ITEM_RECORD;
type ITEM_RECORD Is
record
   ITEM: item_type;
   Next: item_access;
   Pred: item_access;
end record;
简单,对吗?现在,如果我想让ITEM_ACCESS成为在通用包中声明的智能/安全指针,该怎么办?我直觉上是这样做的,只要它起作用:

type ITEM_access;
type Item_Record is record
  Item : Item_Type;
  Next : Item_Access;
  Pred : Item_Access;
end record;
type pointers_on_record is access Item_record;
package pointers_p is new pointers(Item_Record, pointers_on_record);
type item_access is new pointers_p.Pointer_Type;
通用标准的规范如下:

generic
   type Item_Type(<>) is limited private;
   type Access_Type is access Item_Type;
package Pointers is
   type Pointer_Type is private;
通用
类型项_type()是有限的私有类型;
类型访问\类型为访问项\类型;
包指针是
类型指针\u类型为私有类型;
我还不知道怎么做


谢谢

由于循环依赖关系,无法创建要创建的结构。想想看:

泛型包定义一个指针。
指针类型
(可能)的结构和实现取决于通用参数
项类型
(这不一定是真的,但如果不是真的,则不需要将
项类型
作为通用参数)。现在,泛型包实例化中的
Item\u Type
包含来自泛型包的两个智能指针,因此取决于
Pointer\u Type
的结构。这是一个典型的鸡或蛋的问题

因此,解决方案是更改类型的设计。让我给你一些建议(没有双关语):

看起来您正在实现一个双链接列表。请注意,由于列表的循环性质,使用实现引用计数的智能指针是一个严重的错误。如果您的列表中至少有两个项目,则不会解除分配任何内容,因为它们始终指向对方。因此,除非您的智能指针正在执行循环检测(根据您的规范,这是不可能的),否则您不能使用智能指针 按您想要的方式指示

一个可能的解决方案是使用智能指针指向
项目类型
,而不是记录。您将需要手动取消分配记录,但仍需要按照上面的说明执行此操作

另一种解决方案是为整个列表设置一个全局引用计数器。创建一个不透明的列表类型,该类型为列表提供访问器和迭代器子例程,这些子例程向项目发出智能指针。智能指针增加和减少整个列表上的引用计数,一旦最后一个对列表的引用消失,整个列表将被释放。因此,只要列表中至少有一个引用存在,列表就存在。此解决方案需要您自己实现引用计数,因为它专门用于列表结构


最后,您当然可以使用
Ada.Containers.double\u Linked\u list
,就像Jeffrey建议的那样。正如我在第一个解决方案中所建议的那样,您可以将智能指针放在您的
项目类型中。

要使用智能指针,您的智能指针必须使用不完整的类型。因此,您还必须提供一个删除访问变量(当然还有访问类型)的终结过程。当然,这也意味着您的分配函数需要采用访问类型而不是变量。最后,您绝对需要使用弱指针来打破由引用计数智能指针生成的循环引用

generic
   type Item_Type(<>);
   type Item_Access is access Item_Type;
   with procedure Finalize(Ref : in out Item_Access);
package Pointers is
   function Make(Ref : not null Item_Access) return Smart_Pointer;
   -- other stuff
end Pointers;
下面是一个示例规范,我使用它创建了一个带有智能指针的AVL树。我手头没有链表示例

package Trees is
   type Node;
   type Node_Access is access Node;
   procedure Finalize(Memory : in out Node_Access);

   package Node_Smart_Access is new Smart_Access
      (Item_Type        => Node,
       Item_Access      => Node_Access,
       Finalize         => Finalize,
       Atomic_Increment => True);

   type Node is record
      Value  : Integer := 0;
      Height : Integer := 1;
      Parent : Node_Smart_Access.Weak_Access;
      Left   : Node_Smart_Access.Shared_Access;
      Right  : Node_Smart_Access.Shared_Access;
   end record;

   type Tree is tagged record
      Root : Node_Smart_Access.Shared_Access;
   end record;

end Trees;
我的智能指针规范是:

generic
   type Item_Type(<>);
   type Item_Access is access Item_Type;
   with procedure Finalize(Memory : in out Item_Access);
   Atomic_Increment : Boolean := True;
package Smart_Access is

   type Shared_Access is new Ada.Finalization.Controlled with private;
   type Weak_Access is new Ada.Finalization.Controlled with private;

   -- more stuff

   package Make is
      function Shared_Access
         (Source : in not null Item_Access)
          return Smart_Access.Shared_Access;
      -- more stuff
   end Make;
private
   -- implementation
end Smart_Access;
通用
输入Item_type();
类型项访问为访问项类型;
带程序完成(内存:输入输出项访问);
原子增量:布尔:=真;
软件包智能访问是
类型Shared_Access是新的Ada.Finalization.Controlled with private;
类型弱_访问是新的Ada.Finalization.Controlled with private;
--更多的东西
包装制造是
功能共享访问
(来源:在非空项_访问中)
返回智能访问。共享访问;
--更多的东西
成品;
私有的
--实施
终端智能接入;

这很麻烦,但如果您想在Ada中使用智能指针创建自引用类型,则需要这样做。还请注意,如果在智能指针的规范中使用不完整的类型,则GNAT的某些版本会出现隐式解引用方面的编译器错误。如果您使用的版本存在错误,则在编译时,它们将导致编译器崩溃。

是否正在创建双链接列表?为什么?Ada.Containers.Double链接列表有什么问题?错误是什么?因此,正如预期的那样,解决方案是。。。你不能。不知何故,这并不让我感到惊讶:-D。你的主要观点是,在这里放置智能指针是一个设计错误。这使我信服了。我没有使用标准容器,因为这是一个书本练习,你被告知要把它们写成容器;-)我只是想知道我到底错在哪里。我不知道智能指针的设计是否会根据它所使用的数据结构而改变。但这显然是有道理的。太棒了。我开始理解不完整的泛型参数是用来做什么的。实际上,客户机可能会将其实例化为有限的不确定类型,因为任何类型(不包括不完全类型)都可以与未经检查的释放一起使用。您的示例之所以有效,是因为访问类型的大小始终相同(等等……从技术上讲,这不是假的吗?根据类型,边界可以存储到指针中,对吗?泛型如何处理这一事实?该示例适用于任何类型。编译器不需要知道访问类型的大小,直到包被实例化,然后它才知道。访问类型c是正确的一个类型有不同的大小,但对于泛型形式,它在实例化之前并不关心实际大小(正如我所说的,到那时它就知道了)。编译器在实例化之前不会尝试构建任何类型。
generic
   type Item_Type(<>);
   type Item_Access is access Item_Type;
   with procedure Finalize(Memory : in out Item_Access);
   Atomic_Increment : Boolean := True;
package Smart_Access is

   type Shared_Access is new Ada.Finalization.Controlled with private;
   type Weak_Access is new Ada.Finalization.Controlled with private;

   -- more stuff

   package Make is
      function Shared_Access
         (Source : in not null Item_Access)
          return Smart_Access.Shared_Access;
      -- more stuff
   end Make;
private
   -- implementation
end Smart_Access;