2012-02-06 17 views
6

Esiste un modo per scoprire automaticamente il tipo di una variabile in C, tramite un meccanismo all'interno del programma stesso o, più probabilmente, attraverso una pre-compilazione script che usa i passaggi del compilatore fino al punto in cui ha analizzato le variabili e assegnato loro i loro tipi? Sto cercando suggerimenti generali su questo. Di seguito sono riportati ulteriori dettagli su ciò di cui ho bisogno e perché.Ottenere il tipo di una variabile nel codice C

Vorrei cambiare la semantica della clausola di riduzione di OpenMP. A questo punto, sembra più semplice semplicemente sostituire la clausola nel codice sorgente (attraverso uno script) con una chiamata a una funzione, e quindi posso definire la funzione per implementare la semantica di riduzione che voglio. Per esempio, il mio script sarebbe convertire questo

#pragma omp parallel for reduction(+:x) 

in questo:

my_reduction(PLUS, &x, sizeof(x)); 
#pragma omp parallel for 

dove, in precedenza, non ho (diciamo)

enum reduction_op {PLUS, MINUS, TIMES, AND, 
    OR, BIT_AND, BIT_OR, BIT_XOR, /* ... */}; 

E my_reduction ha firma

void my_reduction(enum reduction_op op, void * var, size_t size); 

Tra gli altri t le ali, my_reduction dovrebbero applicare l'operazione di addizione alla variabile di riduzione come originariamente previsto dal programmatore. Ma la mia funzione non può sapere come farlo correttamente. In particolare, sebbene conosca il tipo di operazione (PLUS), la posizione della variabile originale (var) e la dimensione del tipo di variabile, non conosce il tipo stesso della variabile. In particolare, non sa se var ha un tipo integrale o a virgola mobile. Da un POV di basso livello, l'operazione di aggiunta per queste due classi di tipi è completamente diversa.

Se solo l'operatore non standard typeof, supportato da GCC, funzionerebbe nel modo in cui sizeof funziona, restituendo una sorta di variabile di tipo, potrei risolvere facilmente questo problema. Ma typeof non è proprio come sizeof: può essere usato, apparentemente, solo in dichiarazioni di valore l.

Ora, il compilatore ovviamente conosce il tipo di x prima che finisca di generare il codice eseguibile. Questo mi porta a chiedermi se posso in qualche modo sfruttare il parser di GCC, solo per ottenere il tipo di x e passarlo al mio script, e quindi eseguire nuovamente GCC, per compilare il mio codice sorgente modificato. Sarebbe quindi abbastanza semplice da dichiarare

enum var_type { INT8, UINT8, INT16, UINT16, /* ,..., */ FLOAT, DOUBLE}; 
void my_reduction(enum reduction_op op, void * var, enum var_type vtype); 

E my_reduction può lanciare in modo appropriato prima di dereferencing e applicando l'operatore.

Come si può vedere, sto cercando di creare una sorta di meccanismo di "invio" in C. Perché non usare solo l'overloading C++? Perché il mio progetto mi costringe a lavorare con il codice sorgente legacy scritto in C. Posso modificare il codice automaticamente con uno script, ma non riesco a riscriverlo in una lingua diversa.

Grazie!

+2

Che ne dici del post-processing del codice sorgente con alcuni strumenti/script? Per esempio. analizzarlo con clang, trovare i tipi, inserire/regolare il codice specifico del tipo, compilare? –

+0

Grazie, Alex. Sembra che stia andando nella giusta direzione. –

+1

Ho letto da qualche parte che la riduzione definita dall'utente farà parte dello standard 3.1 o 4.0. Hm 3.1 dice: 'reduction ({operator | intrinsic_procedure_name}: list)' ..non ha mai provato intrinsic_procedure_name. anche se solo in parte risolve il rilevamento del tipo. – Bort

risposta

2

GCC fornisce l'estensione typeof. Non è standard, ma abbastanza comune (molti altri compilatori, ad esempio clang/llvm, ce l'hanno).

Si potrebbe forse prendere in considerazione la personalizzazione di GCC estendendolo con MELT (una lingua specifica del dominio per estendere GCC) in base alle proprie finalità.

+1

Grazie, Basile. Come puoi vedere dal mio post, sono a conoscenza di typeof, ma typeof non mi darà quello di cui ho bisogno: non restituisce nulla; non puoi passarlo o le sue informazioni a una funzione. È possibile effettuare ciò: #define add (x, y, z) {\ typeof (x) _x = x; \ typeof (y) _y = y; \ z = _x + _y; \ } L'operatore di tipo mi sembra avere un'utilità molto limitata. –

+1

Non è standard e non funzionerà con la maggior parte dei compilatori C. Nella mia esperienza è una pessima idea usare le estensioni GCC per il codice di produzione. – Lundin

2

Si potrebbe anche considerare la personalizzazione di GCC con un plug-in o un'estensione MELT per le proprie esigenze. Tuttavia, ciò richiede la comprensione di alcune delle rappresentazioni interne di GCC (Gimple, Tree) che sono complesse (quindi ci vorranno almeno giorni di lavoro).

Ma i tipi sono una cosa solo compilata in C. Non sono reificati.

+1

Questo è più o meno quello che ho finito facendo - non un plug-in, ma un codice di hacking all'interno di GCC stesso. Ho capito come ottenere le informazioni sul tipo da Gimple tree_nodes al momento della compilazione. Ci è voluto un bel po 'di scavo. Grazie! –

4

C non ha un modo per eseguire questa operazione in fase di pre-compilazione, a meno che non si scriva un flusso di macro. Vorrei non consiglia l'alluvione del approccio macro, sarebbe praticamente andare in questo modo:

void int_reduction (enum reduction_op op, void * var, size_t size); 

#define reduction(type,op,var,size) type##_reduction(op, var, size) 

... 
reduction(int, PLUS, &x, sizeof(x)); // function call 

Si noti che questo è molto cattiva pratica e deve essere utilizzato solo come ultima risorsa, quando il mantenimento di codice legacy scritto male, se anche poi. Non vi è alcun tipo di sicurezza o altre garanzie di questo tipo con questo approccio.

un approccio più sicuro è quello di chiamare in modo esplicito int_reduction() dal chiamante, o per chiamare una funzione generica che decide il tipo in fase di esecuzione:

void reduction (enum type, enum reduction_op op, void * var, size_t size) 
{ 
    switch(type) 
    { 
    case INT_TYPE: 
     int_reduction(op, var, size); 
     break; 
    ... 
    } 
} 

Se int_reduction è inline e varie altre ottimizzazioni sono fatto, questa valutazione runtime non è necessariamente molto più lento dei macro offuscati, ma è molto più sicuro.

+1

Sì, grazie per i tuoi pensieri. Ma come scopri il tipo? Questa è la mia grande sfida. Il resto è solo sintassi, e ovviamente sono d'accordo con i tuoi commenti sulla migliore sintassi. Nel caso precedente, ad esempio, come faccio a sapere se una determinata variabile è INT_TYPE? Lo script che sto scrivendo per spostare "riduzione (+: x)" fino a una chiamata separata "riduzione (INT_TYPE, PLUS, & var, sizeof (x));" dovrei sapere che x ha tipo INT_TYPE, ma come? - a meno che scrivo un intero parser solo per ottenere il tipo di x? –

+1

@AmittaiAviram Come fai a sapere quando dichiarare una variabile come 'int'? Non ho idea della natura dei tuoi dati o da dove proviene, quindi non posso rispondere a questa domanda. Se si tratta di una sorta di dati grezzi esterni, sarà necessario determinare il tipo di dati prima di eseguire qualsiasi calcolo. E questo è un problema di algoritmo che non ha nulla a che fare con la sintassi di programmazione C in quanto tale. – Lundin

+1

@ Lundin - No, no. L'immagine è più simile a questa. Supponiamo di avere una funzione 'main' che ha la dichiarazione' int x' nella parte superiore. Dì 100 linee in basso, hai '#pragma omp parallelo per riduzione (+: x)'. Il 'x' là ha tipo' int' perché è così che è stato dichiarato nell'ambito corrente. Il programmatore lo sa e potrebbe annotare una chiamata di funzione, ma voglio essere in grado di farlo automaticamente. –

7

C11 _Generic non è una soluzione diretta, ma permette di ottenere il risultato desiderato, se si ha pazienza codificare tutti i tipi come in:

#define typename(x) _Generic((x), \ 
    int:  "int", \ 
    float: "float", \ 
    default: "other") 

int i; 
float f; 
void* v; 
assert(strcmp(typename(i), "int") == 0); 
assert(strcmp(typename(f), "float") == 0); 
assert(strcmp(typename(i), "other") == 0); 

Un buon punto di partenza con le tonnellate di tipi può essere trovato in this answer.

GCC ha aggiunto il supporto solo in 4.9.

4

È possibile utilizzare la funzione sizeof per determinare il tipo, lasciare var la variabile di tipo sconosciuto. poi

if(sizeof(var)==sizeof(char)) 
     printf("char"); 
    else if(sizeof(var)==sizeof(int)) 
     printf("int"); 
    else if(sizeof(var)==sizeof(double)) 
     printf("double"); 

Tu che verrà portato a complicazioni quando due o più tipi principali potrebbero avere stesse dimensioni.

Problemi correlati