2008-08-18 22 views
35

me gustaría hacer una función de registro de depuración con los mismos parámetros que printf. Pero uno que puede ser eliminado por el pre-procesador durante compilaciones optimizadas.¿Cómo se crea una función solo de depuración que toma una lista de argumentos variables? Al igual que printf()

Por ejemplo:

Debug_Print("Warning: value %d > 3!\n", value); 

He mirado en las macros variadic pero los que no están disponibles en todas las plataformas. gcc los admite, msvc no.

+0

Stu, MSVC ¿apoya funciones variadic, que no soporta macros variadic. Editar: Lo malo: la compatibilidad con macros variadic se introdujo en Visual C++ 2005. – hyperlogic

+0

Véase también la macro [C '# define' para la impresión de depuración] (https://stackoverflow.com/questions/1644868/c-define-macro-for -debug-printing). Tenga en cuenta, en particular, que generalmente es mejor asegurarse de que el compilador compila (pero optimiza) el código de una macro de depuración, de modo que el código siempre se comprueba y, por lo tanto, siempre es correcto. De lo contrario, bit-rot puede establecerse y cuando se reactiva la macro de depuración una década más tarde, se encuentra que ya no se compila. –

Respuesta

22

todavía lo hago de la manera antigua, mediante la definición de una macro (xtrace, más adelante) que se correlaciona bien a un no-op o una llamada a la función con una lista de argumentos variable. Internamente, llame vsnprintf para que pueda mantener la sintaxis printf:

#include <stdio.h> 

void XTrace0(LPCTSTR lpszText) 
{ 
    ::OutputDebugString(lpszText); 
} 

void XTrace(LPCTSTR lpszFormat, ...) 
{ 
    va_list args; 
    va_start(args, lpszFormat); 
    int nBuf; 
    TCHAR szBuffer[512]; // get rid of this hard-coded buffer 
    nBuf = _vsnprintf(szBuffer, 511, lpszFormat, args); 
    ::OutputDebugString(szBuffer); 
    va_end(args); 
} 

A continuación, un interruptor típico #ifdef:

#ifdef _DEBUG 
#define XTRACE XTrace 
#else 
#define XTRACE 
#endif 

bien que puede ser limpiado un poco, pero es la idea básica.

+0

Excelente respuesta, pero debería haber usado ** _ vsntprintf ** para hacerlo realmente compatible con Unicode. He agregado mi propia versión, porque tenía un requisito para anteponer una cadena (como [DEBUG]). http://stackoverflow.com/a/39186784/912236 – Orwellophile

+0

'_vsnprintf()' es específico para implementaciones particulares (compatibles con Microsoft), y obsoleto. 'vsnprintf()' es estándar. – Peter

1

¿Qué plataformas no son disponibles en? stdarg es parte de la biblioteca estándar:

http://www.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html

Cualquier plataforma no siempre que no es una implementación estándar de C (o muy, muy viejo). Para aquellos, que tendrá que utilizar varargs:

http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html

+0

http://msdn.microsoft.com/en-us/library/kb57fad8(VS.71).aspx - Para el registro, este artículo incluso le muestra cómo usar los varargs de la vieja escuela como un extra. – Stu

+0

Parece que el soporte para macros variadic se introdujo en Visual C++ 2005. – hyperlogic

11

Aquí hay algo que te gusta hacer en C/C++. Primero, escribes una función que usa las cosas varargs (mira el enlace en la publicación de Stu). A continuación, hacer algo como esto:


int debug_printf(const char *fmt, ...); 
#if defined(DEBUG) 
    #define DEBUG_PRINTF(x) debug_printf x 
#else 
    #define DEBUG_PRINTF(x) 
#endif 

DEBUG_PRINTF(("Format string that takes %s %s\n", "any number", "of args")); 

Todo lo que tiene que recordar es utilizar dobles parens cuando se llama a la función de depuración, y toda la línea obtendrá eliminado en el código no depuración.

2

Ah, vsprintf() era lo que me estaba perdiendo. Puedo usar esto para pasar la lista de argumentos variables directamente a printf():

#include <stdarg.h> 
#include <stdio.h> 

void DBG_PrintImpl(char * format, ...) 
{ 
    char buffer[256]; 
    va_list args; 
    va_start(args, format); 
    vsprintf(buffer, format, args); 
    printf("%s", buffer); 
    va_end(args); 
} 

Luego envuelva todo en una macro.

4

Otra manera divertida de apagar las funciones variadic es:

#define function sizeof 
2

En C++ se puede utilizar el operador de streaming para simplificar las cosas:

#if defined _DEBUG 

class Trace 
{ 
public: 
    static Trace &GetTrace() { static Trace trace; return trace; } 
    Trace &operator << (int value) { /* output int */ return *this; } 
    Trace &operator << (short value) { /* output short */ return *this; } 
    Trace &operator << (Trace &(*function)(Trace &trace)) { return function (*this); } 
    static Trace &Endl (Trace &trace) { /* write newline and flush output */ return trace; } 
    // and so on 
}; 

#define TRACE(message) Trace::GetTrace() << message << Trace::Endl 

#else 
#define TRACE(message) 
#endif 

y usarlo como:

void Function (int param1, short param2) 
{ 
    TRACE ("param1 = " << param1 << ", param2 = " << param2); 
} 

a continuación, puede aplicar resultado de seguimiento personalizado para las clases de la misma manera que lo haría para dar salida a std::cout.

1

Parte del problema con este tipo de funcionalidad es que a menudo requiere macros variadic.Estos fueron estandarizados bastante recientemente (C99), y muchos de los compiladores de C antiguos no son compatibles con el estándar, o tienen su propio trabajo especial alrededor.

A continuación se muestra una cabecera de depuración escribí que tiene varias características interesantes:

  • Soporta C99 y C89 sintaxis para macros de depuración
  • salida de habilitación/Desactivar basado en argumento de la función
  • salida a un archivo descriptor (archivo io)

Nota: Por alguna razón tuve algunos problemas con el formato del código.

#ifndef _DEBUG_H_ 
#define _DEBUG_H_ 
#if HAVE_CONFIG_H 
#include "config.h" 
#endif 

#include "stdarg.h" 
#include "stdio.h" 

#define ENABLE 1 
#define DISABLE 0 

extern FILE* debug_fd; 

int debug_file_init(char *file); 
int debug_file_close(void); 

#if HAVE_C99 
#define PRINT(x, format, ...) \ 
if (x) { \ 
if (debug_fd != NULL) { \ 
fprintf(debug_fd, format, ##__VA_ARGS__); \ 
} \ 
else { \ 
fprintf(stdout, format, ##__VA_ARGS__); \ 
} \ 
} 
#else 
void PRINT(int enable, char *fmt, ...); 
#endif 

#if _DEBUG 
#if HAVE_C99 
#define DEBUG(x, format, ...) \ 
if (x) { \ 
if (debug_fd != NULL) { \ 
fprintf(debug_fd, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \ 
} \ 
else { \ 
fprintf(stderr, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \ 
} \ 
} 

#define DEBUGPRINT(x, format, ...) \ 
if (x) { \ 
if (debug_fd != NULL) { \ 
fprintf(debug_fd, format, ##__VA_ARGS__); \ 
} \ 
else { \ 
fprintf(stderr, format, ##__VA_ARGS__); \ 
} \ 
} 
#else /* HAVE_C99 */ 

void DEBUG(int enable, char *fmt, ...); 
void DEBUGPRINT(int enable, char *fmt, ...); 

#endif /* HAVE_C99 */ 
#else /* _DEBUG */ 
#define DEBUG(x, format, ...) 
#define DEBUGPRINT(x, format, ...) 
#endif /* _DEBUG */ 

#endif /* _DEBUG_H_ */ 
+0

Parece extraño usar '' debug_fd' 'para contener un puntero de archivo en lugar de un descriptor de archivo; sería más convencional usar '' debug_fp' '. –

+0

¡El ## __ VA_ARGS__ es increíble! no sabia sobre eso. ¡Gracias! –

3

@CodingTheWheel:

Hay un pequeño problema con su enfoque. Considere una llamada como

XTRACE("x=%d", x); 

Esto funciona bien en la versión de depuración, pero en el comunicado de construir que se ampliará a:

("x=%d", x); 

que es C perfectamente legítimo y compilará y suele funcionar sin efectos secundarios -efectúa pero genera código innecesario. El enfoque que suelen utilizar para eliminar ese problema es:

  1. hacer que la función devuelve un int xtrace (sólo devuelve 0, el valor de retorno no importa)

  2. Cambiar el # define en el # cláusula else a:

    0 && XTrace 
    

Ahora la versión de lanzamiento se ampliará a:

0 && XTrace("x=%d", x); 

y cualquier optimizador decente lo descartará todo, ya que la evaluación de cortocircuitos hubiera impedido que se ejecute alguna vez el & &.

Por supuesto, justo cuando escribí esa última oración, me di cuenta de que tal vez la forma original también podría optimizarse y en el caso de los efectos secundarios, como llamadas a funciones pasadas como parámetros a XTrace, podría ser una mejor solución ya que se asegurará de que las versiones de depuración y liberación se comporten de la misma manera.

20

Así es como elimino las impresiones en C++. Definir 'dout' (depuración fuera) así:

#ifdef DEBUG 
#define dout cout 
#else 
#define dout 0 && cout 
#endif 

En el código que utilizo 'dout' igual 'cout'.

dout << "in foobar with x= " << x << " and y= " << y << '\n'; 

Si los sustituye preprocesador 'dout' con la nota '0' & & cout que < < tiene mayor precedencia que & & y evaluación de cortocircuito de & & hace que toda la línea de evaluar a 0.Como el 0 no se usa, el compilador no genera ningún código para esa línea.

+0

Encuentro útil la siguiente modificación: '#define dout cout << __FILE__ <<" ("<< __LINE__ <<") DEPURACIÓN: "' – cledoux

+0

En este caso no será posible usar, por ejemplo: 'dout << setw (10) << "test"; ' – sohaibafifi

+2

Esto genera advertencias (gcc -Wall) cuando la depuración no está activada: " warning: la sentencia no tiene efecto [-Wunused-value] 0 && std :: cout << " test "; –

0

Habiendo llegado a través del problema hoy en día, mi solución es la siguiente macro:

static TCHAR __DEBUG_BUF[1024] 
    #define DLog(fmt, ...) swprintf(__DEBUG_BUF, fmt, ##__VA_ARGS__); OutputDebugString(__DEBUG_BUF) 

continuación, puede llamar a la función como esta:

int value = 42; 
    DLog(L"The answer is: %d\n", value); 
0

Esto es lo que yo uso:

inline void DPRINTF(int level, char *format, ...) 
{ 
# ifdef _DEBUG_LOG 
     va_list args; 
     va_start(args, format); 
     if(debugPrint & level) { 
       vfprintf(stdout, format, args); 
     } 
     va_end(args); 
# endif /* _DEBUG_LOG */ 
} 

que no cuesta absolutamente nada en tiempo de ejecución cuando el indicador _DEBUG_LOG está desactivado.

+1

Esto generará advertencias para los parámetros no utilizados en la función. –

0

Esta es una versión de TCHAR de la respuesta del usuario, por lo que funcionará como ASCII (normal), o modo Unicode (más o menos).

#define DEBUG_OUT(fmt, ...) DEBUG_OUT_TCHAR(  \ 
      TEXT(##fmt), ##__VA_ARGS__) 
#define DEBUG_OUT_TCHAR(fmt, ...)     \ 
      Trace(TEXT("[DEBUG]") #fmt,   \ 
      ##__VA_ARGS__) 
void Trace(LPCTSTR format, ...) 
{ 
    LPTSTR OutputBuf; 
    OutputBuf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, \ 
      (size_t)(4096 * sizeof(TCHAR))); 
    va_list args; 
    va_start(args, format); 
    int nBuf; 
    _vstprintf_s(OutputBuf, 4095, format, args); 
    ::OutputDebugString(OutputBuf); 
    va_end(args); 
    LocalFree(OutputBuf); // tyvm @sam shaw 
} 

me dicen, "más o menos", porque no va a convertir automáticamente los argumentos de cadena ASCII a WCHAR, pero debe salir de la mayoría de los raspones Unicode sin tener que preocuparse de envolver la cadena de formato en TEXTO() o la precede con L.

en gran parte derivada de MSDN: Retrieving the Last-Error Code

+1

hey, se olvida de utilizar localfree api y causará la pérdida de memoria.Y aunque lo liberes, no es una buena idea usar Heap para este caso. –

+0

@SamShaw, bien visto, también agregué un aviso que rechazaba cualquier otra responsabilidad señalando sus orígenes MSDN. ¿Estás sugiriendo que montón es malo porque podríamos estar registrando un error al asignar()? No puedo discutir con eso, ya que mientras escribía el código anterior, me di cuenta de que nuestro Registrador existente estaba obteniendo información del registro, y nos volvimos un poco locos ... * Nota: mi reino para una macro VS que me dará la versión de TCHAR de cualquier función dada * – Orwellophile

Cuestiones relacionadas