No hay una solución completamente general. Las cláusulas de representación de enumeración parecen estar diseñadas para dificultar la obtención de esta información.
Este:
function Rep is new Ada.Unchecked_Conversion(Enum, Integer);
es probable que funcione en la mayoría de los casos, pero hay algunas advertencias serias: los valores de representación tienen que estar dentro del rango Integer'First..Integer'Last
, y no se si los tamaños de Enum
Integer
y hacen coincidir con el resultado es en realidad la implementación definida (pero funciona con GNAT).
Como dice Simon Wright, la RM recomienda Unchecked_Conversion
, pero esta no es una solución muy satisfactoria, y la determinación de un tipo de objetivo consistente es difícil.
A partir de la RM 2007, el nivel recomendado de apoyo es:
Una aplicación debe soportar al menos los códigos internos en el System.Min_Int..System.Max_Int gama.
lo que significa que la conversión a Integer
no siempre es suficiente; un valor podría ser menor que Integer'First
, o mayor que Integer'Last
. E incluso si todos los valores están en ese rango, no hay una forma realmente buena de determinar un tipo de objetivo que sea del mismo tamaño que el tipo de enumeración. Por ejemplo, esto:
type Enum is (Ten, Twenty, Thirty);
for Enum use (10, 20, 30);
function Rep is new Ada.Unchecked_Conversion(Enum, Integer);
produce esta advertencia en GNAT:
warning: types for unchecked conversion have different sizes
Pero después de la advertencia, Rep qué devuelven los valores esperados 10, 20 y 30.
El RM afirma explícitamente que si los tamaños de origen y de destino en una instancia de Unchecked_Conversion no coinciden, y el tipo de resultado es escalar, entonces
el resultado de la función se define la aplicación, y puede tener una representación no válida
Por lo tanto, el hecho de que lo anterior funcione para GNAT no significa que está garantizado para funcionar en todas partes.
Para una implementación que solamente soporta valores en el rango System.Min_Int..System.Max_Int
, se puede hacer algo como esto:
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);
y pasar por alto la advertencia. Pero los compiladores son permitidos para aceptar valores mayores que System.Max_Int, siempre que estén dentro del rango de algún tipo entero. Por ejemplo, GNAT rechaza esto, pero otro compilador de Ada podría aceptarlo:
type Longest_Unsigned is mod System.Max_Binary_Modulus;
type Unsigned_Enum is (Zero, Huge);
for Unsigned_Enum use (0, Longest_Unsigned'Last);
y un Unchecked_Conversion de este a cualquier tipo entero con signo no funcionará. Y todavía tiene el problema potencial de la implementación de los resultados definidos si los tamaños no coinciden.
Aquí es una solución genérica que debería funcionar para cualquier tipo de enumeración si (a) los valores de representación están en el rango System.Min_Int..System.Max_Int
, y (b) si la implementación de Unchecked_Conversion
es mejor comportamiento que el estándar Ada exige que sea :
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;
Teniendo en cuenta toda esta confusión, es posible considerar el uso de algún mecanismo distinto al de las cláusulas de representación de enumeración de hacer lo que estamos tratando de hacer.
¡respuesta muy buena e informativa! – oenone
Excelente respuesta. No lo olvides, también puedes hacer lo inverso (int a enum) y verificar la validez con 'valid! – NWS
Muy buena respuesta. Una nota: la advertencia de "diferentes tamaños" puede evitarse si crea un nuevo tipo de entero y especifica el mismo atributo de tamaño para Enum y entero. 'type Unsigned_Byte es el nuevo rango natural 0 .. 255; para uso Unsigned_Byte'Size 8; \t para Enum'Size use 8; ' –