2012-02-29 21 views

Respuesta

8

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

+0

¡respuesta muy buena e informativa! – oenone

+0

Excelente respuesta. No lo olvides, también puedes hacer lo inverso (int a enum) y verificar la validez con 'valid! – NWS

+0

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; ' –

2

AARM 13.4 (para 11/1) recomienda Unchecked_Conversion (a, probablemente, Entero).

5

Si está utilizando GNAT, y no le importa ir al compilador específico, ese compilador proporciona el atributo Enum_Rep para este propósito.

1

Si no está utilizando los compiladores JVM o .NET puede superponer los dos; algo así como:

Value : Integer; 
For Value'Address use ENUM_VAR'Address; 

Vas a querer usar el pragma que suprime la inicialización, aunque no recuerdo en este momento.

IIRC, También existe el método de registro de variante, donde puede superponer los campos exactamente y utilizar el registro como una especie de conversión de vista.

+1

'pragma Import (Value, Ada);', creo. –

+0

Pensé que podría ser eso. – Shark8

+0

Si va a superponer un elemento numérico encima de una enumeración, debe asegurarse de que los tamaños de instancia sean los mismos. –

1

como entiendo la guía de calidad y estilo, uno no debe ocuparse de los valores de representación interna de las enumeraciones en absoluto. después de un gran trabajo de investigación, una vez decidido utilizar la construcción siguiente:

type enum_c is (clk_eq, clk_div_2, clk_div_16, clk_div_128, clk_div_1024); 

type enum_c_values is array (enum_c) of natural; -- or any type you wish 

cdiv_values : constant enum_c_values := (
    clk_eq   => 1, 
    clk_div_2  => 2, 
    clk_div_16  => 16, 
    clk_div_128 => 128, 
    clk_div_1024 => 1024); 

c : enum_c := clk_div_128; 
... 
put_line("c =" & c'img & " natural value associated w/ c =" & cdiv_values(c)'img); 
0

Lo que he encontrado para trabajar en GNAT es para:

type MyEnum is (A, B, C); 

que tenía que hacer:

EVal : MyEnum := B; 
IVal : Integer := MyEnum'Enum_Rep(EVal); 
Cuestiones relacionadas