2009-02-13 15 views
13

E 'possibile fare qualcosa di simileUtilizzare #ifdef e # define facoltativamente trasformare una chiamata di funzione in un commento

#ifdef SOMETHING 
#define foo // 
#else 
#define foo MyFunction 
#endif 

L'idea è che se qualcosa è definito, quindi chiama a foo (...) diventano commenti (o qualcosa che non viene valutato o compilato), altrimenti diventa una chiamata a MyFunction.

Ho visto __noop usato, ma non credo di poterlo usare.

EDIT (s):

Io non penso di poter davvero utilizzare una macro qui, perché MyFunction prende un numero variabile di argomenti.

Inoltre, mi piacerebbe farlo in modo che gli argomenti NON vengano valutati! (Così facendo qualcosa di simile commentando il corpo di MyFunction in realtà non darmi quello che mi serve, come saranno ancora valutati gli argomenti)

+0

Verificare se il compilatore supporta i "macro variadic". – ChrisW

+0

_Perché ... vorresti farlo? Forse c'è un altro modo per ottenere ciò di cui hai bisogno. Perché senza contesto, ciò che si vuole fare sembra piuttosto pericoloso ... – gimpf

+0

Per i compilatori che non supportano macro variadic è bello essere in grado di eliminare le stringhe di debug e le chiamate di funzione. Salva un sacco di spazio eseguibile. – MSN

risposta

24

Prova questo:

#ifdef SOMETHING 
#define foo(x) 
#else 
#define foo(x) MyFunction(x) 
#endif 

Se la funzione ha diversi argomenti, poi:

#ifdef SOMETHING 
#define foo(x,y,z) 
#else 
#define foo(x,y,z) MyFunction(x,y,z) 
#endif 

Se la funzione ha un numero variabile di argomenti, quindi il compilatore può sostenere le cosiddette "macro variadic", in questo modo:

#ifdef SOMETHING 
#define foo(...) 
#else 
#define foo(...) MyFunction(__VA_ARGS__) 
#endif 

La ragione per cui in pratica ho usato questo tipo di cose è di eliminare le funzioni di registrazione da una build di rilascio. Tuttavia, vedere anche Separate 'debug' and 'release' builds? in cui le persone si chiedono se si dovrebbe anche avere build differenti.


In alternativa, invece di ridefinire la chiamata di funzione come nulla, il commento di Jonathan a questa risposta suggerito di fare qualcosa di simile a quanto segue:

#ifdef SOMETHING 
#define foo(...) do { if (false) MyFunction(__VA_ARGS__) } while (0) 
#else 
#define foo(...) do { if (true) MyFunction(__VA_ARGS__) } while (0) 
#endif 

Il ragionamento per fare questo è in modo che la chiamata di funzione è sempre compilato (quindi non sarà lasciato con errori gratuiti come riferimenti a variabili cancellate), ma chiamato solo quando necessario: vedi Kernighan & Pike The Practice of Programming e anche lo Goddard Space Flight Center programming standards.

da un file Debug.h (proveniente dal 1990, e quindi non utilizza __VA_ARGS__):

/* 
** Usage: TRACE((level, fmt, ...)) 
** "level" is the debugging level which must be operational for the output 
** to appear. "fmt" is a printf format string. "..." is whatever extra 
** arguments fmt requires (possibly nothing). 
** The non-debug macro means that the code is validated but never called. 
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike. 
*/ 
#ifdef DEBUG 
#define TRACE(x) db_print x 
#else 
#define TRACE(x) do { if (0) db_print x; } while (0) 
#endif /* DEBUG */ 

Con C99, non c'è più una necessità per il doppio trucco parentesi. Il nuovo codice non dovrebbe usarlo a meno che la compatibilità con C89 non sia un problema.

+0

Funzionerà con un numero variabile di argomenti? per esempio. foo (a, b, c) –

+0

Funzionerà con un numero di argomenti superiore a uno ma fissato. Per un numero variabile di argomenti, Google e/o controlla la documentazione del compilatore per i cosiddetti "macro variadici". – ChrisW

+0

@ChrisW Se si modifica la risposta per utilizzare una macro variadica, allora I _think_ è esattamente ciò di cui ho bisogno :) –

0

Se non ricordo male, si dovrebbe essere in grado di #define la macro a "nulla" e che farà sì che il compilatore di ignorare questa chiamata

#define foo() 

foo(); // this will be ignored 
5

Forse un modo più semplice per farlo sarebbe quello di condizionalmente ometti il ​​corpo della funzione?

void MyFunction() { 
#ifndef SOMETHING 
    <body of function> 
#endif 
} 

A meno che espressamente non si vuole una chiamata di funzione da effettuare a tutti, questo mi sembra un modo pulito per raggiungere il tuo obiettivo.

+1

Ma gli argomenti saranno ancora valutati ... –

+0

Vero. Penso che se non vuoi che gli argomenti vengano valutati, potresti dover andare fino a #ifdefare tutte le chiamate. La soluzione "#define foo (x)" valuterà anche i tuoi argomenti sulla maggior parte dei compilatori. – MattK

+0

Non vengono valutati solo gli argomenti, ma sono inclusi anche i dati per essi, il che non è molto bello se la funzione è una funzione di registrazione e gli argomenti sono stringhe enormi che rivelano il funzionamento dell'applicazione. – sharptooth

2

Se, nel caso in cui non si vuole foo chiamato, si definisce come:

void foo() {} 

eventuali chiamate a foo() dovrebbe essere ottimizzato modo.

+0

Penso che dovresti probabilmente dichiararlo anche in linea per assicurarti che venga ottimizzato. Dovrebbe supportare anche i vararg, quindi penso che questa sia probabilmente la soluzione migliore. – rmeador

+0

Dipende dal compilatore. Ho visto VC++ ottimizzare le funzioni di allontanamento con il codice in esse quando poteva determinare che non avevano effetti collaterali. Molto frustrante quando stai provando a fare benchmark ;-) – Ferruccio

2

Che dire qualcosa in queste righe:

#ifdef NDEBUG 
#define DEBUG(STATEMENT) ((void)0) 
#else 
#define DEBUG(STATEMENT) (STATEMENT) 
#endif 

si può usare in questo modo per registrare i messaggi di debug:

DEBUG(puts("compile with -DNDEBUG and I'm gone")); 

Una versione non generica per l'output formattato con il debug aggiuntivo informazioni utilizzando macro variadic C99 e l'identificatore __func__ potrebbe essere simile a questo:

#ifdef NDEBUG 
#define Dprintf(FORMAT, ...) ((void)0) 
#define Dputs(MSG) ((void)0) 
#else 
#define Dprintf(FORMAT, ...) \ 
    fprintf(stderr, "%s() in %s, line %i: " FORMAT "\n", \ 
     __func__, __FILE__, __LINE__, __VA_ARGS__) 
#define Dputs(MSG) Dprintf("%s", MSG) 
#endif 

Ecco come devi usare queste macro: versione

Dprintf("count = %i", count); 
Dputs("checkpoint passed"); 
3

Purtroppo l'attuale C++ non supporta le macro variadic.

Tuttavia, si può fare questo:

#ifdef SOMETHING 
#define foo 
#else 
#define foo(args) MyFunction args 
#endif 

// you call it with double parens: 
foo((a, b, c)); 
+0

Lo faccio con le funzioni di debug logging in modo che (a) svaniscano completamente nelle build di produzione, e (b) io possa passare in una stringa di formato e qualsiasi numero di argomenti. –

1

No, il C++ Standard C e dire non si può #define qualcosa di essere un commento, così

#define foo // 

non funzionerà.

1
#ifdef SOMETHING 
#define foo sizeof 
#else 
#define foo MyFunction 
#endif 

Suppongo che foo sia una funzione di stile printf? Ad ogni modo, questo non funzionerà con una funzione parametro zero, ma se così fosse, sapresti già cosa fare. Se vuoi davvero essere anale, puoi usare (void)sizeof ma probabilmente non è necessario.

0

Che dire che circonda ogni chiamata a myFunction con

#ifdef SOMETHING 
myFunction(...); 
#endif 

?

+1

Questo diventa intollerabilmente goffo dopo la seconda invocazione. Non andare su questa strada. –

2

Probabilmente, non si desidera eseguire la semplice "rimozione del codice" come suggerito, perché i chiamanti si aspettano che gli effetti collaterali degli argomenti si verifichi. Ecco alcuni frammenti del chiamante fastidioso che dovrebbe farti pensare:

// pre/post increment inside method call: 
MyFunction(i++); 

// Function call (with side effects) used as method argument: 
MyFunction(StoreNewUsernameIntoDatabase(username)); 

Se si dovesse disattivare MyFunction semplicemente dicendo:

#define MyFunction(x) 

quindi gli effetti collaterali che i chiamanti si aspettavano sarebbe andato via, e il loro codice si romperà e sarà abbastanza difficile eseguire il debug.Mi piace il il suggerimento "sizeof" sopra, e mi piace anche il suggerimento di solo disabilitare il corpo di MyFunction() tramite # ifdef, sebbene ciò significhi che tutti i chiamanti ottengano la stessa versione di MyFunction(). Dalla tua affermazione sul problema , presumo che in realtà non sia ciò che desideri.

Se avete veramente bisogno di disabilitare MyFunction() tramite preprocessore definisce in una base per-source-file, poi lo farei in questo modo:

#ifdef SOMETHING 
#define MyFunction(x) NoOp_MyFunction(x) 

int NoOp_MyFunction(x) { } 
#endif 

Si potrebbe anche includere l'attuazione di NoOp_MyFunction() all'interno di le intestazioni di origine & per MyFunction(). È inoltre disponibile la flessibilità per aggiungere ulteriori informazioni di registrazione o di debug in NoOp_MyFunction() come .

+0

'sizeof' ha lo stesso problema con gli effetti collaterali. –

1

Sono un po 'riluttante a postare questa risposta perché l'uso di macerie macro può diventare la fonte di problemi. Tuttavia - se le chiamate alla funzione che si desidera siano scomparse vengono sempre utilizzate da sole in un'istruzione (cioè, non fanno mai parte di un'espressione più grande), quindi qualcosa di simile potrebbe funzionare (e gestisce vararg):

#ifdef SOMETHING 
#define foo (1) ? ((void) 0) : (void) 
#else 
#define foo MyFunction 
#endif 

Quindi, se avete la riga di codice:

foo("this is a %s - a++ is %d\n", "test", a++); 

si finirà dopo la fase di pre-elaborazione o come:

MyFunction("this is a %s - a++ is %d\n", "test", a++); 

o

(1) ? ((void) 0) : (void)("this is a %s - a++ is %d\n", "test", a++); 

che trasforma elenco di parametri di pseudo-funzione in un mazzo di espressioni separate dall'operatore virgola che non sarà mai valutata, poiché il condizionale restituisce sempre il risultato ((void) 0).

Una variante di questo è qualcosa di simile a ciò che ChrisW e Jonathan Leffler ha suggerito:

#ifdef SOMETHING 
#define foo if (0) MyFunction 
#else 
#define foo if (1) MyFunction 
#endif 

Questo è leggermente differente in quanto non richiede il compilatore per supportare le macro variadic (__VA_ARGS__).

Penso che questo possa essere utile per eliminare le chiamate di funzioni di traccia di debug che generalmente non vengono mai combinate in un'espressione più grande, ma al di là di ciò penso che sia una tecnica pericolosa.

Nota il potenziale di problemi, specialmente se i parametri nella chiamata producono effetti collaterali (questo è un problema generale con i macro - non solo questo hack). Nell'esempio, lo a++ verrà valutato solo se SOMETHING è definito nel build, altrimenti non lo è. Quindi se il codice dopo la chiamata dipende dal valore di a da incrementare, una delle build ha un bug.

Problemi correlati