Forum: Compiler & IDEs Printf als Makro


von Debgger (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

Seit C99 gibt es Präprozessor-Makros mit variabler Parameteranzahl.

1
#define PRINT_BEBUG(...) printf(__VA_ARGS__)

von Markus F. (mfro)


Lesenswert?

Bei mir hat jedes Modul folgendes im Header:

1
//#define DBG_XXX
2
#ifdef DBG_XXX
3
#define dbg(format, arg...) do { printf("DEBUG (%s()): " format, __FUNCTION__, ##arg);} while(0)
4
#else
5
#define dbg(format, arg...) do {;} while (0)
6
#endif /* DBG_XXX */
7
#define err(format, arg...) do { printf("ERROR (%s()): " format, __FUNCTION__, ##arg); } while(0);

Damit lassen sich dann Debug-Ausgaben Modulweise ein- und ausschalten.

von Michael F. (startrekmichi)


Lesenswert?

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
extern int debug_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.

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Du koenntest das #ifdef zusammen mit dem printf in eine funktion packen.
1
void MyDebugPrint( 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.

von Christopher (Gast)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

Markus F. schrieb:
> Bei mir hat jedes Modul folgendes im Header:

Mal mit und mal ohne Semikolon am Ende?

von Markus F. (mfro)


Lesenswert?

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).

von Markus F. (mfro)


Lesenswert?

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...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Markus F. schrieb:
> mit gcc's arbeiten müssen, die kein C99 können...

Zeitmaschine ins Pleistozän? :-)

von tictactoe (Gast)


Lesenswert?

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
     return dbg("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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Das wichtigere Detail ist, dass
1
   if (condition) dbg("foo"); else return;

funktioniert, wie man es auf den ersten Blick auf diese Zeile
erwarten würde.

von tictactoe (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ja, für ein einfaches printf() genügt das.  Weiter oben war aber auch
noch eins gezeigt, das dann wieder von einem debug level abhängig
gearbeitet hat.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Klose (Gast)


Lesenswert?

Hi

ich würde folgenden Aufbau empfehlen:
1
#define DBG(mask,...) ((mask) ? printf(__VA_ARGS__) : 0 )

Damit hat man selektierbare Pegel und ein Aufruf ala
1
return DBG(DBG_ERROR, "blahblah")
ist möglich.

Der Parameter mask ist sehr flexibler, ich nutze ihn in mehreren 
Varianten.
Beispiele:
1
#define DBG(mask,...) ((mask) ? printf(__VA_ARGS__) : 0 )
2
3
// Konfiguration der Masken
4
#define DBG_ERROR   1   // Modulübergreifende Errormeldungen immer anzeigen
5
#define DBG_WARNING 0    // Modulübergreifende Warnings aktuell aus
6
int dbg_adc = 0;
7
#define DBG_ADC dbg_adc // Meldungen des ADC Moduls schalt ich erst beim Überschreiten der Pegel ein
8
#define DBG_INPUT (portA & 0x01) // Modul Input: Ausgabe nur Pin 1 high
9
//...
10
11
  DBG(DBG_ERROR, "Data invalid. data = %i\n", data);
12
  DBG(DBG_ADC,   "Hier noch keine Ausgabe, Data = %i\n", data);
13
  //...
14
  DBG_ADC = 1;
15
  DBG(DBG_ADC,   "Ab jetzt Ausgabe, Data = %i\n", data);
16
  DBG(DBG_INPUT, "Signalzustand, portA = %i\n", portA);

So kann man jede beliebige Debugausgabe ein/ausschalten und sich auf die 
wesentlichen Ausgaben konzentrieren.

von Klose (Gast)


Lesenswert?

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...

von Klaus W. (mfgkw)


Lesenswert?

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?

von Klose (Gast)


Lesenswert?

>> ... 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

von Markus F. (mfro)


Lesenswert?

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?

von Klose (Gast)


Lesenswert?

>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.

von Rolf M. (rmagnus)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.