Wie erstellt man eine reine Debug-Funktion, die eine Liste variabler Argumente akzeptiert?Wie printf()

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

  •  08-06-2019
  •  | 
  •  

Frage

Ich möchte eine Debug-Protokollierungsfunktion mit denselben Parametern erstellen wie printf.Aber eines, das bei optimierten Builds vom Vorprozessor entfernt werden kann.

Zum Beispiel:

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

Ich habe mir verschiedene Makros angesehen, aber diese sind nicht auf allen Plattformen verfügbar. gcc unterstützt sie, msvc nicht.

War es hilfreich?

Lösung

Ich mache es immer noch auf die alte Art und Weise, indem ich ein Makro (XTRACE, unten) definiere, das entweder einem No-Op- oder einem Funktionsaufruf mit einer Liste variabler Argumente entspricht.Rufen Sie intern vsnprintf auf, damit Sie die printf-Syntax beibehalten können:

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

Dann ein typischer #ifdef-Schalter:

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

Nun, das lässt sich noch ein wenig klären, aber es ist die Grundidee.

Andere Tipps

So debugge ich Ausdrucke in C++.Definieren Sie „dout“ (Debug-Out) wie folgt:

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

Im Code verwende ich „dout“ genau wie „cout“.

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

Wenn der Präprozessor „dout“ durch „0 && cout“ ersetzt, beachten Sie, dass << eine höhere Priorität als && hat und die Kurzschlussauswertung von && dazu führt, dass die gesamte Zeile als 0 ausgewertet wird.Da die 0 nicht verwendet wird, generiert der Compiler überhaupt keinen Code für diese Zeile.

Hier ist etwas, was ich in C/C++ mache.Zunächst schreiben Sie eine Funktion, die das Varargs-Zeug verwendet (siehe den Link in Stus Beitrag).Dann machen Sie so etwas:


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

Sie müssen lediglich daran denken, beim Aufruf der Debug-Funktion doppelte Klammern zu verwenden, und die gesamte Zeile wird im Nicht-DEBUG-Code entfernt.

Eine weitere unterhaltsame Möglichkeit, variadische Funktionen auszublenden, ist:

#define function sizeof

@CodingTheWheel:

Es gibt ein kleines Problem mit Ihrem Ansatz.Erwägen Sie einen Anruf wie z

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

Dies funktioniert im Debug-Build gut, aber im Release-Build wird es erweitert auf:

("x=%d", x);

Das ist vollkommen legitimes C und lässt sich kompilieren und normalerweise ohne Nebenwirkungen ausführen, generiert aber unnötigen Code.Der Ansatz, den ich normalerweise verwende, um dieses Problem zu beseitigen, ist:

  1. Lassen Sie die XTrace-Funktion einen int zurückgeben (geben Sie einfach 0 zurück, der Rückgabewert spielt keine Rolle)

  2. Ändern Sie #define in der #else-Klausel in:

    0 && XTrace
    

Jetzt wird die Release-Version erweitert auf:

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

und jeder anständige Optimierer wird das Ganze wegwerfen, da eine Kurzschlussauswertung verhindert hätte, dass irgendetwas nach dem && jemals ausgeführt würde.

Als ich den letzten Satz schrieb, wurde mir natürlich klar, dass vielleicht auch die ursprüngliche Form wegoptimiert werden könnte und dass es im Falle von Nebeneffekten wie Funktionsaufrufen, die als Parameter an XTrace übergeben werden, möglicherweise eine bessere Lösung ist, da dies der Fall ist Stellen Sie sicher, dass sich die Debug- und Release-Versionen gleich verhalten.

In C++ können Sie den Streaming-Operator verwenden, um die Dinge zu vereinfachen:

#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

und verwenden Sie es wie folgt:

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

Anschließend können Sie eine angepasste Trace-Ausgabe für Klassen auf die gleiche Weise implementieren, wie Sie es für die Ausgabe an tun würden std::cout.

Ah, vsprintf() war das, was mir gefehlt hat.Ich kann dies verwenden, um die Liste der Variablenargumente direkt an printf() zu übergeben:

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

Dann packen Sie das Ganze in ein Makro.

Auf welchen Plattformen sind sie nicht verfügbar?stdarg ist Teil der Standardbibliothek:

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

Jede Plattform, die dies nicht bereitstellt, ist keine Standard-C-Implementierung (oder sehr, sehr alt).Für diese müssen Sie Varargs verwenden:

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

Ein Teil des Problems mit dieser Art von Funktionalität ist, dass es häufig variadische Makros erfordert.Diese wurden kürzlich ziemlich standardisiert (C99), und viele alte C -Compiler unterstützen den Standard nicht oder haben ihre eigenen besonderen Arbeiten nicht.

Unten ist ein Debug-Header, den ich geschrieben habe und der mehrere coole Funktionen enthält:

  • Unterstützt C99- und C89-Syntax für Debug-Makros
  • Ausgabe basierend auf dem Funktionsargument aktivieren/deaktivieren
  • Ausgabe an den Dateideskriptor (Datei io)

Notiz:Aus irgendeinem Grund hatte ich leichte Probleme mit der Codeformatierung.

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

Schauen Sie sich diesen Thread an:

Es sollte Ihre Frage beantworten.

Da ich heute auf das Problem gestoßen bin, ist meine Lösung das folgende Makro:

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

Anschließend können Sie die Funktion wie folgt aufrufen:

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

Das verwende ich:

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

was zur Laufzeit absolut nichts kostet, wenn das Flag _DEBUG_LOG deaktiviert ist.

Dies ist eine TCHAR-Version der Benutzerantwort, daher funktioniert sie als ASCII (normal) oder Unicode-Modus (mehr oder weniger).

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

Ich sage „mehr oder weniger“, weil es ASCII-String-Argumente nicht automatisch in WCHAR konvertiert, aber es sollte Sie aus den meisten Unicode-Problemen herausholen, ohne sich darum kümmern zu müssen, den Format-String in TEXT() einzuschließen oder ihm ein L voranzustellen .

Weitgehend abgeleitet von MSDN:Abrufen des letzten Fehlercodes

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top