2016-05-05 2 views
73

Nel mio attuale base di codice vedo questo schema seguente:!perché uno usare MACRO + 0 = 0

#if SOMETHING_SUPPORTED+0 != 0 
... 
#endif 

Purtroppo questa è una base di codice molto vecchio e nessuno sa come e perché è iniziato. Penso che sia iniziato in C e sia stato lentamente convertito in C con le classi e ora tende a C++

Non riesco a vedere alcun ovvio vantaggio nell'usare il costrutto precedente invece del "classico", ma forse mi manca qualcosa:

#if SOMETHING_SUPPORTED 
... 
#endif 

sapete perché uno usare #if MACRO+0 != 0 invece di #if MACRO?

+3

Potrebbe essere correlato: [Test per definizione macro vuota] (http://stackoverflow.com/questions/4102351/test-for-empty-macro-definition) –

+3

Il '! = 0' è sicuramente ridondante –

risposta

71

L'indizio qui è che il codice base è molto vecchio.

Questo trucco probabilmente esiste perché il codice una volta era stato portato su un compilatore con alcuni molto vecchi preprocessore che non tratta indefiniti macro come 0 in preprocessore #if condizionali.

Vale a dire, come del 1989 ANSI C è stato standardizzato che se abbiamo:

#if foo + bar - xyzzy

la direttiva è soggetta alla sostituzione macro, in modo che se foo, bar o xyzzy sono macro, vengono sostituiti. Quindi tutti gli identificatori rimanenti che non sono stati sostituiti vengono sostituiti con 0. Quindi, se foo è definito come 42, ma bar e xyzzy non sono definiti a tutti, otteniamo:

#if 42 + 0 - 0 

e non, per esempio, cattiva sintassi:

#if 42 + - 

o qualche altro comportamento, come la diagnostica circa bar non definito.

Su un preprocessore in cui le macro non definite vengono considerate vuote, #if SOMETHING_SUPPORTED si espande a solo #if, che è quindi errato.

Questo è l'unico modo in cui questo trucco IDENT+0 ha senso. Semplicemente non vorrai mai farlo se puoi contare sul fatto che la pre-elaborazione sia conforme ISO C.

La ragione è che se si prevede che SOMETHING_SUPPORTED abbia valori numerici, è erroneamente disperso per definirlo semplicemente come uno spazio vuoto. Idealmente vuoi rilevare quando è successo e interrompere la compilazione con una diagnostica.

In secondo luogo, se il tuo do supporta un tale uso spregevole, quasi certamente vuoi un simbolo esplicitamente definito, ma vuoto comportarsi come se avesse il valore 1, non il valore 0. Altrimenti, sei creando una trappola. Qualcuno potrebbe farlo sulla riga di comando del compilatore:

-DSOMETHING_SUPPORTED=$SHELL_VAR # oops, SHELL_VAR expanded to nothing 

o nel codice:

#define SOMETHING_SUPPORTED /* oops, forgot "1" */ 

Nessuno sta andando a aggiungere un #define o -D per un simbolo con l'intento di trasformare off la funzionalità che controlla! Il programmatore che inserisce un #define SOMETHING_SUPPORTED senza 1 sarà sorpresa dal comportamento di

#if SOMETHING_SUPPORTED+0 

che salta il materiale che doveva essere abilitato.

Questo è il motivo per cui sospetto che pochi programmatori C leggendo questo abbiano mai visto un tale utilizzo e perché sospetto che sia solo una soluzione per il comportamento del preprocessore il cui effetto voluto è saltare il blocco se manca SOMETHING_SUPPORTED. Il fatto che si tratti di una "trappola programmatore" è solo un effetto collaterale della soluzione alternativa.

Per risolvere tale problema preprocessore senza creare una trappola programmatore è quello di avere, da qualche parte nelle prime fasi l'unità di traduzione, questo:

#ifndef SOMETHING_SUPPORTED 
#define SOMETHING_SUPPORTED 0 
#endif 

e poi altrove basta usare #if SOMETHING_SUPPORTED.Forse quell'approccio non si presentava al programmatore originale, o forse quel programmatore pensava che lo stratagemma +0 fosse pulito, e avesse dato valore al suo autocontenimento.

+1

Non hai torto, ma ... tutti i compilatori Unixy che conosco trattano '-DTHING' come abbreviazione di' -DTHING = 1'; se davvero vuoi che la macro si espanda, devi dire '-DTHING ='. – zwol

+1

@zwol Potrebbe accadere per '-DFOO = $ FOO_ON', dove la variabile var è stata espansa a zero, piuttosto che 0 o 1 come previsto! (modificato). – Kaz

+1

Gli script di compilazione sono notoriamente fragili come quello ... – zwol

35

Facciamo un tavolo!

X  #if X  #if X+0 != 0 
<undef> false  false 
<empty> error  false 
0  false  false 
1  true  true 
2  true  true 
a  false  false 
xyz  false  false 
12a  error  error 
12 a error  error 

Così l'unica differenza che abbiamo trovato (grazie a commentatori) è il caso in cui X è definito, ma non ha alcun valore (come stringa vuota). Non ho mai visto la variante +0 != 0 prima.

+2

' # se X' è "errore" quando 'X' è vuoto –

+1

@MM: Anche se così pure. L'ho appena provato Funziona come previsto. Quindi ho cancellato la mia risposta come sbagliata. –

+0

@ M.M: No, non lo è. http://stackoverflow.com/questions/1643820/why-no-warning-with-if-x-when-x-undefined –

7

È un altro modo di scrivere

#if defined(MACRO) && MACRO != 0 

Il +0 è lì per garantire il risultato è un numero. Se MACRO non è definito, si evita l'errore di sintassi che risulterebbe da #if MACRO != 0.

Povera roba di qualità, ma non scherzare se non è necessario.

+0

#if defined (MACRO) && MACRO! = 0 => false quando la macro non è definita #if MACRO + 0! = 0 => false quando la macro non è definita, ma per un motivo diverso: 0 + 0! = 0 Hanno lo stesso risultato, ma non sono equivalenti http://stackoverflow.com/questions/5085392/questo-è-il-valore-di-an-undefined-constant-used-in-if-c – Felics

+0

@Felics Non sono in grado di attribuire alcun significato utile a "hanno lo stesso risultato ma non sono equivalenti". Non ho detto che * erano * equivalenti in realtà. Il tuo punto rimane oscuro. – EJP

+0

Volevo sottolineare che '#if MACRO + 0! = 0' non è un modo diverso di scrivere' #if definito (MACRO) && MACRO! = 0'. Es: '#define MACRO' causerebbe un errore di compilazione per' #if definito (MACRO) && MACRO! = 0' e verrebbe compilato bene per '#if MACRO + 0! = 0' – Felics

44

#if X+0 != 0 è diverso da #if X nel caso in cui X è definito a vuoto (nota: questo è diverso per il caso di X non essendo definito), ad esempio:

#define X 

#if X   // error 
#if X+0 != 0 // no error; test fails 

È molto comune per definire vuoto macro: la configurazione del progetto può generare un intestazione comune contenente un gruppo di linee #define USE_FOO, #define USE_BAR per abilitare le funzionalità supportate dal sistema e così via.

Il != 0 è ridondante, il codice potrebbe essere appena stato #if X+0.


Così, il vantaggio di utilizzare #if X+0 è che se X è definito come vuoto poi compilazione continua con il blocco essendo saltato, invece di innescare un errore.

Se questa è una buona idea è discutibile, personalmente vorrei utilizzare #ifdef per le macro booleani come USE_SOME_FEATURE e #if per le macro in cui il valore potrebbe essere una serie di numeri interi, per esempio; e vorrei vedere un errore se uso accidentalmente #if con qualcosa di definito da svuotare.

+1

Un flag come '-DNDEBUG' definirà' NDEBUG' come '1', non vuoto. –

+0

@DietrichSe hai ragione. '-DNEBUG =' lo definirei vuoto –

+0

Non ho mai visto un errore da questo. Il '# se 'è valutato come falso nella mia esperienza (che probabilmente non è ancora desiderato). EDIT: Sono corretto, questo in effetti produce un errore. Non so cosa ho visto in passato. Forse era specifico del compilatore. – Cameron