2012-02-29 13 views

risposta

8

Non esiste una soluzione completamente generale. Le clausole di rappresentazione dell'enumerazione sembrano essere progettate per rendere queste informazioni difficili da ottenere.

questo:

function Rep is new Ada.Unchecked_Conversion(Enum, Integer); 

rischia di funzionare nella maggior parte dei casi, ma ci sono alcuni avvertimenti seri: i valori di rappresentanza devono essere all'interno della gamma Integer'First..Integer'Last, e se le dimensioni di Enum e Integer Non la corrispondenza con il risultato è in realtà definita dall'implementazione (ma funziona con GNAT).

Come dice Simon Wright, RM raccomanda Unchecked_Conversion, ma questa non è una soluzione molto soddisfacente e la determinazione di un tipo di obiettivo coerente è difficile.

Alla RM 2007, il livello raccomandato di supporto è:

L'attuazione dovrebbe sostenere almeno i codici interni nella gamma System.Min_Int..System.Max_Int.

che significa che la conversione in Integer non è sempre sufficiente; un valore potrebbe essere inferiore a Integer'First o superiore a Integer'Last. E anche se tutti i valori si trovano in quell'intervallo, non esiste un modo veramente valido per determinare un tipo di destinazione della stessa dimensione del tipo di enumerazione. Ad esempio, questo:

type Enum is (Ten, Twenty, Thirty); 
for Enum use (10, 20, 30); 
function Rep is new Ada.Unchecked_Conversion(Enum, Integer); 

produce questo avvertimento in GNAT:

warning: types for unchecked conversion have different sizes 

Ma dopo l'avvertimento, Rep fa ritorno i valori attesi 10, 20, e 30.

La RM afferma esplicitamente che se i formati di origine e destinazione di un'istanza di Unchecked_Conversion non corrispondono, e il tipo di risultato è scalare,

il risultato della funzione è definita attuazione, e può avere un rappresentazione valida

Quindi il fatto che i lavori di cui sopra per GNAT non significa che è garantito il funzionamento in tutto il mondo.

Per un'implementazione che solo supporta i valori nella gamma System.Min_Int..System.Max_Int, si può fare qualcosa di simile:

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

e ignorare l'avviso. Ma i compilatori sono consentito per accettare valori maggiori di System.Max_Int, purché siano all'interno dell'intervallo di un determinato tipo intero. Ad esempio, GNAT rifiuta questo, ma un altro compilatore Ada potrebbe accettarlo:

type Longest_Unsigned is mod System.Max_Binary_Modulus; 
type Unsigned_Enum is (Zero, Huge); 
for Unsigned_Enum use (0, Longest_Unsigned'Last); 

e un Unchecked_Conversion da questo a qualsiasi tipo intero con segno non funzionerà. E hai ancora il potenziale problema dei risultati definiti dall'implementazione se le dimensioni non corrispondono.

Ecco una soluzione generica che dovrebbe funzionare per qualsiasi tipo di enumerazione se (a) i valori di rappresentanza sono nell'intervallo System.Min_Int..System.Max_Int, e (b) se l'implementazione di Unchecked_Conversion è meglio comportati rispetto allo standard Ada richiede di essere :

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; 

Dato tutto questa confusione, si potrebbe considerare l'utilizzo di un meccanismo diverso da clausole di rappresentazione di enumerazione di fare quello che stai cercando di fare.

+0

risposta molto buona e informativa! – oenone

+0

Ottima risposta. Non dimenticare, puoi anche fare il contrario (int enum) e verificare la validità con 'valido! – NWS

+0

Risposta molto bella. Una nota: l'avviso "diverse dimensioni" può essere evitato se si crea un nuovo tipo intero e si specifica lo stesso attributo Dimensione sia per Enum che per intero. 'type Unsigned_Byte è il nuovo intervallo naturale 0 .. 255; per Unsigned_Byte'Dimensione utilizza 8; \t per Enum'Size utilizzare 8; ' –

2

AARM 13.4 (para 11/1) consiglia Unchecked_Conversion (to, probabilmente, numero intero).

5

Se si utilizza GNAT e non importa che sia specifico del compilatore, il compilatore fornisce l'attributo Enum_Rep per questo scopo.

1

Se non si utilizzano i compilatori JVM o .NET, è possibile sovrapporre i due; qualcosa di simile:

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

Stai andando a voler utilizzare il pragma che sopprime l'inizializzazione, anche se non mi ricordo che in questo momento.

IIRC, C'è anche il metodo variant-record, in cui è possibile sovrapporre esattamente i campi e utilizzare il record come una sorta di conversione della vista.

+1

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

+0

Ho pensato che potesse essere così. – Shark8

+0

Se si sovrappone un elemento numerico in cima a un'enumerazione, è necessario assicurarsi che le dimensioni dell'istanza siano uguali. –

1

come ho capito la guida di qualità e stile, non ci si dovrebbe preoccupare dei valori di rappresentazione interna delle enumerazioni. dopo un sacco di ricerca, una volta ho deciso di utilizzare il seguente costrutto:

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

Quello che ho trovato a lavorare in GNAT è per:

type MyEnum is (A, B, C); 

che dovevo fare:

EVal : MyEnum := B; 
IVal : Integer := MyEnum'Enum_Rep(EVal); 
Problemi correlati