Printf als Makro

Gast #3999346
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.
#3999442
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.
#4000525
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.
(Firma: RUB) #4000712
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.
Moderator #4006472
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.
Gast #4007398
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.
Gast #4007617
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.
Gast #4007930
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.
Gast #4008018
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
#4008043
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?
Gast #4008131
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.
#4009406
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren