指定具有无效值的范围的惯用方式是什么?
What is the idiomatic way of specifying a range with an invalid value?
我经常发现我需要指定一个变量从具有某种物理意义(例如,SoC 上的特定内核)的范围中获取一个值。但我还需要能够将它设置为“none”,意思是“目前它没有持有真正的核心标识符”。为此,我通常使用下面给出的两种模式之一,但每种模式都有缺点:
第一个要求定义一个额外的(实际上是不必要的)类型。
第二个要求手动对齐两个字段(值和定义值是否有效的字段)。编译器无法检查此对齐方式。
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;
我经常发现我需要指定一个变量从具有某种物理意义(例如,SoC 上的特定内核)的范围中获取一个值。但我还需要能够将它设置为“none”,意思是“目前它没有持有真正的核心标识符”。为此,我通常使用下面给出的两种模式之一,但每种模式都有缺点:
第一个要求定义一个额外的(实际上是不必要的)类型。
第二个要求手动对齐两个字段(值和定义值是否有效的字段)。编译器无法检查此对齐方式。
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;