Ich baue debugg-printf momentan so in meinen Code ein:
1
#ifdef DEBUG
2
printf("....",a,b,c,...);
3
#endif
Da es mich nervt jedes Mal #ifdef, #endif zu schreiben möchte ich fragen
ob es irgendwie möglich ist das Ganze in ein Makro zu stecken dass ich
dann ungefähr so aufrufe:
1
PRINT_BEBUG("....",a,b,c,..);
"Normale" Makros sind kein Problem, ich weiß aber nicht wie ich die
Parameter a, b, c,... an ein Makro übergebe.
Die primitiv-Methode, ohne dass du ein fancy/variadisches Makro
benötigst:
1
#ifdef DEBUG
2
#define PRINTF printf
3
#else
4
#define PRINTF if(0) printf
5
#endif
Oder mit zur Laufzeit selektierbarem Pegel:
1
externintdebug_level;
2
#ifdef DEBUG
3
#define PRINTF(a) if((a) < debug_level) printf
4
#else
5
#define PRINTF(a) if(0) printf
6
#endif
Das unschöne sind dann die zwei Klammerpaare beim Aufruf, da wäre ein
variadisches Makro doch von Vorteil. Kombinationen mit den bereits
genannten Varianten sind also natürlich auch möglich.
Du koenntest das #ifdef zusammen mit dem printf in eine funktion packen.
1
voidMyDebugPrint(hier_Parameter)
2
{
3
#ifdef DEBUG
4
printf("was auch immer...",hier_Parameter);
5
#endif
6
}
Dann brauchst du nur noch die Funktion hinschreiben (ob du nun die
Funktion oder Makro hinschreibst ist ja voellig wurst) und als
Compilerflag kannst du -DDEBUG uebergeben, zumindestens beim gcc und
clang. wie das unter Windows mit Visual Studio ist weiss ich nicht.
Markus F. schrieb:> #ifdef DBG_XXX> #define dbg(format, arg...) do { printf("DEBUG (%s()): " format,> _FUNCTION_, ##arg);} while(0)
Warum wir dort ein do ... while Konstrukt verwendet? Nur das printf
funktioniert nicht?
Christopher schrieb:> Warum wir dort ein do ... while Konstrukt verwendet?
Weil es auf diese Weise syntaktisch ein statement ist.
Aber für einen C99-fähigen Compiler (und wir sind ja hier im
GCC-Forum …) würde ich auf jeden Fall die gleich im zweiten Posting
genannte Form bevorzugen. Die ist nicht „fancy“, sondern einfach.
Jörg Wunsch schrieb:> Christopher schrieb:>> Warum wir dort ein do ... while Konstrukt verwendet?>> Weil es auf diese Weise syntaktisch ein statement ist.
In diesem Fall würde das Makro ohne die do-Schleife, die geschweiften
Klammern und das Semikolon genauso gut funktionieren. Die do-Schleife
braucht man nur bei aus mehreren Einzelanweisungen zusammengestezten
Anweisungen.
Klaus Wachtler schrieb:> Markus F. schrieb:>> Bei mir hat jedes Modul folgendes im Header:>> Mal mit und mal ohne Semikolon am Ende?
Das ist wohl beim Kopieren passiert, das Semikolon gehört da nicht hin
(schadet aber auch nix).
Jörg Wunsch schrieb:> Aber für einen C99-fähigen Compiler (und wir sind ja hier im> GCC-Forum …) würde ich auf jeden Fall die gleich im zweiten Posting> genannte Form bevorzugen. Die ist nicht „fancy“, sondern einfach.
Einverstanden. Allerdings gibt's auch Leute, die (leider) mit gcc's
arbeiten müssen, die kein C99 können...
Yalu X. schrieb:> Die do-Schleife> braucht man nur bei aus mehreren Einzelanweisungen zusammengestezten> Anweisungen.
Ein Detail ist, dass man mit do ... while(0) noch folgende Nutzung
ausschließt:
1
returndbg("returning now"),42;
Wenn man nur die Funktion lässt, handelt es sich "nur" um eine
Expression, und das oben wäre erlaubt.
Kann man wollen oder auch nicht.
Jörg Wunsch schrieb:> Das wichtigere Detail ist, dass> if (condition) dbg("foo"); else return;>> funktioniert, wie man es auf den ersten Blick auf diese Zeile> erwarten würde.
Dass man do...while für Multi-Statements braucht ist eh klar. Aber ich
glaube, die Frage war eher, warum man nicht die einfachere Variante ohne
do...while wählen sollte:
1
#define dbg(format, arg...) printf("DEBUG (%s()): " format, _FUNCTION_, ##arg)
Damit würde dein Beispiel natürlich auch funktionieren.
Markus F. schrieb:> Allerdings gibt's auch Leute, die (leider) mit gcc's arbeiten müssen, die> kein C99 können...
gcc hat variadische Makros schon lange vor C99 unterstützt, allerdings
mit einer etwas anderen Syntax.
Ergänzung:
Außerdem wirft der Compiler alle Ausgaben mit einer mask=0 zur
Compilezeit raus. Ein nicht unerheblicher Vorteil wenn es um Laufzeit
oder Speicherplatz geht...
Klose schrieb:> ... und ein Aufruf ala return DBG(DBG_ERROR, "blahblah")> ist möglich.
möglich ja, aber wozu sollte eine Funktion den Wert ihrer letzten
Debugausgabe zurückliefern? Was soll der Aufrufer damit anfangen?
>> ... und ein Aufruf ala return DBG(DBG_ERROR, "blahblah") ist möglich.>möglich ja, aber wozu sollte eine Funktion den Wert ihrer letzten>Debugausgabe zurückliefern? Was soll der Aufrufer damit anfangen?
(Dachte mir fast, dass jemand diese Frage stellt, ich hatte sie auch…
:-)
Ich hatte nur einmal den Fall, dass es was nutzte:
Die übergeordnete Funktion hat damit erkannt, dass Ausgaben am Display
angezeigt wurden und wartete, bis der User diese per Taste bestätigte.
Interessanter ist eher folgender Aspekt:
Durch diesen Aufbau ist eine bedingte Debug-Ausgabe möglich, das Makro
verhält sich aber weiterhin wie eine printf Funktion (die ja auch einen
Wert zurückliefert).
Auch so etwas ist möglich:
1
numOfChars=DBG(DBG_INFO,"Data1=%i",data1)+DBG(DBG_INFO,"Data2=%f",data2);// Zusammenzählen alle Chars
2
numOfChars=DBG(DBG_INFO,"Hello"),DBG(DBG_INFO,"World");// Beachte: hier Kommaoperator
Klose schrieb:> Auch so etwas ist möglich:
Hmmm.
Zunächst ist es natürlich mal kein Schaden, wenn auch die Debug-Ausgabe
der Semantik von printf() folgt. Dann macht man üblicherweise von
alleine "das Richtige" und hoffentlich nix falsch.
Bloß kann ich den Nutzen noch nicht wirklich erkennen. "numOfChars" wäre
zwangsweise auch existent, wenn Debugausgaben deaktiviert sind. Dann
habe ich plötzlich Warnungen über ungenutze Variablen, die bei
eingeschalteten Debug-Ausgaben nicht da sind und muß mich darum kümmern.
Dasselbe bei deinem "Warten auf Taste"-Beispiel von oben: da wird
plötzlich der Aufrufer mit der Frage konfrontiert, ob in der gerufenen
Funktion Debugausgaben aktiv sind oder nicht bzw. was der Rückgabewert
denn gerade zu bedeuten hat.
Ich versuche eigentlich normalerweise, bei Debug-Ausgaben so wenig wie
möglich Abhängigkeiten zum "echten Code" zu erzeugen. Wenn man auf eine
Taste warten muß, damit man die Ausgaben lesen kann, dann sollte das
m.E. im Debug-Macro drin sein und nicht irgendwo anders?
>Bloß kann ich den Nutzen noch nicht wirklich erkennen
Das Beispiel sollte lediglich zeigen, dass mit diesem Konstrukt auch
return oder anderweitige Konstrukte funktionieren.
>Dasselbe bei deinem "Warten auf Taste"-Beispiel von oben
Die damalige Debug Lösung sollte erst anhalten, wenn ein Codeteil
durchlaufen war.
Ich wollte hier beim Thema "printf als Makro" zeigen, dass es
transparentere Varianten gibt.
Du hast vollkommen Recht, dass es viel wichtiger ist, möglichst keine
Abhängigkeit zw. Code und Debugging zu haben.
Es gibt noch eine Stelle in meinem Codebeispiel, die dem widerspricht:
DBG_ADC = 1; Bei Umstellen der mask (z.B. #define DBG_ADC 0) würde der
Compiler einen Fehler melden. Aber so ein Konstrukt nutze ich meist nur
temporär und kann es ja noch mittels #if DEBUG … kapseln.
Markus F. schrieb:> Ich versuche eigentlich normalerweise, bei Debug-Ausgaben so wenig wie> möglich Abhängigkeiten zum "echten Code" zu erzeugen. Wenn man auf eine> Taste warten muß, damit man die Ausgaben lesen kann, dann sollte das> m.E. im Debug-Macro drin sein und nicht irgendwo anders?
Sofern "warten" einfach das Verbraten von Zeit in einer Schleife
bedeutet. Evtl. muss aber das Programm weiterlaufen, und man muss
warten, indem man Teile der Hauptschleife überspringt.