通过指针使用变体记录

时间:2016-02-02 13:30:42

标签: ada

我根本不明白为什么以下不起作用。有人可以帮我解决吗?它抱怨(在运行时):

引发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)没有

提前致谢!

2 个答案:

答案 0 :(得分:4)

RM明确指出“如果指定的类型是复合的,则[...]创建的对象受其初始值的约束(即使指定的子类型不受默认值限制)。” (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;

答案 1 :(得分:0)

正如egilhh所说,当您使用new分配判别记录时,您无法更改所分配记录的判别式,即使您可以为变量执行此操作类型(与分配的记录相对)。自Ada 83以来,这个规则已经存在。我认为,理由是它允许编译器在分配记录时优化空间。在您的示例中,如果我们假设所有字段(包括判别式)都是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

这些都可以,因为分配的记录是一辆车,这不是一个判别记录。可以改变像这样的子组件的判别式。这就是我绕过规则的方式。