指定具有无效值的范围的惯用方式是什么?

What is the idiomatic way of specifying a range with an invalid value?

我经常发现我需要指定一个变量从具有某种物理意义(例如,SoC 上的特定内核)的范围中获取一个值。但我还需要能够将它设置为“none”,意思是“目前它没有持有真正的核心标识符”。为此,我通常使用下面给出的两种模式之一,但每种模式都有缺点:

  1. 第一个要求定义一个额外的(实际上是不必要的)类型。

  2. 第二个要求手动对齐两个字段(值和定义值是否有效的字段)。编译器无法检查此对齐方式。

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is

  --  We want to specify a Unit Identifier in the range 1 .. 10.
  --  However, sometimes we need to say that the Unit Identifier
  --  being stored is invalid.
  --
  --  The first way of doing this is to define an extended range
  --  and an Invalid Identifier.

  type Extended_Core_Identifier is new Natural range 0 .. 10;
  subtype Core_Identifier is Extended_Core_Identifier range 1 .. 10;

  Invalid_Core : constant Extended_Core_Identifier := 0;

  --  The second way is to define the range to be what we actually
  --  want and then define a Boolean to say whether the value is valid.

  type Second_Try_Identifier is new Natural range 1 .. 10;
  Identifier_Is_Valid : Boolean := False;
  Identifier1 : Extended_Core_Identifier := Invalid_Core;
  Identifier2 : Second_Try_Identifier    := 6;

begin
  if Identifier1 = Invalid_Core then
    Put_Line ("Identifier1 does not hold a real core");
  end if;

  if not Identifier_Is_Valid then
    Put_Line ("Identifier2 is not a real core");
  end if;

end Main;

我喜欢在这种情况下使用 variant record。它允许您指定仅当 Valid 判别式设置为 True 时才存在 ID。它还允许 Ada 的强类型检查来强制执行该不变性(即,如果您尝试访问无效核心的 ID,代码将引发异常)。

with Ada.Text_IO; use Ada.Text_IO;

procedure Hello is

    type Core_Identifier is new Positive range 1 .. 10;

    type Core (Valid : Boolean := False) is record
        case Valid is
            when True  => ID : Core_Identifier := 1;
            when False => null;
        end case;
    end record;
    
    Invalid_Core : constant Core := (Valid => False);
    
    -- Constructing function
    function Make_Core(ID : Core_Identifier) return Core
        is (Valid => True, ID => ID);
    
    -- Various initialization methods
    Core_1 : Core := Invalid_Core;
    Core_2 : Core := (Valid => True, ID => 6);
    Core_3 : Core := Make_Core(9);
    Core_4 : Core := (Valid => False);

begin
    Put_Line("Hello, world!");
  
    if not Core_1.Valid then
        Put_Line("Core 1 invalid");
    else
        Put_Line("Core 1 ID:" & Core_1.ID'Image);
    end if;
    
    if not Core_2.Valid then
        Put_Line("Core 2 invalid");
    else
        Put_Line("Core 2 ID:" & Core_2.ID'Image);
    end if;
    
    if not Core_3.Valid then
        Put_Line("Core 3 invalid");
    else
        Put_Line("Core 3 ID:" & Core_3.ID'Image);
    end if;
    
    if not Core_4.Valid then
        Put_Line("Core 4 invalid");
    else
        Put_Line("Core 4 ID:" & Core_4.ID'Image);
    end if;
    
end Hello;