我需要在Ada中获取与枚举类型值相关的数值。不是枚举中的位置,而是“for TYPE use”子句赋值给每个值。
有人知道是否有可能吗?
答案 0 :(得分:9)
目前还没有完全通用的解决方案。枚举表示条款似乎旨在使这些信息难以获得。 (但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)如果{{1}的实现}比Ada标准要求的表现更好:
Unchecked_Conversion
考虑到所有这些混淆,您可以考虑使用除枚举表示子句之外的某种机制来执行您想要执行的任何操作。
更新:
GNAT具有实现定义的属性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;
和'Enum_Rep
。预计Ada 2020将采用它们。
http://www.ada-auth.org/standards/2xrm/html/RM-13-4.html#p10.1
答案 1 :(得分:5)
如果您正在使用GNAT,并且不介意使用特定于编译器,那么该编译器会为此提供Enum_Rep属性。
答案 2 :(得分:2)
AARM 13.4(第11/1段)建议Unchecked_Conversion
(可能是整数)。
答案 3 :(得分:1)
如果你没有使用JVM或.NET编译器,你可以覆盖这两个;类似的东西:
Value : Integer;
For Value'Address use ENUM_VAR'Address;
你会想要使用抑制初始化的pragma,但我现在不记得了。
IIRC,还有变体记录方法,您可以在其中精确覆盖字段并将记录用作一种视图转换。
答案 4 :(得分:1)
因为我理解质量和风格指南,所以根本不应该关注枚举的内部表示值。经过大量的研究,我曾经决定使用以下结构:
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);