【发布时间】:2012-03-19 05:18:03
【问题描述】:
我需要在 Ada 中获取与枚举类型的值相关联的数值。不是枚举中的位置,而是使用“for TYPE use”子句分配给每个值的值。
有谁知道这是否可行?
【问题讨论】:
标签: enumeration ada
我需要在 Ada 中获取与枚举类型的值相关联的数值。不是枚举中的位置,而是使用“for TYPE use”子句分配给每个值的值。
有谁知道这是否可行?
【问题讨论】:
标签: enumeration ada
目前没有完全通用的解决方案。枚举表示条款似乎旨在使这些信息难以获得。 (但是 Ada 2020 将添加一个解决方案;有关详细信息,请参阅此答案的底部。)
这个:
function Rep is new Ada.Unchecked_Conversion(Enum, Integer);
可能在大多数情况下都有效,但有一些严重的警告:表示值必须在Integer'First..Integer'Last 范围内,如果Enum 和Integer 的大小不匹配,则结果为实际定义的实现(但它适用于 GNAT)。
正如 Simon Wright 所说,RM 推荐 Unchecked_Conversion,但这不是一个非常令人满意的解决方案,并且很难确定一致的目标类型。
从 2007 RM 开始,推荐的支持级别为:
一个实现应该至少支持 范围 System.Min_Int..System.Max_Int.
这意味着转换为Integer 并不总是足够的;值可能小于Integer'First,或大于Integer'Last。即使所有值都在该范围内,也没有真正好的方法来确定与枚举类型大小相同的目标类型。例如,这个:
type Enum is (Ten, Twenty, Thirty);
for Enum use (10, 20, 30);
function Rep is new Ada.Unchecked_Conversion(Enum, Integer);
在 GNAT 中产生此警告:
warning: types for unchecked conversion have different sizes
但是在警告之后,Rep 确实返回了预期值 10、20 和 30。
RM 明确指出,如果 Unchecked_Conversion 实例中的源大小和目标大小不匹配,并且结果类型是标量,则
函数的结果是实现定义的,并且可以有一个 无效的表示
因此,上述适用于 GNAT 的事实并不意味着它可以保证在任何地方都有效。
对于仅支持System.Min_Int..System.Max_Int范围内的值的实现,您可以执行以下操作:
type Enum is (...);
for Enum use (...);
type Longest_Integer is range System.Min_Int .. System.Max_Int;
function Rep is new Ada.Unchecked_Conversion(Enum, Longest_Integer);
并忽略警告。但是编译器允许接受大于 System.Max_Int 的值,只要它们在某个整数类型的范围内。例如,GNAT 拒绝这个,但另一个 Ada 编译器可能会接受它:
type Longest_Unsigned is mod System.Max_Binary_Modulus;
type Unsigned_Enum is (Zero, Huge);
for Unsigned_Enum use (0, Longest_Unsigned'Last);
从这个到任何有符号整数类型的 Unchecked_Conversion 将不起作用。如果大小不匹配,您仍然存在实现定义结果的潜在问题。
这是一个适用于任何枚举类型的通用解决方案如果 (a) 表示值在 System.Min_Int..System.Max_Int 范围内,并且 (b) 如果 Unchecked_Conversion 的实现表现更好超出 Ada 标准的要求:
type Longest_Signed is range System.Min_Int .. System.Max_Int;
generic
type Enum is (<>);
function Generic_Rep(E: Enum) return Longest_Signed;
function Generic_Rep(E: Enum) return Longest_Signed is
function Rep is new Ada.Unchecked_Conversion(Enum, Longest_Signed);
begin
return Rep(E);
end Generic_Rep;
鉴于所有这些混乱,您可能会考虑使用除枚举表示子句之外的某种机制来完成您想做的任何事情。
更新:
GNAT 具有实现定义的属性'Enum_Rep 和'Enum_Val。预计 Ada 2020 将采用它们。
http://www.ada-auth.org/standards/2xrm/html/RM-13-4.html#p10.1
【讨论】:
type Unsigned_Byte is new natural range 0 .. 255; for Unsigned_Byte'Size use 8; for Enum'Size use 8;
type short_int is mod 2**enum_type'size;的类型
type Enum is (Minus_One, Zero, One); for Enum use (-1, 0, 1);
我发现在 GNAT 中工作的目的是:
type MyEnum is (A, B, C);
我必须这样做:
EVal : MyEnum := B;
IVal : Integer := MyEnum'Enum_Rep(EVal);
【讨论】:
'Enum_Rep 和 'Enum_Val 是 GNAT 特定的,但它们将在 2020 Ada 标准中采用。
据我了解质量和风格指南,根本不应该关心枚举的内部表示值。经过大量研究,我曾经决定使用以下构造:
type enum_c is (clk_eq, clk_div_2, clk_div_16, clk_div_128, clk_div_1024);
type enum_c_values is array (enum_c) of natural; -- or any type you wish
cdiv_values : constant enum_c_values := (
clk_eq => 1,
clk_div_2 => 2,
clk_div_16 => 16,
clk_div_128 => 128,
clk_div_1024 => 1024 );
c : enum_c := clk_div_128;
...
put_line("c =" & c'img & " natural value associated w/ c =" & cdiv_values(c)'img);
【讨论】:
如果您不使用 JVM 或 .NET 编译器,则可以将两者叠加;类似:
Value : Integer;
For Value'Address use ENUM_VAR'Address;
你会想要使用抑制初始化的编译指示,虽然我现在不记得了。
IIRC,还有 variant-record 方法,您可以在其中精确覆盖字段并将记录用作一种视图转换。
【讨论】:
pragma Import (Value, Ada);,我想。
如果您使用 GNAT,并且不介意使用特定于编译器,则该编译器为此目的提供 Enum_Rep 属性。
【讨论】:
AARM 13.4(第 11/1 段)推荐Unchecked_Conversion(可能是整数)。
【讨论】: