Enumeration representation
Note
Enumeration representation attributes are supported by
GNAT Community Edition 2019
GCC 9
Enumeration types in Ada are represented as integers at the machine level. But there are actually two mappings from enumeration to integer: a literal position and a representation value.
Literal positions
Each enumeration literal has a corresponding position in the type
declaration. We can easily obtain it from the Type'Pos (Enum)
attribute.
with Ada.Text_IO; with Ada.Integer_Text_IO; procedure Main is begin Ada.Text_IO.Put ("Pos(False) ="); Ada.Integer_Text_IO.Put (Boolean'Pos (False)); Ada.Text_IO.New_Line; Ada.Text_IO.Put ("Pos(True) ="); Ada.Integer_Text_IO.Put (Boolean'Pos (True)); end Main;
For the reverse mapping, we use Type'Val (Int)
:
with Ada.Text_IO; procedure Main is begin Ada.Text_IO.Put_Line (Boolean'Val (0)'Image); Ada.Text_IO.Put_Line (Boolean'Val (1)'Image); end Main;
Representation values
The representation value defines the internal code, used to store
enumeration values in memory or CPU registers. By default, enumeration
representation values are the same as the corresponding literal
positions, but you can redefine them. Here, we created a copy of
Boolean
type and assigned it a custom representation.
In Ada 2022, we can get an integer value of the representation with
Type'Enum_Rep(Enum)
attribute:
with Ada.Text_IO; with Ada.Integer_Text_IO; procedure Main is type My_Boolean is new Boolean; for My_Boolean use (False => 3, True => 6); begin Ada.Text_IO.Put ("Enum_Rep(False) ="); Ada.Integer_Text_IO.Put (My_Boolean'Enum_Rep (False)); Ada.Text_IO.New_Line; Ada.Text_IO.Put ("Enum_Rep(True) ="); Ada.Integer_Text_IO.Put (My_Boolean'Enum_Rep (True)); end Main;
And, for the reverse mapping, we can use Type'Enum_Val (Int)
:
with Ada.Text_IO; with Ada.Integer_Text_IO; procedure Main is type My_Boolean is new Boolean; for My_Boolean use (False => 3, True => 6); begin Ada.Text_IO.Put_Line (My_Boolean'Enum_Val (3)'Image); Ada.Text_IO.Put_Line (My_Boolean'Enum_Val (6)'Image); Ada.Text_IO.Put ("Pos(False) ="); Ada.Integer_Text_IO.Put (My_Boolean'Pos (False)); Ada.Text_IO.New_Line; Ada.Text_IO.Put ("Pos(True) ="); Ada.Integer_Text_IO.Put (My_Boolean'Pos (True)); end Main;
Note that the 'Val(X)/'Pos(X)
behaviour still is the same.
Custom representations can be useful for integration with a low level protocol or hardware.
Before Ada 2022
This doesn't initially look like an important feature, but let's see
how we'd do the equivalent with Ada 2012 and earlier versions. First,
we need an integer type of matching size, then we instantiate
Ada.Unchecked_Conversion
. Next, we call To_Int/From_Int
to work with representation values. And finally an extra type
conversion is needed:
with Ada.Text_IO; with Ada.Integer_Text_IO; with Ada.Unchecked_Conversion; procedure Main is type My_Boolean is new Boolean; for My_Boolean use (False => 3, True => 6); type My_Boolean_Int is range 3 .. 6; for My_Boolean_Int'Size use My_Boolean'Size; function To_Int is new Ada.Unchecked_Conversion (My_Boolean, My_Boolean_Int); function From_Int is new Ada.Unchecked_Conversion (My_Boolean_Int, My_Boolean); begin Ada.Text_IO.Put ("To_Int(False) ="); Ada.Integer_Text_IO.Put (Integer (To_Int (False))); Ada.Text_IO.New_Line; Ada.Text_IO.Put ("To_Int(True) ="); Ada.Integer_Text_IO.Put (Integer (To_Int (True))); Ada.Text_IO.New_Line; Ada.Text_IO.Put ("From_Int (3) ="); Ada.Text_IO.Put_Line (From_Int (3)'Image); Ada.Text_IO.New_Line; Ada.Text_IO.Put ("From_Int (6) ="); Ada.Text_IO.Put_Line (From_Int (6)'Image); end Main;
Even with all that, this solution doesn't work for generic formal type
(because T'Size
must be a static value)!
We should note that these new attributes may already be familiar to GNAT users because they've been in the GNAT compiler for many years.