2012-02-16 14 views
11

Mam funkcję, która próbuje logować dane do konsoli, a także do pliku dziennika, ale to nie działa. Drugie użycie argumentu o zmiennej długości powoduje, że śmieci są zapisywane na konsoli. Jakieś pomysły?Powtórne użycie argumentu funkcji variadic nie działa

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

Musisz użyć vprintf po raz drugi, a nie printf. –

Odpowiedz

-2

myślę w ten sposób większy sens:

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

Dzięki Tony, ale to też nie działa. To tak, jakby wskaźnik został umieszczony na końcu listy, więc drugie użycie zostaje usunięte. – Neddie

+0

Tak, właśnie to się dzieje. 'va_list's to zawsze" pass-by-reference ". –

+1

Ten przykład (nawet z 'vprintf' zamiast' printf') jest niepoprawny zgodnie ze stroną man: "Jeśli ap jest przekazywane do funkcji, która używa va_arg (ap, type), to wartość ap jest niezdefiniowana po powrocie ta funkcja. " Na Linuksie x86-32 działa zgodnie z przeznaczeniem, ale nie na przykład na x86-64. –

2

uaktualnić swoje kompilatora, który jest bardziej jak C++:

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

    printf(fmt, args...); 
} 

Choć oczywiście byłoby wtedy być dobry smak, aby zapewnić wersje bezpiecznych typów printf i fprintf.

+0

Powyższy przykład wymaga C++ 11. –

+1

@DavidGiven: yes (w ten sposób * zaktualizuj swój kompilator *). –

+0

Myślę, że vfprintf musi być zamiast tego fprintf - przekazujesz rzeczywiste argumenty zamiast va_list. –

8

Oryginalny kod kończy się niepowodzeniem, ponieważ próbuje użyć printf(), gdzie musi użyć vprintf(). Biorąc wątpliwych punktów jak logOpen i logClose sprawozdaniu w wartości nominalnej (biorąc pod uwagę zapis, przypuszczalnie są makra, które otwierają i zamykają strumień flog pliku), kod powinien być:

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); 
} 

Nie ma szczególności wymóg stosowania dwie oddzielne zmienne va_list; jest całkowicie OK, aby użyć tego samego dwa razy , o ile użyjesz va_end(), zanim ponownie użyjesz 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); 
} 

Gdy wartość va_list jest przekazywane do innej funkcji (vfprintf() i vprintf() w tym kodzie), należy założyć, że nie jest już wykorzystywane w bieżącej funkcji jest. Bezpiecznie jest wywoływać na nim tylko va_end().

W tym kodzie nie ma potrzeby stosowania va_copy(). Działa, ale nie jest potrzebna. Musisz va_copy() w innych okolicznościach, takich jak wtedy, gdy funkcja jest podjęło va_list i trzeba przetworzyć listę dwukrotnie:

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); 
} 

pamiętać, że w tym kodzie, to odpowiedzialność kodu wywołującego zadzwonić va_end() na args1. Rzeczywiście, średnia mówi:

Każdy wezwaniem va_start i va_copy makr powinna być dopasowana przez odpowiedni wezwaniem va_end makra w tej samej funkcji.

Ponieważ funkcja logVprintf() nie wymaga ani va_start lub va_copy zainicjować args1, nie można zasadnie nazwać va_end na args1. Z drugiej strony standard wymaga, aby wywołać va_end dla args2.

Funkcja logPrintf() mogą być realizowane w warunkach logVprintf() się:

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

Ta struktura - funkcja operacyjna, która przyjmuje va_list i funkcję pokrywy, które ma symbol (zmienne argumentów) i przekazuje je do eksploatacji funkcja po konwersji na va_list - często jest dobrym sposobem na pracę. Prędzej czy później zazwyczaj potrzebujesz wersji z argumentem va_list.

+2

To jest poprawna odpowiedź, która bezpośrednio rozwiązuje pytanie OP bez zalecania zmiany ich architektury. – Vortico

Powiązane problemy