I need to get the numeric value asociated with a value of an enumerated type in Ada. Not the position in the enumeration, but the value assigned with the "for TYPE use" clause to every value.
Does anyone know if it is possible?
An enumeration type is defined as a list of possible values: type Primary_Color is (Red, Green, Blue); Like for numeric types, where e.g. 1 is an integer literal, Red, Green and Blue are called the literals of this type. There are no other values assignable to objects of this type.
An enumeration type (or enum type) is a value type defined by a set of named constants of the underlying integral numeric type. To define an enumeration type, use the enum keyword and specify the names of enum members: C# Copy.
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.
An enumeration is a data type that consists of a set of named values that represent integral constants, known as enumeration constants. An enumeration is also referred to as an enumerated type because you must list (enumerate) each of the values in creating a name for each of them.
There is currently no completely general solution. Enumeration representation clauses seem to be designed to make this information difficult to obtain. (However Ada 2020 will add a solution; see the bottom of this answer for details.)
This:
function Rep is new Ada.Unchecked_Conversion(Enum, Integer);
is likely to work in most cases, but there are some serious caveats: the representation values have to be within the range Integer'First..Integer'Last
, and if the sizes of Enum
and Integer
don't match the result is actually implementation defined (but it works with GNAT).
As Simon Wright says, the RM recommends Unchecked_Conversion
, but this is not a very satisfying solution, and determining a consistent target type is difficult.
As of the 2007 RM, the recommended level of support is:
An implementation should support at least the internal codes in the range System.Min_Int..System.Max_Int.
which means that converting to Integer
is not always sufficient; a value could be less than Integer'First
, or greater than Integer'Last
. And even if all the values are in that range, there's no really good way to determine a target type that's the same size as the enumeration type. For example, this:
type Enum is (Ten, Twenty, Thirty);
for Enum use (10, 20, 30);
function Rep is new Ada.Unchecked_Conversion(Enum, Integer);
produces this warning in GNAT:
warning: types for unchecked conversion have different sizes
But after the warning, Rep does return the expected values 10, 20, and 30.
The RM explicitly states that if the source and target sizes in an instance of Unchecked_Conversion don't match, and the result type is scalar, then
the result of the function is implementation defined, and can have an invalid representation
So the fact that the above works for GNAT doesn't mean it's guaranteed to work everywhere.
For an implementation that only supports values in the range System.Min_Int..System.Max_Int
, you can do something like this:
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);
and ignore the warning. But compilers are allowed to accept values greater than System.Max_Int, as long as they're within the range of some integer type. For example, GNAT rejects this, but another Ada compiler might accept it:
type Longest_Unsigned is mod System.Max_Binary_Modulus;
type Unsigned_Enum is (Zero, Huge);
for Unsigned_Enum use (0, Longest_Unsigned'Last);
and an Unchecked_Conversion from this to any signed integer type will not work. And you still have the potential problem of implementation defined results if the sizes don't match.
Here's a generic solution that should work for any enumeration type if (a) the representation values are in the range System.Min_Int..System.Max_Int
, and (b) if the implementation of Unchecked_Conversion
is better behaved than the Ada standard requires it to be:
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;
Given all this confusion, you might consider using some mechanism other than enumeration representation clauses to do whatever you're trying to do.
UPDATE:
GNAT has implementation-defined attributes 'Enum_Rep
and 'Enum_Val
. Ada 2020 is expected to adopt them.
http://www.ada-auth.org/standards/2xrm/html/RM-13-4.html#p10.1
What I found to work in GNAT is for:
type MyEnum is (A, B, C);
I had to do:
EVal : MyEnum := B;
IVal : Integer := MyEnum'Enum_Rep(EVal);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With