Get the representation value of an enumeration type in Ada
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
AARM 13.4 (para 11/1) recommends Unchecked_Conversion
(to, probably, Integer).
If you're using GNAT, and don't mind going compiler-specific, that compiler provides the Enum_Rep attribute for this purpose.