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.
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;
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.
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.