2012-02-14 9 views
6

Attualmente sto pulendo una libreria C esistente per pubblicarla spudoratamente.Come verificare se un parametro è un'espressione costante integrale in una macro del preprocessore C?

Una macro di preprocessore NPOT viene utilizzata per calcolare la potenza successiva successiva di due per una determinata espressione di costante integrale in fase di compilazione. La macro viene normalmente utilizzata nelle inizializzazioni dirette. Per tutti gli altri casi (ad esempio utilizzando parametri variabili), esiste una funzione inline con la stessa funzione.

Ma se l'utente passa una variabile, l'algoritmo si espande in un enorme pezzo di codice macchina. La mia domanda è: Cosa posso fare per impedire a un utente di passare qualsiasi cosa tranne un'espressione costante integrata alla mia macro?

#define NPOT(x) complex_algorithm(x) 

const int c=10; 
int main(void) { 
    int i=5; 

    foo = NPOT(5); // works, and does everything it should 
    foo = NPOT(c); // works also, but blows up the code extremely 
    foo = NPOT(i); // blows up the code also 
} 

Quello che ho già provato:

  1. Definire la macro #define NPOT(x) complex_algorithm(x ## u). Funziona ancora e genera un errore del compilatore, anche se difficilmente utile, per i parametri variabili. A meno che non ci siano variabili come iu ... Sporco, pericoloso, non lo voglio.
  2. La documentazione non ha funzionato per la maggior parte degli utenti.
+0

Per moralizzare, ritengo che questa domanda sia un ottimo esempio del perché le macro siano incomprensibili. È una specie di droghe altamente addictive. Sai che sono cattivi e una volta che inizi con loro, non puoi uscire. Troverete di aver bisogno sempre di più di loro. Basta guardare tra le righe di ciò che è veramente richiesto qui: "Qualcuno sa una macro per correggere questa macro, che chiama una macro." – Lundin

+0

Anche così non sono riuscito a trovare niente di male con una macro che chiama un'altra macro, non è questo il punto. Above * complex_algorithm * sta per qualsiasi cosa. Puoi avere una macro oltre alla funzione inline che ne consente l'uso nelle inizializzazioni dirette dando la possibilità di convalidare i parametri. – Lutz

risposta

6

È possibile utilizzare qualsiasi espressione che richiede un'espressione integrale costante e che verrà quindi ottimizzata.

#define NPOT(X)           \ 
(1              \ 
? complex_algorithm(X)         \ 
: sizeof(struct { int needs_constant[1 ? 1 : (X)]; }) \ 
) 

alla fine si dovrebbe lanci il risultato della sizeof al tipo intero caso, quindi l'espressione di ritorno è di un tipo che ci si aspetterebbe.

Sto utilizzando un senza tag struct qui per

  • hanno un tipo così davvero non temporanea viene prodotto
  • hanno un unico tipo in modo tale che l'espressione può essere ripetuta in qualsiasi parte del codice senza causare conflitti
  • innescare l'uso di un VLA, che non è consentita all'interno di una struct come di C99:

Un membro di una struttura o unione può avere qualsiasi tipo di oggetto diverso da un tipo modificato .

Sto usando il ternario ?: con 1 come l'espressione di selezione per garantire che il : viene sempre valutato per il suo tipo, ma mai valutato come espressione.

Edit: Sembra che gcc accetta VLA all'interno struct come un'estensione e non ha nemmeno avvertire su di esso, anche quando dico esplicitamente -std=c99. Questa è davvero una cattiva idea di loro.

Per un compilatore così strano :) è possibile utilizzare sizeof((int[X]){ 0 }), invece. Questo è "come proibito" come la versione sopra, ma in aggiunta anche gcc si lamenta di ciò.

+1

Penso che compili con C99 VLA's. Ma 'struct {int needs_constant: X; } 'dovrebbe funzionare; i campi di bit a lunghezza variabile non sono ancora disponibili. – MSalters

+0

@MSalters, no non verrebbe compilato con un VLA, non sono permessi all'interno di 'struct'. –

+0

I VLA sono consentiti all'interno di una struttura, ma solo come ultimo membro. – Lutz

0
#define INTEGRAL_CONST_EXPR(x) ((void) sizeof (struct {int a:(x);}), (x)) 

questo darà un errore di compilazione se x non è un'espressione costante intera.

my_function(INTEGRAL_CONST_EXPR(1 + 2 + 3)); // OK 
my_function(INTEGRAL_CONST_EXPR(1.0 + 2 + 3)); // compile error 

noti che questa soluzione non funziona per l'inizializzazione di una variabile statica:

static int a = INTEGRAL_CONST_EXPR(2 + 3); 

innescheranno un errore di compilazione causa di un'espressione con , non è un'espressione costante.

Come @JensGustedt inserito nel commento, non è possibile utilizzare un'espressione costante integrale che risolve un numero intero negativo in questa soluzione poiché la larghezza del campo di bit non può essere negativa.

+0

No, non funziona in C99 perché quindi "int [x]' è semplicemente un VLA. Inoltre, non funziona se viene utilizzato in un contesto di inizializzazione statica, poiché "assert" è un controllo di runtime. –

+0

C99 consente questo penso. Controlla il commento di MSalters – Anycorn

+0

@JensGustedt aggiornato con campi di bit a causa di VLA – ouah