Get the representation value of an enumeration type in Ada

user1240854 picture user1240854 · Feb 29, 2012 · Viewed 9.9k times · Source

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?

Answer

Keith Thompson picture Keith Thompson · Feb 29, 2012

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