通过指针使用变体记录
Using a Variant Record by Pointer
我根本不明白为什么以下内容不起作用。有人可以帮我解决吗?它抱怨(在运行时):
提高 CONSTRAINT_ERROR : variant2.adb:21 判别检查失败
procedure Variant2 is
type POWER is (NONE,GAS, STEAM);
type VEHICLE (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
Works : VEHICLE;
Works_Not : access VEHICLE := new VEHICLE;
begin
Works := (GAS,1980,4); -- (1)
Works_Not.all := (GAS,1981,8); -- (2)
end Variant2;
(1) 有效,但 (2) 无效
提前致谢!
RM 明确指出 "If the designated type is composite, [...] the created object is constrained by its initial value (even if the designated subtype is unconstrained with defaults)." (RM 4.8(6/3))
这意味着您必须重新分配您的访问类型
Works_Not := new VEHICLE'(GAS,1981,8);
(当然,你应该先释放旧的访问值(见RM 13.11.2 Unchecked Storage Deallocation),但我把它留作练习)
更新:如评论中所述
这是一个您可以尝试的示例:
with Ada.Text_IO;
procedure Array_Of_Aliased is
type POWER is (NONE, GAS, STEAM);
type VEHICLE(Engine : POWER := NONE) is
record
Model_Year : Integer range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
-- array of aliased elements
type Vehicle_Array is array(1..5) of aliased VEHICLE;
-- the access type need to be "all" or "constant" in order to access aliased values
type Vehicle_Access is access all VEHICLE;
Vehicles : Vehicle_Array;
Works : Vehicle_Access;
begin
-- access to the first element of the array. Can't change discriminant this way...
Works := Vehicles(1)'Access;
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- However, using the array, we _can_ change the discriminant, since it's _not_ an access value
Vehicles(1) := (STEAM, 1890, 20, True);
Vehicles(2) := (GAS, 1981, 8);
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- We can still update the record elements using the access value, as long as the discriminant stays the same
Works.all := (STEAM, 1900, 15, False);
end Array_Of_Aliased;
正如 egilhh 所说,当您使用 new
分配判别式记录时,您无法更改您分配的记录的判别式,即使您可以为 变量 的类型(与分配的记录相反)。这条规则自 Ada 83 以来一直存在。我认为,其基本原理是它允许编译器在分配记录时优化 space。在您的示例中,如果我们假设所有字段(包括判别式)都是 1 个词,那么如果 ENGINE=NONE 则记录将是 2 个词,如果 ENGINE=GAS 则为 3 个词,如果 ENGINE=STEAM 则为 4 个词。当 Works_Not
被初始化时,它被初始化为一个 NONE
,这意味着它可能只占用堆上的 2 个字(注意:编译器不需要以这种方式优化)。如果它只使用 2 个字,那么将记录重新分配给一个带有 ENGINE=GAS 的记录将是一场灾难——你会溢出你之前分配的区域,并踩踏其他数据。
这是否是一个好的语言设计决定,我不能说;我不知道需要多少编译器和多少应用程序才能利用此优化。 33 年前有人认为它会有用,他们一定有充分的理由这样想。
这个限制很烦人,但并非无法克服。我肯定 运行 之前多次使用它,但简单的答案是将它包装在另一条记录中。
type VEHICLE_DATA (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
type VEHICLE is record
Data : VEHICLE_DATA;
end record;
Now_Works : access VEHICLE := new VEHICLE; -- still sets ENGINE=NONE
Now_Works := (Data => (Gas, 1981, 8)); -- legal
Now_Works.Data := (Gas, 1981, 8); -- legal, does the same thing
这些都可以,因为分配的记录是 VEHICLE,它不是判别记录。像这样更改子组件的判别式就可以了。我就是这样绕过规则的。
我根本不明白为什么以下内容不起作用。有人可以帮我解决吗?它抱怨(在运行时):
提高 CONSTRAINT_ERROR : variant2.adb:21 判别检查失败
procedure Variant2 is
type POWER is (NONE,GAS, STEAM);
type VEHICLE (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
Works : VEHICLE;
Works_Not : access VEHICLE := new VEHICLE;
begin
Works := (GAS,1980,4); -- (1)
Works_Not.all := (GAS,1981,8); -- (2)
end Variant2;
(1) 有效,但 (2) 无效
提前致谢!
RM 明确指出 "If the designated type is composite, [...] the created object is constrained by its initial value (even if the designated subtype is unconstrained with defaults)." (RM 4.8(6/3))
这意味着您必须重新分配您的访问类型
Works_Not := new VEHICLE'(GAS,1981,8);
(当然,你应该先释放旧的访问值(见RM 13.11.2 Unchecked Storage Deallocation),但我把它留作练习)
更新:如评论中所述
这是一个您可以尝试的示例:
with Ada.Text_IO;
procedure Array_Of_Aliased is
type POWER is (NONE, GAS, STEAM);
type VEHICLE(Engine : POWER := NONE) is
record
Model_Year : Integer range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
-- array of aliased elements
type Vehicle_Array is array(1..5) of aliased VEHICLE;
-- the access type need to be "all" or "constant" in order to access aliased values
type Vehicle_Access is access all VEHICLE;
Vehicles : Vehicle_Array;
Works : Vehicle_Access;
begin
-- access to the first element of the array. Can't change discriminant this way...
Works := Vehicles(1)'Access;
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- However, using the array, we _can_ change the discriminant, since it's _not_ an access value
Vehicles(1) := (STEAM, 1890, 20, True);
Vehicles(2) := (GAS, 1981, 8);
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- We can still update the record elements using the access value, as long as the discriminant stays the same
Works.all := (STEAM, 1900, 15, False);
end Array_Of_Aliased;
正如 egilhh 所说,当您使用 new
分配判别式记录时,您无法更改您分配的记录的判别式,即使您可以为 变量 的类型(与分配的记录相反)。这条规则自 Ada 83 以来一直存在。我认为,其基本原理是它允许编译器在分配记录时优化 space。在您的示例中,如果我们假设所有字段(包括判别式)都是 1 个词,那么如果 ENGINE=NONE 则记录将是 2 个词,如果 ENGINE=GAS 则为 3 个词,如果 ENGINE=STEAM 则为 4 个词。当 Works_Not
被初始化时,它被初始化为一个 NONE
,这意味着它可能只占用堆上的 2 个字(注意:编译器不需要以这种方式优化)。如果它只使用 2 个字,那么将记录重新分配给一个带有 ENGINE=GAS 的记录将是一场灾难——你会溢出你之前分配的区域,并踩踏其他数据。
这是否是一个好的语言设计决定,我不能说;我不知道需要多少编译器和多少应用程序才能利用此优化。 33 年前有人认为它会有用,他们一定有充分的理由这样想。
这个限制很烦人,但并非无法克服。我肯定 运行 之前多次使用它,但简单的答案是将它包装在另一条记录中。
type VEHICLE_DATA (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
type VEHICLE is record
Data : VEHICLE_DATA;
end record;
Now_Works : access VEHICLE := new VEHICLE; -- still sets ENGINE=NONE
Now_Works := (Data => (Gas, 1981, 8)); -- legal
Now_Works.Data := (Gas, 1981, 8); -- legal, does the same thing
这些都可以,因为分配的记录是 VEHICLE,它不是判别记录。像这样更改子组件的判别式就可以了。我就是这样绕过规则的。