2012-02-16 13 views
11

Ho una funzione che tenta di registrare elementi nella console e anche in un file di registro, ma non funziona. Il secondo uso dell'argomento della lunghezza variabile dà immondizia scritta alla console. Qualche idea?L'uso ripetuto di un argomento di funzione variad non funziona

void logPrintf(const char *fmt, ...) { 
     va_list ap; // log to logfile 
     va_start(ap, fmt); 
     logOpen; 
     vfprintf(flog, fmt, ap); 
     logClose; 
     va_end(ap); 
     va_list ap2; // log to console 
     va_start(ap2, fmt); 
     printf(fmt, ap2); 
     va_end(ap2); 
    } 
+1

È necessario utilizzare vprintf la seconda volta, non printf. –

risposta

-2

Penso che in questo modo ha più senso:

void logPrintf(const char *fmt, ...) { 
     va_list ap; // log to logfile 
     va_start(ap, fmt); 
     logOpen; 
     vfprintf(flog, fmt, ap); //logfile 
     printf(fmt, ap); //console 
     logClose; 
     va_end(ap); 
    } 
+0

Grazie Tony, ma anche questo non funziona. È come se il puntatore venisse lasciato alla fine dell'elenco in modo che il secondo utilizzo venisse cancellato. – Neddie

+0

Sì, è esattamente quello che sta succedendo. 'va_list's sono sempre pass-by-reference. –

+1

Questo esempio (anche con 'vprintf' invece di' printf') è errato in base alla pagina man: "Se ap viene passato a una funzione che utilizza va_arg (ap, type), allora il valore di ap non è definito dopo il ritorno di quella funzione. " Su linux x86-32 funziona come previsto, ma non per es. su x86-64. –

2

Amplia il tuo compilatore, che è più simile a C++:

template <typename... Args> 
void logPrintf(const char *fmt, Args&&... args) { 
    logOpen; 
    fprintf(flog, fmt, args...); 
    logClose; 

    printf(fmt, args...); 
} 

Anche se naturalmente sarebbe allora il buon gusto di fornire versioni typesafe di printf e fprintf.

+0

L'esempio precedente richiede C++ 11. –

+1

@DavidGiven: sì (quindi * aggiorna il compilatore *). –

+0

Penso vfprintf ha bisogno di essere fprintf invece qui - tu sei passando gli argomenti reali e non un va_list. –

8

Il codice originale non riesce perché tenta di utilizzare printf() dove è necessario utilizzare vprintf(). Prendendo punti discutibili come le dichiarazioni logOpen e logClose al valore nominale (dato la notazione, presumibilmente sono le macro che si aprono e chiudono il flusso flog file), il codice dovrebbe essere:

void logPrintf(const char *fmt, ...) { 
    va_list ap; 
    va_start(ap, fmt); 
    logOpen; 
    vfprintf(flog, fmt, ap); 
    logClose; 
    va_end(ap); 
    va_list ap2; 
    va_start(ap2, fmt); 
    vprintf(fmt, ap2); 
    va_end(ap2); 
} 

Non c'è alcun requisito particolare da usare due variabili separate va_list; è perfettamente OK usare lo stesso due volte finché si utilizza va_end() prima di utilizzare nuovamente va_start().

void logPrintf(const char *fmt, ...) { 
    va_list ap; 
    va_start(ap, fmt); 
    logOpen; 
    vfprintf(flog, fmt, ap); 
    logClose; 
    va_end(ap); 
    va_start(ap, fmt); 
    vprintf(fmt, ap); 
    va_end(ap); 
} 

Quando un valore va_list si passa ad un'altra funzione (vfprintf() e vprintf() in questo codice), si deve presumere che non è più utilizzabile nella funzione attuale. Su di esso è sicuro chiamare lo va_end().

In questo codice non è necessario va_copy(). Funziona, ma non è necessario. È necessario va_copy() in altre circostanze, ad esempio quando la funzione viene passata una va_list ed è necessario elaborare l'elenco due volte:

void logVprintf(const char *fmt, va_list args1) 
{ 
    va_list args2; 
    va_copy(args2, args1); 
    logOpen; 
    vfprintf(flog, fmt, args1); 
    logClose; 
    vprintf(fmt, args2); 
    va_end(args2); 
} 

Si noti che in questo codice, è responsabilità del codice chiamante per chiamare va_end() su args1. Infatti, lo standard dice:

ogni chiamata va_start e va_copy macro sono abbinati da una corrispondente invocazione del va_end macro nella stessa funzione.

Poiché la funzione logVprintf() non chiama né va_start o va_copy inizializzare args1, non può legittimamente chiamare va_end su args1. D'altra parte, lo standard richiede di chiamare va_end per args2.

La funzione logPrintf() può essere implementata in termini di logVprintf() ora:

void logPrintf(const char *fmt, ...) 
{ 
    va_list args; 
    va_start(args, fmt); 
    logVprintf(fmt, args); 
    va_end(args); 
} 

Tale struttura - una funzione operativa che accetta un va_list e una funzione di copertura che richiederà ellissi (argomenti variabili) e li passa al operativa funzione dopo la conversione in un va_list - è spesso un buon modo di lavorare. Prima o poi, di solito trovi la necessità della versione con un argomento va_list.

+2

Questa è la risposta corretta che risolve direttamente la domanda dell'OP senza raccomandare di cambiare la propria architettura. – Vortico

Problemi correlati