c++ - todas - tipos de funciones en c
¿Cómo se crea una función de depuración que toma una lista de argumentos variables? Como printf() (13)
¿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 muy, muy antigua). Para ellos, tendrás que usar varargs:
http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html
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.
Por ejemplo:
Debug_Print("Warning: value %d > 3!/n", value);
He visto macros variadas, pero esas no están disponibles en todas las plataformas. gcc
admite, msvc
no.
Aquí hay algo que hago en C / C ++. Primero, escribes una función que usa las cosas varargs (mira 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 tiene que recordar es usar doble par cuando se llama a la función de depuración, y toda la línea se eliminará en código que no sea DEBUG.
@CodingTheWheel:
Hay un pequeño problema con su enfoque. Considere una llamada como
XTRACE("x=%d", x);
Esto funciona bien en la compilación de depuración, pero en la compilación de lanzamiento se expandirá a:
("x=%d", x);
Lo cual es perfectamente legítimo C y se compilará y generalmente se ejecutará sin efectos secundarios, pero genera código innecesario. El enfoque que generalmente uso para eliminar ese problema es:
Haga que la función XTrace devuelva un int (solo devuelva 0, el valor de retorno no importa)
Cambie el #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 del cortocircuito hubiera impedido cualquier cosa después de la ejecución de &&.
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 las llamadas a funciones pasadas como parámetros a XTrace, podría ser una solución mejor ya que asegúrese de que las versiones de depuración y versión se comporten de la misma manera.
Parte del problema con este tipo de funcionalidad es que a menudo requiere macros variadas. Estos fueron estandarizados bastante recientemente (C99), y muchos viejos compiladores de C no son compatibles con el estándar, o tienen su propio trabajo especial.
A continuación se muestra un encabezado de depuración que escribí que tiene varias características interesantes:
- Admite la sintaxis C99 y C89 para macros de depuración
- Habilitar / Deshabilitar salida en función del argumento de la función
- Salida al descriptor de archivo (archivo io)
Nota: por alguna razón tuve algunos problemas de formato de código leve.
#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_ */
Así es como elimino las impresiones en C ++. Defina ''dout'' (depuración) de esta manera:
#ifdef DEBUG
#define dout cout
#else
#define dout 0 && cout
#endif
En el código utilizo ''dout'' como ''cout''.
dout << "in foobar with x= " << x << " and y= " << y << ''/n'';
Si el preprocesador reemplaza ''dout'' por ''0 && cout'', tenga en cuenta que << tiene una precedencia mayor que && y la evaluación de cortocircuito de && hace que toda la línea se evalúe a 0. Como el 0 no se usa, el compilador no genera ningún código para esa linea
En C ++ puedes usar el operador de transmisión 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 úsalo como:
void Function (int param1, short param2)
{
TRACE ("param1 = " << param1 << ", param2 = " << param2);
}
Luego puede implementar un resultado de rastreo personalizado para las clases de la misma manera que lo haría para enviar a std::cout
.
Otra forma divertida de eliminar funciones variadas es:
#define function sizeof
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)
A continuación, puede 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.
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 envuelve todo en una macro.
Todavía lo hago de la manera antigua, definiendo una macro (XTRACE, a continuación) que se correlaciona con una llamada no operativa o con una lista de argumentos variables. Internamente, llame a vsnprintf para que pueda conservar 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 interruptor #ifdef:
#ifdef _DEBUG
#define XTRACE XTrace
#else
#define XTRACE
#endif
Bueno, eso se puede limpiar bastante, pero es la idea básica.
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
}
Digo, "más o menos", porque no convertirá automáticamente los argumentos de cadena ASCII en WCHAR, pero debería sacarlo de la mayoría de los rasguños Unicode sin tener que preocuparse de envolver la cadena de formato en TEXTO () o precederlo con L .
Derivado en gran medida de MSDN: recuperación del código de último error
Eche un vistazo a este hilo:
Debería responder tu pregunta.