¿Cómo se crea una función de solo depuración que toma una lista de argumentos variables?Como imprimirf()

StackOverflow https://stackoverflow.com/questions/15240

  •  08-06-2019
  •  | 
  •  

Pregunta

Me gustaría crear una función de registro de depuración con los mismos parámetros que printf.Pero uno que el preprocesador puede eliminar durante compilaciones optimizadas.

Por ejemplo:

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

He analizado macros variadas, pero no están disponibles en todas las plataformas. gcc los apoya, msvc no es.

¿Fue útil?

Solución

Todavía lo hago a la antigua usanza, definiendo una macro (XTRACE, a continuación) que se correlaciona con una llamada de función o no operativa con una lista de argumentos variables.Internamente, llame a vsnprintf para poder mantener la sintaxis de 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);
}

Luego, un típico cambio #ifdef:

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

Bueno, eso se puede limpiar un poco, pero es la idea básica.

Otros consejos

Así es como depuro impresiones en C++.Defina 'dout' (depuración) así:

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

En el código uso 'dout' al igual que 'cout'.

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

Si el preprocesador reemplaza 'dout' con '0 && cout', tenga en cuenta que << tiene mayor prioridad que && y la evaluación de cortocircuito de && hace que toda la línea se evalúe a 0.Como no se utiliza el 0, el compilador no genera ningún código para esa línea.

Aquí hay algo que hago en C/C++.En primer lugar, escribe una función que utiliza el material varargs (consulte el enlace en la publicación de Stu).Entonces haz 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 debe recordar es usar pares dobles al llamar a la función de depuración, y toda la línea se eliminará en el código que no sea DEBUG.

Otra forma divertida de eliminar funciones variadas es:

#define function sizeof

@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 la versión de lanzamiento se expandirá a:

("x=%d", x);

Que es C perfectamente legítimo y se compilará y generalmente se ejecutará sin efectos secundarios, pero genera código innecesario.El enfoque que suelo utilizar para eliminar ese problema es:

  1. Haga que la función XTrace devuelva un int (solo devuelva 0, el valor de retorno no importa)

  2. Cambie #define en la cláusula #else a:

    0 && XTrace
    

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

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

y cualquier optimizador decente descartará todo, ya que la evaluación de cortocircuito habría impedido que se ejecutara cualquier cosa después de &&.

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

En C++ puedes usar 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);
}

Luego puede implementar una salida de seguimiento personalizada para las clases de la misma manera que lo haría para la salida a std::cout.

Ah, vsprintf() era lo que me faltaba.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.

¿En qué plataformas no están disponibles?stdarg es parte de la biblioteca estándar:

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

Cualquier plataforma que no lo proporcione no es una implementación C estándar (o es muy, muy antigua).Para eso, tendrás que usar varargs:

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

Parte del problema con este tipo de funcionalidad es que a menudo requiere macros variádicos.Estos se estandarizaron bastante recientemente (C99), y muchos compiladores de C antiguos no admiten el estándar ni tienen su propio trabajo especial.

A continuación se muestra un encabezado de depuración que escribí y que tiene varias características interesantes:

  • Admite la sintaxis C99 y C89 para macros de depuración
  • Activar/desactivar la salida según el argumento de la función
  • Salida al descriptor de archivo (archivo io)

Nota:Por alguna razón tuve algunos pequeños 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_ */

Echa un vistazo a este hilo:

Debería responder a tu pregunta.

Habiendo encontrado el problema hoy, mi solución es la siguiente macro:

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

Luego puedes llamar a la función de esta manera:

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

Esto es lo que 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.

Esta es una versión 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
}

Digo "más o menos", porque no convertirá automáticamente los argumentos de la cadena ASCII a WCHAR, pero debería sacarte de la mayoría de los problemas Unicode sin tener que preocuparte por envolver la cadena de formato en TEXT() o precederla con L. .

Derivado en gran medida de MSDN:Recuperar el último código de error

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top