variable para nombre macro imprimir formulario documentos depurar con codigo cerrar archivo analizar abrir c c-preprocessor

nombre - macro para imprimir en excel



C definición de macro para la impresión de depuración (11)

Intentando crear una macro que se pueda usar para imprimir mensajes de depuración cuando se define DEBUG, como el siguiente pseudo código:

#define DEBUG 1 #define debug_print(args ...) if (DEBUG) fprintf(stderr, args)

¿Cómo se logra esto con una macro?


Si usa un compilador C99

#define debug_print(fmt, ...) / do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)

Supone que está utilizando C99 (la notación de lista de argumentos variables no es compatible con versiones anteriores). La expresión do { ... } while (0) garantiza que el código actúa como una declaración (llamada de función). El uso incondicional del código asegura que el compilador siempre verifique que su código de depuración sea válido, pero el optimizador eliminará el código cuando DEBUG sea 0.

Si desea trabajar con #defdef DEPURAR, cambie la condición de prueba:

#ifdef DEBUG #define DEBUG_TEST 1 #else #define DEBUG_TEST 0 #endif

Y luego uso DEBUG_TEST donde utilicé DEBUG.

Si insiste en un literal de cadena para la cadena de formato (probablemente una buena idea), también puede introducir elementos como __FILE__ , __LINE__ y __func__ en la salida, lo que puede mejorar los diagnósticos:

#define debug_print(fmt, ...) / do { if (DEBUG) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, / __LINE__, __func__, __VA_ARGS__); } while (0)

Esto depende de la concatenación de cadenas para crear una cadena de formato más grande de lo que escribe el programador.

Si usa un compilador C89

Si está atascado con C89 y sin una extensión de compilación útil, entonces no hay una manera particularmente limpia de manejarlo. La técnica que solía usar era:

#define TRACE(x) do { if (DEBUG) dbg_printf x; } while (0)

Y luego, en el código, escribe:

TRACE(("message %d/n", var));

El doble paréntesis es crucial, y es por eso que tiene la notación divertida en la macro expansión. Como antes, el compilador siempre verifica la validez sintáctica del código (lo cual es bueno) pero el optimizador solo invoca la función de impresión si la macro DEBUG se evalúa como distinta de cero.

Esto requiere una función de soporte - dbg_printf () en el ejemplo - para manejar cosas como ''stderr''. Requiere que sepas cómo escribir funciones varargs, pero eso no es difícil:

#include <stdarg.h> #include <stdio.h> void dbg_printf(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); }

También puede usar esta técnica en C99, por supuesto, pero la técnica __VA_ARGS__ es más nítida porque usa la notación de función regular, no el hack de doble paréntesis.

¿Por qué es crucial que el compilador siempre vea el código de depuración?

[ Reposicionando comentarios hechos a otra respuesta. ]

Una idea central detrás de las implementaciones C99 y C89 es que el compilador siempre ve las instrucciones de depuración como printf. Esto es importante para el código de código a largo plazo que durará una o dos décadas.

Supongamos que un trozo de código ha estado mayormente inactivo (estable) durante varios años, pero ahora debe cambiarse. Se vuelve a habilitar el seguimiento de depuración, pero es frustrante tener que depurar el código de depuración (rastreo) porque hace referencia a las variables que se han renombrado o vuelto a escribir, durante los años de mantenimiento estable. Si el compilador (post preprocesador) siempre ve la instrucción de impresión, se asegura de que los cambios circundantes no invaliden los diagnósticos. Si el compilador no ve la declaración de impresión, no puede protegerlo contra su propio descuido (o el descuido de sus colegas o colaboradores). Ver " La práctica de la programación " por Kernighan y Pike, especialmente el Capítulo 8 (ver también Wikipedia en TPOP ).

Esta es la experiencia ''he estado allí, hecho eso'': utilicé esencialmente la técnica descrita en otras respuestas donde la versión sin depuración no ve las declaraciones similares a printf durante varios años (más de una década). Pero encontré el consejo en TPOP (ver mi comentario anterior), y luego habilité un código de depuración después de varios años, y me encontré con problemas de cambio de contexto al eliminar la depuración. Varias veces, tener la impresión siempre validada me ha salvado de problemas posteriores.

Yo uso NDEBUG para controlar aserciones solamente, y una macro separada (generalmente DEBUG) para controlar si el rastreo de depuración está incorporado en el programa. Incluso cuando el rastreo de depuración está incorporado, con frecuencia no quiero que la salida de depuración aparezca incondicionalmente, entonces tengo un mecanismo para controlar si aparece el resultado (niveles de depuración, y en lugar de llamar fprintf () directamente, llamo a una función de impresión de depuración que solo se imprime condicionalmente para que la misma compilación del código pueda imprimirse o no imprimirse según las opciones del programa). También tengo una versión del código ''subsistema múltiple'' para programas más grandes, de modo que puedo tener diferentes secciones del programa que producen diferentes cantidades de rastreo, bajo el control del tiempo de ejecución.

Estoy defendiendo que para todas las construcciones, el compilador debería ver las declaraciones de diagnóstico; sin embargo, el compilador no generará ningún código para las instrucciones de seguimiento de depuración a menos que la depuración esté habilitada. Básicamente, significa que el compilador verifica todo su código cada vez que compila, ya sea para la versión o la depuración. ¡Ésto es una cosa buena!

debug.h - versión 1.2 (1990-05-01)

/* @(#)File: $RCSfile: debug.h,v $ @(#)Version: $Revision: 1.2 $ @(#)Last changed: $Date: 1990/05/01 12:55:39 $ @(#)Purpose: Definitions for the debugging system @(#)Author: J Leffler */ #ifndef DEBUG_H #define DEBUG_H /* -- Macro Definitions */ #ifdef DEBUG #define TRACE(x) db_print x #else #define TRACE(x) #endif /* DEBUG */ /* -- Declarations */ #ifdef DEBUG extern int debug; #endif #endif /* DEBUG_H */

debug.h - versión 3.6 (2008-02-11)

/* @(#)File: $RCSfile: debug.h,v $ @(#)Version: $Revision: 3.6 $ @(#)Last changed: $Date: 2008/02/11 06:46:37 $ @(#)Purpose: Definitions for the debugging system @(#)Author: J Leffler @(#)Copyright: (C) JLSS 1990-93,1997-99,2003,2005,2008 @(#)Product: :PRODUCT: */ #ifndef DEBUG_H #define DEBUG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ /* ** Usage: TRACE((level, fmt, ...)) ** "level" is the debugging level which must be operational for the output ** to appear. "fmt" is a printf format string. "..." is whatever extra ** arguments fmt requires (possibly nothing). ** The non-debug macro means that the code is validated but never called. ** -- See chapter 8 of ''The Practice of Programming'', by Kernighan and Pike. */ #ifdef DEBUG #define TRACE(x) db_print x #else #define TRACE(x) do { if (0) db_print x; } while (0) #endif /* DEBUG */ #ifndef lint #ifdef DEBUG /* This string can''t be made extern - multiple definition in general */ static const char jlss_id_debug_enabled[] = "@(#)*** DEBUG ***"; #endif /* DEBUG */ #ifdef MAIN_PROGRAM const char jlss_id_debug_h[] = "@(#)$Id: debug.h,v 3.6 2008/02/11 06:46:37 jleffler Exp $"; #endif /* MAIN_PROGRAM */ #endif /* lint */ #include <stdio.h> extern int db_getdebug(void); extern int db_newindent(void); extern int db_oldindent(void); extern int db_setdebug(int level); extern int db_setindent(int i); extern void db_print(int level, const char *fmt,...); extern void db_setfilename(const char *fn); extern void db_setfileptr(FILE *fp); extern FILE *db_getfileptr(void); /* Semi-private function */ extern const char *db_indent(void); /**************************************/ ** MULTIPLE DEBUGGING SUBSYSTEMS CODE ** /**************************************/ /* ** Usage: MDTRACE((subsys, level, fmt, ...)) ** "subsys" is the debugging system to which this statement belongs. ** The significance of the subsystems is determined by the programmer, ** except that the functions such as db_print refer to subsystem 0. ** "level" is the debugging level which must be operational for the ** output to appear. "fmt" is a printf format string. "..." is ** whatever extra arguments fmt requires (possibly nothing). ** The non-debug macro means that the code is validated but never called. */ #ifdef DEBUG #define MDTRACE(x) db_mdprint x #else #define MDTRACE(x) do { if (0) db_mdprint x; } while (0) #endif /* DEBUG */ extern int db_mdgetdebug(int subsys); extern int db_mdparsearg(char *arg); extern int db_mdsetdebug(int subsys, int level); extern void db_mdprint(int subsys, int level, const char *fmt,...); extern void db_mdsubsysnames(char const * const *names); #endif /* DEBUG_H */

Argumento único Variante C99

Kyle Brandt preguntó:

De todos modos, para hacer esto, ¿así que debug_print aún funciona aunque no haya argumentos? Por ejemplo:

debug_print("Foo");

Hay un truco simple y anticuado:

debug_print("%s/n", "Foo");

La solución solo de GCC también brinda soporte para eso.

Sin embargo, puedes hacerlo con el sistema C99 directo usando:

#define debug_print(...) / do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while (0)

En comparación con la primera versión, se pierde la comprobación limitada que requiere el argumento ''fmt'', lo que significa que alguien podría llamar a ''debug_print ()'' sin argumentos. Si la pérdida de la verificación es un problema en absoluto es discutible.

Técnica específica de GCC

Algunos compiladores pueden ofrecer extensiones para otras formas de manejar listas de argumentos de longitud variable en macros. Específicamente, como se señaló por primera vez en los comentarios de Hugo Ideler , GCC le permite omitir la coma que normalmente aparecería después del último argumento ''fijo'' para la macro. También le permite usar ##__VA_ARGS__ en el texto de sustitución de macro, que elimina la coma que precede a la notación si, pero solo si, el símbolo anterior es una coma:

#define debug_print(fmt, ...) / do { if (DEBUG) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)

Esta solución conserva el beneficio de requerir el argumento de formato al tiempo que acepta argumentos opcionales después del formato.

Esta técnica también es compatible con Clang para compatibilidad GCC.

¿Por qué el ciclo do-while?

¿Cuál es el propósito del do while aquí?

Desea poder usar la macro para que parezca una llamada de función, lo que significa que irá seguido de un punto y coma. Por lo tanto, debe empaquetar el cuerpo de la macro a su gusto. Si usa una instrucción if sin el entorno do { ... } while (0) , tendrá:

/* BAD - BAD - BAD */ #define debug_print(...) / if (DEBUG) fprintf(stderr, __VA_ARGS__)

Ahora, supongamos que escribes:

if (x > y) debug_print("x (%d) > y (%d)/n", x, y); else do_something_useful(x, y);

Desafortunadamente, esa sangría no refleja el control real del flujo, porque el preprocesador produce un código equivalente a esto (sangría y llaves agregadas para enfatizar el significado real):

if (x > y) { if (DEBUG) fprintf(stderr, "x (%d) > y (%d)/n", x, y); else do_something_useful(x, y); }

El siguiente intento en la macro podría ser:

/* BAD - BAD - BAD */ #define debug_print(...) / if (DEBUG) { fprintf(stderr, __VA_ARGS__); }

Y el mismo fragmento de código ahora produce:

if (x > y) if (DEBUG) { fprintf(stderr, "x (%d) > y (%d)/n", x, y); } ; // Null statement from semi-colon after macro else do_something_useful(x, y);

Y lo else ahora es un error de sintaxis. El ciclo do { ... } while(0) evita estos dos problemas.

Hay otra forma de escribir la macro que podría funcionar:

/* BAD - BAD - BAD */ #define debug_print(...) / ((void)((DEBUG) ? fprintf(stderr, __VA_ARGS__) : 0))

Esto deja el fragmento del programa mostrado como válido. El molde (void) impide que se use en contextos en los que se requiere un valor, pero podría usarse como el operando izquierdo de un operador de coma donde la versión do { ... } while (0) no puede utilizarse. Si crees que deberías poder insertar código de depuración en tales expresiones, podrías preferir esto. Si prefiere requerir que la impresión de depuración actúe como una declaración completa, entonces la versión do { ... } while (0) es mejor. Tenga en cuenta que si el cuerpo de la macro involucra cualquier punto y coma (más o menos), entonces solo puede usar la notación do { ... } while(0) . Siempre funciona; el mecanismo de declaración de expresión puede ser más difícil de aplicar. También puede recibir advertencias del compilador con el formulario de expresión que prefiere evitar; dependerá del compilador y las banderas que use.

TPOP estuvo anteriormente en http://plan9.bell-labs.com/cm/cs/tpop y http://cm.bell-labs.com/cm/cs/tpop pero ambos son ahora (2015-08-10) roto.

Código en GitHub

Si tiene curiosidad, puede ver este código en GitHub en mi SOQ ( Questions) como archivos debug.c , debug.h y mddebug.c en el src/libsoq .


Aquí está la versión que uso:

#ifdef NDEBUG #define Dprintf(FORMAT, ...) ((void)0) #define Dputs(MSG) ((void)0) #else #define Dprintf(FORMAT, ...) / fprintf(stderr, "%s() in %s, line %i: " FORMAT "/n", / __func__, __FILE__, __LINE__, __VA_ARGS__) #define Dputs(MSG) Dprintf("%s", MSG) #endif


De acuerdo con ##__VA_ARGS__ , debería haber un ## antes de __VA_ARGS__ .

De lo contrario, una macro #define dbg_print(format, ...) printf(format, __VA_ARGS__) no compilará el siguiente ejemplo: dbg_print("hello world"); .


Esto es lo que uso:

#if DBG #include <stdio.h> #define DBGPRINT printf #else #define DBGPRINT(...) /**/ #endif

Tiene la ventaja agradable de manejar printf correctamente, incluso sin argumentos adicionales. En caso de DBG == 0, incluso el compilador más estúpido no tiene nada que masticar, por lo que no se genera código.


Haría algo como

#ifdef DEBUG #define debug_print(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) #else #define debug_print(fmt, ...) do {} while (0) #endif

Creo que esto es más limpio.


He estado guiándome por cómo hacerlo durante años, y acabo de presentar esta solución antes de ver esta pregunta. Primero, a diferencia de la respuesta de Leffler , no veo su argumento de que siempre se compilen las impresiones de depuración. Sí, terminas con impresiones de depuración que no compilan a veces, pero no es tan difícil compilarlas y probarlas antes de finalizar un proyecto. Digamos que no. Lo peor que ocurre es que tienes que corregir algunos puñados en un proyecto grande. Si su proyecto no es HUNGRÍA, es más fácil que eso.

Mi solución proporciona niveles de detalle de depuración; y si lo configura al nivel más alto, todos compilan. Si ha estado usando recientemente un nivel de detalle de depuración alto, probablemente todos ya compilan. Nunca he necesitado más de tres niveles, pero Jonathan dice que ha usado nueve. Su método también puede tener niveles de detalle de impresión de depuración. El mío se puede extender fácilmente para admitir cualquier cantidad de niveles, y puede admitir cualquier cantidad de niveles. El uso de mi método puede ser más simple; requiriendo solo dos declaraciones cuando se usa en tu código (aunque también estoy usando el tercero).

Contra el costo, el paso adicional de probarlos para ver si compilarán antes de la entrega es que

  1. Debes confiar en que se optimizarán, lo que es cierto que DEBERÍA suceder si tienes un nivel de optimización suficiente.
  2. Además, probablemente no lo harán si realiza una compilación de liberación con la optimización desactivada para fines de prueba (lo que es ciertamente raro); y es casi seguro que no lo harán durante la depuración, ejecutando docenas o cientos de declaraciones "if (DEBUG)" en tiempo de ejecución; lo que ralentiza la ejecución (que es mi principal objeción) y, lo que es menos importante, aumenta el tamaño de tu ejecutable o dll; y por lo tanto, tiempos de ejecución y compilación. Jonathan, sin embargo, me informa que su método puede hacerse para no compilar declaraciones.

Las sucursales son relativamente costosas en los procesadores modernos de precarga. Tal vez no es un gran problema si su aplicación no es una de tiempo crítico; pero si el rendimiento es un problema, entonces, sí, lo suficientemente grande como para preferir optar por un código de depuración algo más rápido (y posiblemente un lanzamiento más rápido, en casos raros, como se indica).

Entonces, lo que quería era una macro de impresión de depuración que no compila si no se va a imprimir, pero si lo hace. También quería niveles de depuración, de modo que, por ejemplo, si quería que las partes del código que no son de rendimiento no se imprimieran en algunas ocasiones, sino que imprimiera otras, podía establecer un nivel de depuración y hacer que se imprimieran las impresiones de depuración adicionales. encontré una forma de implementar niveles de depuración que determina si la impresión fue compilada o no. Lo logré de esta manera:

DebugLog.h:

// FILE: DebugLog.h // REMARKS: This is a generic pair of files useful for debugging. It provides three levels of // debug logging, currently; in addition to disabling it. Level 3 is the most information. // Levels 2 and 1 have progressively more. Thus, you can write: // DEBUGLOG_LOG(1, "a number=%d", 7); // and it will be seen if DEBUG is anything other than undefined or zero. If you write // DEBUGLOG_LOG(3, "another number=%d", 15); // it will only be seen if DEBUG is 3. When not being displayed, these routines compile // to NOTHING. I reject the argument that debug code needs to always be compiled so as to // keep it current. I would rather have a leaner and faster app, and just not be lazy, and // maintain debugs as needed. I don''t know if this works with the C preprocessor or not, // but the rest of the code is fully C compliant also if it is. #define DEBUG 1 #ifdef DEBUG #define DEBUGLOG_INIT(filename) debuglog_init(filename) #else #define debuglog_init(...) #endif #ifdef DEBUG #define DEBUGLOG_CLOSE debuglog_close #else #define debuglog_close(...) #endif #define DEBUGLOG_LOG(level, fmt, ...) DEBUGLOG_LOG ## level (fmt, ##__VA_ARGS__) #if DEBUG == 0 #define DEBUGLOG_LOG0(...) #endif #if DEBUG >= 1 #define DEBUGLOG_LOG1(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__) #else #define DEBUGLOG_LOG1(...) #endif #if DEBUG >= 2 #define DEBUGLOG_LOG2(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__) #else #define DEBUGLOG_LOG2(...) #endif #if DEBUG == 3 #define DEBUGLOG_LOG3(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__) #else #define DEBUGLOG_LOG3(...) #endif void debuglog_init(char *filename); void debuglog_close(void); void debuglog_log(char* format, ...);

DebugLog.cpp:

// FILE: DebugLog.h // REMARKS: This is a generic pair of files useful for debugging. It provides three levels of // debug logging, currently; in addition to disabling it. See DebugLog.h''s remarks for more // info. #include <stdio.h> #include <stdarg.h> #include "DebugLog.h" FILE *hndl; char *savedFilename; void debuglog_init(char *filename) { savedFilename = filename; hndl = fopen(savedFilename, "wt"); fclose(hndl); } void debuglog_close(void) { //fclose(hndl); } void debuglog_log(char* format, ...) { hndl = fopen(savedFilename,"at"); va_list argptr; va_start(argptr, format); vfprintf(hndl, format, argptr); va_end(argptr); fputc(''/n'',hndl); fclose(hndl); }

Usando las macros

Para usarlo, simplemente hazlo:

DEBUGLOG_INIT("afile.log");

Para escribir en el archivo de registro, simplemente hazlo:

DEBUGLOG_LOG(1, "the value is: %d", anint);

Para cerrarlo, haces:

DEBUGLOG_CLOSE();

aunque actualmente esto ni siquiera es necesario, técnicamente hablando, ya que no hace nada. Todavía estoy usando el CLOSE ahora, sin embargo, en caso de que cambie de opinión sobre cómo funciona, y quiero dejar el archivo abierto entre las declaraciones de registro.

Luego, cuando desee activar la impresión de depuración, solo edite el primer #define en el archivo de encabezado para decir, por ej.

#define DEBUG 1

Para que las declaraciones de registro se compilen a nada, haga

#define DEBUG 0

Si necesita información de un fragmento de código frecuentemente ejecutado (es decir, un alto nivel de detalle), puede escribir:

DEBUGLOG_LOG(3, "the value is: %d", anint);

Si define DEBUG como 3, compilar los niveles de registro 1, 2 y 3. Si lo configura en 2, obtiene los niveles de registro 1 y 2. Si lo establece en 1, solo obtiene las declaraciones de nivel 1 de registro.

En cuanto al ciclo do-while, dado que esto se evalúa como una función única o como nada, en lugar de una instrucción if, el ciclo no es necesario. OK, critíquenme por usar C en lugar de C ++ IO (y Qt''s QString :: arg () es una forma más segura de formatear variables cuando está en Qt, también; es bastante ingenioso, pero requiere más código y la documentación de formato no está tan organizada como podría ser, pero aun así he encontrado casos en los que es preferible), pero puedes poner el código que quieras en el archivo .cpp. También podría ser una clase, pero luego necesitaría crear una instancia y mantenerse actualizado, o hacer una nueva () y almacenarla. De esta manera, simplemente suelta #include, init y opcionalmente cierra declaraciones en su fuente, y está listo para comenzar a usarlo. Sin embargo, sería una buena clase si así lo desea.

Anteriormente había visto muchas soluciones, pero ninguna se ajustaba a mis criterios tan bien como esta.

  1. Se puede ampliar para hacer tantos niveles como desee.
  2. Se compila a nada si no se imprime.
  3. Centraliza IO en un lugar fácil de editar.
  4. Es flexible, utilizando el formato printf.

En adición,

  1. No requiere hackear para imprimir sin argumentos (por ejemplo, ERRLOG_LOG(3, "got here!"); ); permitiéndole usar, por ejemplo, el formateo .arg () más seguro de Qt. Funciona en MSVC, y por lo tanto, probablemente gcc. Utiliza ## en #define s, que no es estándar, como señala Leffler, pero es ampliamente compatible. (Puede recodificarlo para no usar ## si es necesario, pero deberá usar un hack como el que proporciona).

Advertencia: si olvida proporcionar el argumento del nivel de registro, MSVC afirma sin fundamento que el identificador no está definido.

Es posible que desee utilizar un nombre de símbolo del preprocesador que no sea DEBUG, ya que alguna fuente también define ese símbolo (por ejemplo, progs utilizando comandos ./configure para prepararse para la construcción). Me pareció natural cuando lo desarrollé. Lo desarrollé en una aplicación donde la DLL está siendo utilizada por otra cosa, y es más conveniente enviar copias de registro a un archivo; pero cambiarlo a vprintf () también funcionaría bien.

Espero que esto les ahorre a muchos de ustedes la pena de descubrir la mejor manera de hacer el registro de depuración; o te muestra uno que quizás prefieras Hace tiempo que intento resolver esto por décadas. Funciona en MSVC 2012 y 2015, y probablemente también en gcc; así como probablemente trabajando en muchos otros, pero no lo he probado en ellos.


Mi favorito de abajo es var_dump , que cuando se llama como:

var_dump("%d", count);

produce resultados como:

patch.c:150:main(): count = 0

Crédito a @ "Jonathan Leffler". Todos son felices C89:

Código

#define DEBUG 1 #include <stdarg.h> #include <stdio.h> void debug_vprintf(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } /* Call as: (DOUBLE PARENTHESES ARE MANDATORY) */ /* var_debug(("outfd = %d, somefailed = %d/n", outfd, somefailed)); */ #define var_debug(x) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", / __FILE__, __LINE__, __func__); debug_vprintf x; }} while (0) /* var_dump("%s" variable_name); */ #define var_dump(fmt, var) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", / __FILE__, __LINE__, __func__); debug_vprintf ("%s = " fmt, #var, var); }} while (0) #define DEBUG_HERE do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): HERE/n", / __FILE__, __LINE__, __func__); }} while (0)


Para una implementación portátil (ISO C90), podría usar doble paréntesis, como este;

#include <stdio.h> #include <stdarg.h> #ifndef NDEBUG # define debug_print(msg) stderr_printf msg #else # define debug_print(msg) (void)0 #endif void stderr_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } int main(int argc, char *argv[]) { debug_print(("argv[0] is %s, argc is %d/n", argv[0], argc)); return 0; }

o (hackish, no lo recomendaría)

#include <stdio.h> #define _ , #ifndef NDEBUG # define debug_print(msg) fprintf(stderr, msg) #else # define debug_print(msg) (void)0 #endif int main(int argc, char *argv[]) { debug_print("argv[0] is %s, argc is %d"_ argv[0] _ argc); return 0; }


Yo uso algo como esto:

#ifdef DEBUG #define D if(1) #else #define D if(0) #endif

De lo que acabo de usar D como prefijo:

D printf("x=%0.3f/n",x);

El compilador ve el código de depuración, no hay un problema de coma y funciona en todas partes. También funciona cuando printf no es suficiente, por ejemplo, cuando debe volcar una matriz o calcular algún valor de diagnóstico que sea redundante para el programa en sí.

EDITAR: Ok, podría generar un problema cuando hay algo else cerca que pueda ser interceptado por este inyectado if . Esta es una versión que lo repasa:

#ifdef DEBUG #define D #else #define D for(;0;) #endif


I believe this variation of the theme gives debug categories without the need to have a separate macro name per category.

I used this variation in an Arduino project where program space is limited to 32K and dynamic memory is limited to 2K. The addition of debug statements and trace debug strings quickly uses up space. So it is essential to be able to limit the debug trace that is included at compile time to the minimum necessary each time the code is built.

debug.h

#ifndef DEBUG_H #define DEBUG_H #define PRINT(DEBUG_CATEGORY, VALUE) do { if (DEBUG_CATEGORY & DEBUG_MASK) Serial.print(VALUE);} while (0); #endif

calling .cpp file

#define DEBUG_MASK 0x06 #include "Debug.h" ... PRINT(4, "Time out error,/t"); ...


#define debug_print(FMT, ARGS...) do { / if (DEBUG) / fprintf(stderr, "%s:%d " FMT "/n", __FUNCTION__, __LINE__, ## ARGS); / } while (0)