这真是个烦人的问题。我有一个包装各种基类型的记录类型,现在我需要它能够存储一个向量(来自Ada.Containers.Vectors)本身!我想这是不可能的,但任何人都可以给我建议如何以另一种方式解决这个问题?为了让您更好地了解我的目标,以下是不的工作原理:
with Base_Types; use Base_Types;
with Ada.Strings.Wide_Unbounded; use Ada.Strings.Wide_Unbounded;
with Ada.Containers.Vectors;
with Green_Tasks; use Green_Tasks;
with Ada.Unchecked_Deallocation;
package Boxed_Types is
type String_Ptr is access Unbounded_Wide_String;
procedure Free_Unbounded_Wide_String is new Ada.Unchecked_Deallocation
(Object => Unbounded_Wide_String, Name => String_Ptr);
type Vector_Ptr; -- this won't work
type Type_T is (T_Null, T_UInt64, T_Text, T_Bool, T_GTask, T_Vector);
type Item (IType : Type_T := T_Null) is 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 : Vector_Ptr; -- error here
end case;
end record;
package Item_Vectors is new Ada.Containers.Vectors
(Index_Type => Natural,
Element_Type => Item);
use Item_Vectors;
type Vector_Ptr is access Vector;
end Boxed_Types;
这给了我不太意外的错误“在Vector_Ptr完全声明之前无效使用类型”。但是,在我声明Item之前,我无法实例化vector包,我真的需要将vector和基类型包装成一个记录类型。 (这是我在业余时间写的解释器; VM必须在堆栈中存储各种不同类型,异构数组,操作它们等。)
我是否必须完全打破类型安全并乱用地址来访问转换,还是有更清洁的解决方案?
答案 0 :(得分:2)
这是一个不同的版本,它将Vector(不是它的访问权限)存储在Vector中。它的工作原理是使用继承,创建一个基类型的Vector。这意味着Indefinite_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;
此基类型具有可以存储在向量中的属性,并且其存储管理由Indefinite_Vectors处理。现在我们可以继承它,具有我们需要的特征。
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;
原始设计的一个特性已经消失:标记类型不允许使用默认判别式:这意味着您创建了具有明确判别式(因此具有确定大小!)的实例,并且以后无法对其进行修改(只需更换一个新的对象)。
另一个功能可能值得一提:Indefinite_Vectors可能会比其确定的表兄弟有性能损失:如果是这样,这是异构对象大小所产生的必要成本,并会以某种形式弹出,但是你解决问题。
通过为每种类型的Item创建不同的子类,也可以消除判别式Type_T;也许是一个更清洁的设计,但在这个阶段,它比你想要的更重构!
答案 1 :(得分:1)
我清理了一些东西,以下编译(如果你用绿色任务替换w / null;我没有Green_Tasks包),但我没有测试它。
with
Interfaces,
Green_Tasks,
Ada.Containers.Indefinite_Vectors,
Ada.Strings.Wide_Unbounded;
use
Green_Tasks,
Ada.Strings.Wide_Unbounded;
package Boxed_Types is
type Type_T is (T_Null, T_UInt64, T_Text, T_Bool, T_GTask, T_Vector);
type Item (IType : Type_T := T_Null) is private; -- Forward declaration;
private
type NNA_Item is Not Null Access Item;
package Item_Vectors is new Ada.Containers.Indefinite_Vectors
( Index_Type => Natural,
Element_Type => NNA_Item
);
type Item (IType : Type_T := T_Null) is record
case IType is
when T_Null => null;
when T_UInt64 => UInt64 : Interfaces.Unsigned_64;
when T_Text => String : Unbounded_Wide_String;
when T_Bool => Bool : Boolean;
when T_GTask => Green_Task : Green_Task_Ptr;
when T_Vector => Item_Vector : Item_Vectors.Vector;
end case;
end record;
end Boxed_Types;
答案 2 :(得分:0)
我怀疑Shark8有你想要的答案。而brian drummond刚刚给我发了一个类似的选项!
但是,作为一种完全不同的方法,您可以尝试(不编译,类似ada的伪代码):
FILE Boxed_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;
FILE:boxed_types.uint64.adb(或选择你自己明智的名字):
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;
这应该绕过包含Item的Item的问题,并且还有一个优点,即在对其数据字段进行操作之前不必询问该类型!