2012-02-16 14 views
11

Tengo una función que intenta registrar cosas en la consola y también en un archivo de registro, pero no funciona. El segundo uso del argumento de longitud variable da basura escrita en la consola. ¿Algunas ideas?El uso repetido de un argumento de función variable no funciona

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

Necesita usar vprintf por segunda vez, no printf. –

Respuesta

-2

Creo que de esta manera tiene más sentido:

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

Gracias Tony, pero eso tampoco funciona. Es como si el puntero se queda al final de la lista, por lo que el segundo uso se vuelve basura. – Neddie

+0

Sí, eso es exactamente lo que está sucediendo. 'va_list's siempre pasan de referencia. –

+1

Este ejemplo (incluso con 'vprintf' en lugar de' printf') es incorrecto de acuerdo con la página man: "Si ap se pasa a una función que usa va_arg (ap, type), entonces el valor de ap no está definido después del retorno de esa función ". En linux x86-32 funciona según lo previsto, pero no p. en x86-64. –

2

Actualiza su compilador, que es más como C++:

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

    printf(fmt, args...); 
} 

Aunque, por supuesto, sería entonces el buen gusto para proporcionar versiones seguras de printf y fprintf.

+0

El ejemplo anterior requiere C++ 11. –

+1

@DavidGiven: sí (así * actualice su compilador *). –

+0

Creo que vfprintf necesita ser fprintf en su lugar aquí - estás pasando los argumentos reales en lugar de una va_list. –

8

El código original falla porque intenta usar printf() donde necesita usar vprintf(). Tomando puntos dudosos como los logOpen y logClose declaraciones a su valor nominal (dada la notación, es de suponer que son macros que abren y cierran la corriente flog archivo), el código debe ser:

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

No hay requisito particular para usar dos variables separadas va_list; está perfectamente bien usar el mismo dos veces siempre que use va_end() antes de usar va_start() nuevamente.

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

Cuando un valor va_list se pasa a otra función (vfprintf() y vprintf() en este código), se debe asumir que ya no puedan utilizarse en la función actual es. Solo es seguro llamar al va_end() en él.

No hay necesidad de va_copy() en este código. Funciona, pero no es necesario. Es necesario va_copy() en otras circunstancias, como cuando se pasa a la función de un va_list y que necesita para procesar la lista dos veces:

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

Tenga en cuenta que en este código, es responsabilidad del código de llamada para llamar va_end() en args1. En efecto, la norma dice:

cada invocación de la va_start y va_copy macros se corresponde con una invocación correspondiente de la va_end macro en la misma función.

Dado que la función logVprintf() no llama ya sea va_start o va_copy para inicializar args1, no puede legítimamente llamar va_end en args1. Por otro lado, el estándar requiere que llame al va_end para args2.

La función logPrintf() puede ser implementada en términos de logVprintf() ahora:

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

Esta estructura - una función operativa que toma un va_list y una función de cubierta que lleva puntos suspensivos (argumentos variables) y los pasa a la operativa función después de la conversión a va_list - a menudo es una buena manera de trabajar. Tarde o temprano, generalmente encontrará una necesidad para la versión con un argumento va_list.

+2

Esta es la respuesta correcta que resuelve directamente la pregunta de OP sin recomendar cambiar su arquitectura. – Vortico

Cuestiones relacionadas