2009-03-25 11 views
158

Voglio scrivere una macro in C che accetta qualsiasi numero di parametri, non un numero specificoCome fare una macro variadic (numero variabile di argomenti)

esempio:

#define macro(X) something_complicated(whatever(X)) 

dove X è qualsiasi numero di parametri

Ho bisogno di questo perché whatever è sovraccarico e può essere chiamato con 2 o 4 parametri.

Ho provato a definire la macro due volte, ma la seconda definizione ha sovrascritto la prima!

Il compilatore con cui sto lavorando è g ++ (più precisamente, mingw)

+6

Vuoi C o C++? Se stai usando C, perché stai compilando con un compilatore C++? Per usare le macro variadiche C99 corrette, dovresti compilare un compilatore C che supporti C99 (come gcc), non un compilatore C++, poiché C++ non ha macro variadic standard. –

+0

Beh, ho assunto che il C++ sia un super set di C in questo senso .. – hasen

+0

http://tigcc.ticalc.org/doc/cpp.html#SEC13 ha una spiegazione dettagliata delle macro variadiche. – Gnubie

risposta

244

C99 way, supportato anche dal compilatore VC++.

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__) 
+6

Non credo che C99 richieda il ## prima di __VA_ARGS__. Potrebbe essere solo VC++. –

+85

Il motivo di ## prima di __VA_ARGS__ è che ingoia la virgola precedente nel caso in cui l'elenco di argomenti variabili sia vuoto, ad es. FOO ("a") si espande in printf ("a"). Questa è un'estensione di gcc (e vC++, forse), C99 richiede almeno un argomento per essere presente al posto dei puntini di sospensione. – jpalecek

+83

'##' non è necessario e non è portatile. '#define FOO (...) printf (__ VA_ARGS __)' fa il lavoro in modo portatile; il parametro 'fmt' può essere omesso dalla definizione. – alecov

4

spiegato per g ++ qui, anche se è parte di C99 così dovrebbe funzionare per tutti

http://www.delorie.com/gnu/docs/gcc/gcc_44.html

esempio veloce :

#define debug(format, args...) fprintf (stderr, format, args) 
+2

Le macro variadiche di GCC non sono macro variadiche C99. GCC _has_ C99 macro variadic, ma G ++ non li supporta, perché C99 non fa parte di C++. –

+0

In realtà g ++ compilerà macro C99 in file C++. Emetterà un avvertimento, tuttavia, se compilato con '-pedantic'. –

+2

Non è C99. C99 usa la macro __VA_ARGS__). – qrdl

22

non credo che sia possibile, si potrebbe fingere con doppie parentesi ... basta che non avete bisogno degli argomenti individualmente.

#define macro(ARGS) some_complicated (whatever ARGS) 
// ... 
macro((a,b,c)) 
macro((d,e)) 
+0

C99 aggiunge macro variadic. –

+16

Mentre è possibile avere una macro variadica, usare una doppia parentesi è un buon consiglio. –

+1

Il compilatore XC di Microchip non supporta i macro variadici, quindi questa doppia parentesi è il meglio che puoi fare. – gbmhunter

8
#define DEBUG 

#ifdef DEBUG 
    #define PRINT print 
#else 
    #define PRINT(...) ((void)0) //strip out PRINT instructions from code 
#endif 

void print(const char *fmt, ...) { 

    va_list args; 
    va_start(args, fmt); 
    vsprintf(str, fmt, args); 
     va_end(args); 

     printf("%s\n", str); 

} 

int main() { 
    PRINT("[%s %d, %d] Hello World", "March", 26, 2009); 
    return 0; 
} 

Se il compilatore non capisce le macro variadic, è anche possibile eliminare fuori PRINT con uno dei seguenti:

#define PRINT // 

o

#define PRINT if(0)print 

I primi commenti fuori le istruzioni PRINT, la seconda impedisce l'istruzione PRINT a causa di una condizione NULL se. Se l'ottimizzazione è impostata, il compilatore dovrebbe rimuovere le istruzioni mai eseguite come: if (0) print ("hello world"); o ((void) 0);

+6

#define STAMPA // non sostituirà STAMPA con // – bitc

+7

#define STAMPA se (0) la stampa non è una buona idea perché il codice chiamante potrebbe avere il proprio se per chiamare STAMPA. Migliore è: #define PRINT if (true); else print – bitc

+2

Lo standard "do nothing, gracely" è do {} while (0) – vonbrand

26

__VA_ARGS__ è il modo standard per farlo. Non utilizzare hack specifici del compilatore se non è necessario.

Sono davvero infastidito dal fatto che non posso commentare il post originale. In ogni caso, C++ non è un superset di C. È davvero stupido compilare il codice C con un compilatore C++. Non fare ciò che Donny non fa.

+3

* "È davvero sciocco compilare il tuo codice C con un compilatore C++" * => Non considerato da tutti (incluso me). Consulta ad esempio le linee guida del core C++: ** CPL.1: preferisci C++ a C **, [** CPL.2: se devi usare C, usa il sottoinsieme comune di C e C++ e compila il codice C come C++ * *] (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rcpl-subset). Ho difficoltà a pensare a ciò che "C-only-isms" ha davvero bisogno di fare in modo che valga la pena di non programmare nel sottoinsieme compatibile, e i comitati C e C++ hanno lavorato sodo per rendere disponibile quel sottoinsieme compatibile. – HostileFork

+1

@HostileFork Giusto abbastanza, sebbene * naturalmente * i C++ desiderino incoraggiare l'uso del C++. Altri non sono d'accordo, però; Linux Torvalds, ad esempio, ha apparentemente rifiutato più patch proposte per il kernel Linux che tentano di sostituire l'identificatore 'class' con' klass' per consentire la compilazione con un compilatore C++. Nota anche che ci sono alcune differenze che ti faranno inciampare; per esempio, l'operatore ternario non viene valutato allo stesso modo in entrambe le lingue e la parola chiave "in linea" significa qualcosa di completamente diverso (come ho appreso da una domanda diversa). –

+0

Per i progetti di sistemi realmente multipiattaforma come un sistema operativo, si vuole davvero aderire alla rigida C, perché i compilatori C sono molto più comuni.Nei sistemi embedded, ci sono ancora piattaforme senza compilatori C++. (Esistono piattaforme con solo compilatori C passabili!) I compilatori C++ mi rendono nervoso, in particolare per i sistemi cyber-fisici, e direi che non sono l'unico programmatore software/C incorporato con quella sensazione. – downbeat