Wie erstellen Sie eine Nur-Debug-Funktion, die eine variable Argumentliste verwendet? Wie printf()

Lesezeit: 2 Minuten

Benutzeravatar von hyperlogic
hyperlogisch

Ich möchte eine Debug-Protokollierungsfunktion mit denselben Parametern wie erstellen printf. Aber eines, das vom Präprozessor während optimierter Builds 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.

  • Stu, MSVC unterstützt variadische Funktionen, es unterstützt keine variadischen Makros. Edit: My bad: Unterstützung für variadische Makros wurde in Visual C++ 2005 eingeführt.

    – hyperlogisch

    18. August 2008 um 22:15 Uhr

  • Siehe auchC #define Makro für Debug-Druck. Beachten Sie insbesondere, dass es im Allgemeinen am besten ist sicherzustellen, dass der Compiler den Code aus einem Debug-Makro kompiliert (aber optimiert), sodass der Code immer überprüft wird und daher immer korrekt ist. Andernfalls kann Bit-Rot einsetzen und wenn Sie das Debug-Makro ein Jahrzehnt später erneut aktivieren, stellen Sie fest, dass es nicht mehr kompiliert.

    – Jonathan Leffler

    9. April 2016 um 23:18 Uhr

Ich mache es immer noch auf die alte Art, indem ich ein Makro (XTRACE, unten) definiere, das entweder mit einer No-Op oder einem Funktionsaufruf mit einer variablen Argumentliste korreliert. 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 kann ziemlich aufgeräumt werden, aber es ist die Grundidee.

  • Ausgezeichnete Antwort, aber Sie sollten verwendet haben _vsntprintf um es tatsächlich Unicode-kompatibel zu machen. Ich habe meine eigene Version hinzugefügt, weil ich eine Zeichenfolge voranstellen musste (wie z [DEBUG]). stackoverflow.com/a/39186784/912236

    – Orwellophil

    27. August 2016 um 23:50 Uhr

  • _vsnprintf() ist spezifisch für bestimmte (Microsoft-kompatible) Implementierungen – und veraltet. vsnprintf() ist Standard.

    – Petrus

    28. August 2016 um 0:11 Uhr

  • Warum verwenden :: ?

    – Konrad

    8. Juli 2018 um 11:10 Uhr

  • @Konrad Hinzufügen von :: qualifiziert die Funktion so, dass sie aus dem globalen Bereich stammt.

    – Programmierer

    4. Juli 2020 um 0:16 Uhr

  • @pro-gramer ja, aber nur, wenn Sie eine Funktion mit demselben Namen im lokalen Bereich haben, daher sehe ich keinen Grund, dies hier zu tun.

    – Konrad

    5. Juli 2020 um 13:38 Uhr


Benutzeravatar von snstrand
snstrand

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 && die gesamte Zeile zu 0 auswertet. Da die 0 nicht verwendet wird, generiert der Compiler überhaupt keinen Code für diese Zeile.

  • Ich finde folgende Modifikation sinnvoll: #define dout cout << __FILE__ << "(" << __LINE__ << ") DEBUG: "

    – Kledoux

    18. April 2013 um 19:33 Uhr

  • In diesem Fall ist es beispielsweise nicht möglich, Folgendes zu verwenden: dout << setw(10) << "test";

    – sohaibafifi

    11. August 2013 um 12:11 Uhr


  • Dies erzeugt Warnungen (gcc -Wall), wenn Debug nicht aktiviert ist: “warning: statement has no effect [-Wunused-value] 0 && std::cout << "testen";

    – Etienne

    27. November 2014 um 13:24 Uhr

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


 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 sich nur daran erinnern, beim Aufrufen der Debug-Funktion doppelte Klammern zu verwenden, und die gesamte Zeile wird in Nicht-DEBUG-Code entfernt.

Benutzeravatar von hyperlogic
hyperlogisch

Ah, vsprintf() war das, was ich vermisst habe. Ich kann dies verwenden, um die Variablenargumentliste 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.

Benutzeravatar von Mat Noguchi
Mat Noguchi

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

#define function sizeof

Benutzeravatar von Ferruccio
Ferruccio

@CodingTheWheel:

Es gibt ein kleines Problem mit Ihrem Ansatz. Betrachten Sie einen Anruf wie z

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

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

("x=%d", x);

Das ist vollkommen legitimes C und wird normalerweise ohne Nebenwirkungen kompiliert und ausgeführt, generiert aber unnötigen Code. Der Ansatz, den ich normalerweise verwende, um dieses Problem zu beseitigen, ist:

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

  2. Ändern Sie das #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 die Kurzschlussauswertung alles nach dem && daran gehindert hätte, jemals ausgeführt zu werden.

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

Benutzeravatar von Tim Cooper
Tim Cooper

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 verwende es wie folgt:

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

Sie können dann angepasste Ablaufverfolgungsausgaben für Klassen auf die gleiche Weise implementieren, wie Sie es für die Ausgabe an tun würden std::cout.

1402120cookie-checkWie erstellen Sie eine Nur-Debug-Funktion, die eine variable Argumentliste verwendet? Wie printf()

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy