Generics 包含对基于该记录的通用包实例化的访问的记录

Generics 包含对基于该记录的通用包实例化的访问的记录,generics,containers,record,ada,forward-declaration,Generics,Containers,Record,Ada,Forward Declaration,这真是个烦人的问题。我有一个记录类型,它包装了各种基本类型,现在我需要它能够存储一个向量(来自Ada.Containers.Vectors)本身!我想这是不可能的,但有人能给我一些建议,告诉我如何用另一种方式解决这个问题吗?为了让你更好地了解我在做什么,以下是不可行的: 这给了我一个不太意外的错误,Vector_Ptr的“在完全声明之前类型的使用无效”。但是,在声明项之前,我也无法实例化vector包,我确实需要将vector和基类型包装到一个记录类型中。(这是我在业余时间编写的解释器;VM必须

这真是个烦人的问题。我有一个记录类型,它包装了各种基本类型,现在我需要它能够存储一个向量(来自Ada.Containers.Vectors)本身!我想这是不可能的,但有人能给我一些建议,告诉我如何用另一种方式解决这个问题吗?为了让你更好地了解我在做什么,以下是不可行的:

这给了我一个不太意外的错误,Vector_Ptr的“在完全声明之前类型的使用无效”。但是,在声明项之前,我也无法实例化vector包,我确实需要将vector和基类型包装到一个记录类型中。(这是我在业余时间编写的解释器;VM必须在堆栈上以异构数组的形式存储各种不同的类型,并对它们进行操作,等等)


我是否必须完全破坏类型安全,处理地址到访问的转换,还是有更干净的解决方案?

我清理了一些东西,下面的编译(如果你用空替换绿色任务;我没有绿色任务包),但我没有测试它

与
接口,
绿色任务,
Ada.Containers.unfinite_向量,
Ada.Strings.Wide_无界;
使用
绿色任务,
Ada.Strings.Wide_无界;
包装盒类型为
类型类型是(T_Null、T_UInt64、T_Text、T_Bool、T_GTask、T_Vector);
类型项(IType:type\U T:=T\U Null)是私有的;——远期申报;
私有的
类型NNA_项不是空的访问项;
包装项目_向量是新的Ada.Containers.unfinite_向量
(索引类型=>自然,
元素类型=>NNA\U项
);    
类型项(IType:type\U T:=T\U Null)为记录
案例类型为
当T_Null=>Null时;
当T_UInt64=>UInt64:Interfaces.Unsigned_64;
当T_Text=>String时:无界_宽_字符串;
当T_Bool=>Bool:Boolean;
当T_GTask=>Green_Task:Green_Task_Ptr;
当T_Vector=>Item_Vector时:Item_Vectors.Vector;
终例;
结束记录;
端盒型;

这里有一个不同的版本,它将项目(而不是其访问)存储在向量中。它通过使用继承来工作,创建一个基类型的向量。这意味着一个不确定的_向量,因为每个单独组件的大小事先不知道

同样,已编译但未经测试

with Ada.Containers.Indefinite_Vectors;

package Boxed_Base is

   type Base_Item is tagged record
      null;
   end record;

   package Item_Vectors is new Ada.Containers.Indefinite_Vectors
     (Index_Type   => Natural,
      Element_Type => Base_Item'Class);
   use Item_Vectors;

   type Vector_Ptr is access Vector;

end Boxed_Base;
此基类型具有可以存储在向量中的属性,并且其存储管理由不确定的_向量处理。现在我们可以继承它,具有我们需要的特征

with Ada.Strings.Wide_Unbounded; use Ada.Strings.Wide_Unbounded;
with Ada.Unchecked_Deallocation;
with Boxed_Base;

package Boxed_Types is

   type UInteger_64 is new integer;
   type Green_Task_Ptr is access UInteger_64;
   -- these two because original testcase was incomplete

   type String_Ptr is access Unbounded_Wide_String;
   type Type_T is (T_Null, T_UInt64, T_Text, T_Bool, T_GTask, T_Vector);

   type Item (IType : Type_T ) is new Boxed_Base.Base_Item with record
      case IType is
         when T_Null   => null;
         when T_UInt64 => UInt64      : UInteger_64;
         when T_Text   => String      : String_Ptr;
         when T_Bool   => Bool        : Boolean;
         when T_GTask  => Green_Task  : Green_Task_Ptr;
         when T_Vector => Item_Vector : Boxed_Base.Vector_Ptr;    
      end case;
   end record;

end Boxed_Types;
原始设计的一个特性已经消失:默认的判别式不允许用于标记类型:这意味着您创建了一个具有明确判别式(因此具有明确的大小!)的实例,并且以后无法对其进行修改(只需将对象替换为新对象)

另一个特性可能值得一提:不确定的_向量可能比其确定的近亲有性能损失:如果是这样,这是由异构对象大小引起的必要成本,并且将以某种形式出现,不管您如何分割问题


还可以通过为每种类型的项目创建不同的子类来消除判别类型;也许是一个更干净的设计,但在这个阶段,重构可能比你想要的要多

我怀疑Shark8找到了你想要的答案。brian drummond刚刚发布了一个类似于我的选项

但是,作为一种完全不同的方法,您可以尝试(不编译,类似于ada的伪代码):

文件装箱_Types.ads:

type item is tagged null record;

type item_ptr is access all item'Class;

package Item_Vectors is new Ada.Containers.Vectors
  ( Index_Type   => Natural,
    Element_Type => item_ptr -- Actually you may have to wrap this in a record type and possibly make it a controlled type.
  );    

procedure foo (object : in item'Class) is abstract;
文件:boxed_types.uint64.adb(或选择您自己的合理名称):

对原始记录中的其他元素重复此操作

这意味着您可以声明类范围的对象并使用动态调度:

declare
   Obj : Boxed_Types.Item'Class := ...; 
begin
   Boxed_Types.foo; -- dynamic dispatching
end;

这应该可以绕过包含Item的Item问题,并且还有一个进一步的优势,即在对其数据字段执行操作之前不必查询类型

这与我可能会尝试的方法大致相同,但不确定_向量的要点是不需要访问类型。如果直接用Item实例化会发生什么?(对不起,我手头没有编译器,无法尝试)您不能;禁止在不过早使用的情况下实例化它。我将测试所有三个建议的性能影响,并将此标记为答案,因为它给出了最详细的解释。所有的答案都对我很有帮助。谢谢!
type T_uint64 is new item with record
  UInt64      : Interfaces.Unsigned_64;
end record;

procedure foo (object : in T_uint64);
declare
   Obj : Boxed_Types.Item'Class := ...; 
begin
   Boxed_Types.foo; -- dynamic dispatching
end;