Comment créer une fonction de débogage uniquement qui prend une liste d'arguments variables ?Comme printf()

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

  •  08-06-2019
  •  | 
  •  

Question

J'aimerais créer une fonction de journalisation de débogage avec les mêmes paramètres que printf.Mais celui-ci peut être supprimé par le pré-processeur lors des builds optimisés.

Par exemple:

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

J'ai regardé les macros variadiques mais celles-ci ne sont pas disponibles sur toutes les plateformes. gcc les soutient, msvc ne fait pas.

Était-ce utile?

La solution

Je le fais toujours à l'ancienne, en définissant une macro (XTRACE, ci-dessous) qui correspond soit à une non-opération, soit à un appel de fonction avec une liste d'arguments variables.En interne, appelez vsnprintf pour pouvoir conserver la syntaxe 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);
}

Puis un commutateur #ifdef typique :

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

Eh bien, cela peut être un peu nettoyé, mais c'est l'idée de base.

Autres conseils

C’est ainsi que je débogue les impressions en C++.Définissez 'dout' (debug out) comme ceci :

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

Dans le code, j'utilise « dout » comme « cout ».

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

Si le préprocesseur remplace 'dout' par '0 && cout', notez que << a une priorité plus élevée que && et l'évaluation de court-circuit de && fait que la ligne entière est évaluée à 0.Puisque le 0 n’est pas utilisé, le compilateur ne génère aucun code pour cette ligne.

Voici quelque chose que je fais en C/C++.Tout d'abord, vous écrivez une fonction qui utilise les éléments varargs (voir le lien dans la publication de Stu).Ensuite, faites quelque chose comme ceci :


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

Tout ce que vous devez retenir est d'utiliser des doubles parenthèses lors de l'appel de la fonction de débogage, et toute la ligne sera supprimée dans le code non-DEBUG.

Une autre façon amusante de supprimer les fonctions variadiques est la suivante :

#define function sizeof

@CodingTheWheel :

Il y a un léger problème avec votre approche.Pensez à un appel tel que

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

Cela fonctionne bien dans la version de débogage, mais dans la version release, cela s'étendra à :

("x=%d", x);

Ce qui est parfaitement légitime en C et compilera et fonctionnera généralement sans effets secondaires mais génère du code inutile.L'approche que j'utilise habituellement pour éliminer ce problème est la suivante :

  1. Faites en sorte que la fonction XTrace renvoie un int (renvoyez simplement 0, la valeur de retour n'a pas d'importance)

  2. Remplacez le #define dans la clause #else par :

    0 && XTrace
    

Désormais, la version finale s'étendra à :

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

et tout optimiseur décent jettera le tout, car une évaluation de court-circuit aurait empêché quoi que ce soit après l'exécution du &&.

Bien sûr, juste au moment où j'écrivais cette dernière phrase, j'ai réalisé que peut-être le formulaire original pourrait également être optimisé et dans le cas d'effets secondaires, tels que les appels de fonction passés en paramètres à XTrace, cela pourrait être une meilleure solution puisqu'elle le fera. assurez-vous que les versions de débogage et de publication se comporteront de la même manière.

En C++, vous pouvez utiliser l'opérateur streaming pour simplifier les choses :

#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

et utilisez-le comme :

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

Vous pouvez ensuite implémenter une sortie de trace personnalisée pour les classes de la même manière que vous le feriez pour une sortie vers std::cout.

Ah, vsprintf() était ce qui me manquait.Je peux l'utiliser pour transmettre la liste d'arguments variables directement à 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);
}

Ensuite, enveloppez le tout dans une macro.

Sur quelles plateformes ne sont-ils pas disponibles ?stdarg fait partie de la bibliothèque standard :

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

Toute plate-forme ne le fournissant pas n'est pas une implémentation C standard (ou très, très ancienne).Pour ceux-là, vous devrez utiliser des varargs :

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

Une partie du problème avec ce type de fonctionnalité est qu'il nécessite souvent des macros variadiques.Ceux-ci ont été standardisés assez récemment (C99), et de nombreux anciens compilateurs C ne soutiennent pas la norme ou ne sont pas leur propre travail spécial.

Vous trouverez ci-dessous un en-tête de débogage que j'ai écrit et qui présente plusieurs fonctionnalités intéressantes :

  • Prend en charge la syntaxe C99 et C89 pour les macros de débogage
  • Activer/Désactiver la sortie en fonction de l'argument de la fonction
  • Sortie vers le descripteur de fichier (fichier io)

Note:Pour une raison quelconque, j'ai eu quelques légers problèmes de formatage du code.

#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_ */

Jetez un œil à ce fil :

Cela devrait répondre à votre question.

Ayant rencontré le problème aujourd'hui, ma solution est la macro suivante :

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

Vous pouvez ensuite appeler la fonction comme ceci :

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

Voici ce que j'utilise :

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 */
}

ce qui ne coûte absolument rien au moment de l'exécution lorsque l'indicateur _DEBUG_LOG est désactivé.

Il s'agit d'une version TCHAR de la réponse de l'utilisateur, elle fonctionnera donc en ASCII (normale), ou en mode Unicode (plus ou moins).

#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
}

Je dis "plus ou moins", car cela ne convertira pas automatiquement les arguments de chaîne ASCII en WCHAR, mais cela devrait vous sortir de la plupart des problèmes Unicode sans avoir à vous soucier d'envelopper la chaîne de format dans TEXT() ou de la faire précéder de L. .

Largement dérivé de MSDN :Récupération du code de la dernière erreur

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top