Forum: Mikrocontroller und Digitale Elektronik dumme Frage zu Makro


von HL.Klaus (Gast)


Lesenswert?

Guten Abend,
was macht dieses Makro
1
#define NO_PRINTF
2
#ifdef NO_PRINTF
3
#define printf(...)
4
#endif /* NO_PRINTF */

Verstehe es nicht:(
Bitte bringt licht ins Dunkel.

von Michael R. (mr-action)


Lesenswert?

Alle deine printf Aufrufe werden durch nichts ersetzt also quasi 
gelöscht.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

dafür müssen wir von hinten anfangen.

Es soll ein Makro namens
printf(...)
unter einer bestimmten Bedingung definiert/erstellt werden.

Dieses obige Makro wird nur definiert/erstellt wenn ein define namens
NO_PRINTF
existiert. Die Existensabfrage #ifdef sorgt dafür.

Also. Wenn die Zeile
#define NO_PRINTF
vorhanden ist wird obiges Makro erstellt.
Fehlt die Zeile oder wird auskommentiert wird obiges Makro nicht 
erstellt.

NO_PRINTF ist von der Benennung negierte Logik. Warum auch immer.

Solche Konstrukte werden gern als Schalter vewendet um im Code 
eingebaute Debugausgaben abhängig kompiliert zubekommen. Oder eben auch 
nicht. Mann muss nicht jede print Zeile im Code auskommentieren und 
wieder kommentieren. Oder andere Zwecke.

: Bearbeitet durch User
von HL.Klaus (Gast)


Lesenswert?

Vielen Dank für die Guten Erklärungen!

von Dirk B. (dirkb2)


Lesenswert?

Veit D. schrieb:
> NO_PRINTF ist von der Benennung negierte Logik. Warum auch immer.

Es schaltet printf ab, wenn es aktiviert ist.
Wie sollte es sonst heißen?

von Sebastian S. (amateur)


Lesenswert?

Mir scheint das Makro unsinnig zu sein.
In der ersten Zeile wird NO_PRINTF definiert.
Also ist es in der zweiten Zeile, auf jeden Fall, definiert.
Die dritte Zeile wird dann folgerichtig auch immer ausgeführt 
(definiert).
...und alles bis zum endif.

Das Argument: "Ausklammern" der ersten Definition erscheint mir etwas an 
den Haaren herbei gezogen. Da kann man die Definition selber genauso gut 
aussperren.
Anders sieht es natürlich aus, wenn man ein Dutzend verschiedene 
Dingsbumse in der if-Klammer stehen hat.

von Teo D. (teoderix)


Lesenswert?

Sebastian S. schrieb:
> In der ersten Zeile wird NO_PRINTF definiert.
> Also ist es in der zweiten Zeile, auf jeden Fall, definiert.

Das ist denke ich Jedem klar, daher ist's auch unnötig darauf 
hinzuweisen, dass das so nicht im Code steht...


> #define NO_PRINTF // Steht im betreffenden .h Fill

> #ifdef NO_PRINTF  // Der ganze Mist im Code wo immer er gebraucht wird
> #define printf(...)
> #endif /* NO_PRINTF */

von Sebastian S. (amateur)


Lesenswert?

@ Teo D.
Bestimmt schon mehr als 100 Mal wurde hier im Forum darauf hingewiesen, 
dass der Zusammenhang, einer Codesequenz, immens wichtig ist.
Wer hat sich nicht schon öfters einen Wolf gesucht, um festzustellen, wo 
in den vielen defs und undefs, was gesagt wurde.

von Dirk B. (dirkb2)


Lesenswert?

Man kann eine Makro auch als Compileroption mit angeben.

Manche finden es einfacher dies in einem Header zu tun.
Dann entsteht so etwas wie oben.

von A. S. (Gast)


Lesenswert?

Sebastian S. schrieb:
> Da kann man die Definition selber genauso gut aussperren.

Nicht wirklich:
Wenn das definierte mehrere Zeilen umspannt
Wenn der Schalter mehrfach gebraucht wird (z.b.um stattdessen eine 
alternative LED blinken zu lassen)
Wenn der Schalter zentral mit anderen gesetzt wird
Wenn parallele Schalter existieren (= oder-verknüpft)
Zudem warnt der Compiler, wenn der Schalter verschieden definiert ist, 
z.b. mit 0 und ohne.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn es um einfache Debugschalter o.ä. geht mache ich das unter C++ so 
wie aus dem Lehrbuch.
1
enum class Debug : uint8_t {RELEASE, DEBUG};
2
Debug debugMode = Debug::RELEASE;        // mit serieller Ausgabe >> debug::DEBUG
3
4
...
5
...
6
7
if (debugMode == Debug::DEBUG) {
8
   "Debugausgabe"
9
   ...
10
   ...
11
}

von Dirk B. (dirkb2)


Lesenswert?

Mit der Methode bleibt der Code aber auch im Object/Exe.
Du musst immer das if (debug ....) darum basteln.

Und du kannst es nicht über das Makefile schalten.

Der Vorteil ist, du kannst es während der Laufzeit umschalten.
Aber da wäre eine spezielles printf für Log-Ausgaben besser.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

irgendeine if Abfrage muss man schreiben.  :-)
Der Rest ist Geschmackssache bzw. kommt auf den Einzelfall an wofür man 
es einsetzen möchte.

von Dirk B. (dirkb2)


Lesenswert?

Veit D. schrieb:
> irgendeine if Abfrage muss man schreiben.  :-)

Bei deiner Methode, musst du das bei jeder Ausgabe machen.
Beim Makro, nur bei der Makrodefinition.

von MaWin (Gast)


Lesenswert?

Dirk B. schrieb:
> Bei deiner Methode, musst du das bei jeder Ausgabe machen.
> Beim Makro, nur bei der Makrodefinition.

Na, der Unterschied bei deinem Beispiel liegt doch beim Zeitpunkt der 
Auswertung: Makros werden vor dem Compilerlauf aufgelöst und beinflussen 
den Quellcode (und damit das Programm an sich). Eine if Verzweigung wird 
zur Laufzeit ausgewertet und ist bereits Bestandteil des Programms.

Nur mal so am Rande ...

von g457 (Gast)


Lesenswert?

> Nur mal so am Rande ...

Kann aber beides ("gleichzeitig") gewünscht sein. Was dagegen immer 
unerwünscht ist, das ist die Variante wie ganz oben das printf 
"wegdefiniert" wird, denn das kann semantikverändernd sein. Und das 
wiederum gibt gaaanz pöse Pugs.

HTH

von Wilhelm M. (wimalopaan)


Lesenswert?

MaWin schrieb:
> Dirk B. schrieb:
>> Bei deiner Methode, musst du das bei jeder Ausgabe machen.
>> Beim Makro, nur bei der Makrodefinition.
>
> Na, der Unterschied bei deinem Beispiel liegt doch beim Zeitpunkt der
> Auswertung: Makros werden vor dem Compilerlauf aufgelöst und beinflussen
> den Quellcode (und damit das Programm an sich). Eine if Verzweigung wird
> zur Laufzeit ausgewertet und ist bereits Bestandteil des Programms.
>
> Nur mal so am Rande ...

1. Ein Laufzeit-Vergleich mit einer Compilezeit-Konstanten wird vom 
Compiler (üblicherweise) wegoptimiert.

2. Um die Compilezeit-Auswertung zu erzwingen, kann man das Ganze auch 
constexpr machen. Damit ist es zur Laufzeit definitiv weg.

3. Denselben Effekt wie mit dem Makro kann man mit einer 
Template-Spezialisierung erreichen.

von MaWin (Gast)


Lesenswert?

g457 schrieb:
> Was dagegen immer
> unerwünscht ist, das ist die Variante wie ganz oben das printf
> "wegdefiniert" wird

Woher weisst du das? Ohne den Kontext zu kennen, ist deine Behauptung 
einfach nur frech, dreist, m. E. sogar dumm!

von MaWin (Gast)


Lesenswert?

Wilhelm M. schrieb:
> constexpr

in C99 ?

Wilhelm M. schrieb:
> Template-Spezialisierung

in C99 ?

von Wilhelm M. (wimalopaan)


Lesenswert?

MaWin schrieb:
> Wilhelm M. schrieb:
>> constexpr
>
> in C99 ?

Wo steht im Eingangspost C99?

von MaWin (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Wo steht im Eingangspost C99?

Wo steht C++?

Dann wird es wohl fortran sein und ein Syntax Fehler. ;->>>

von Wilhelm M. (wimalopaan)


Lesenswert?

MaWin schrieb:
> Wilhelm M. schrieb:
>> Wo steht im Eingangspost C99?
>
> Wo steht C++?
>
> Dann wird es wohl fortran sein und ein Syntax Fehler. ;->>>

Ja, ich finde es ja auch schade, dass C seit C99 (20 Jahre!) oder auch 
C11 nicht wirkliche Fortschritte gemacht hat.

von MaWin (Gast)


Lesenswert?

Wilhelm M. schrieb:
> ich finde es ja auch schade

Ich auch. Jetzt sind wir zu Zweit, und? Wie geht es weiter?

von Wilhelm M. (wimalopaan)


Lesenswert?

MaWin schrieb:
> Wilhelm M. schrieb:
>> ich finde es ja auch schade
>
> Ich auch. Jetzt sind wir zu Zweit, und? Wie geht es weiter?

Nimmste halt auch C++ ;-)

von MaWin (Gast)


Lesenswert?

Wilhelm M. schrieb:
> MaWin schrieb:
>> Wilhelm M. schrieb:
>>> ich finde es ja auch schade
>>
>> Ich auch. Jetzt sind wir zu Zweit, und? Wie geht es weiter?
>
> Nimmste halt auch C++ ;-)

Ich nehme was richtiges. C und seine Ableger - wie C++ - kommen nur auf 
ausdrücklichen Wunsch zum Einsatz.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habe das gleich einmal in meiner Arduino IDE probiert mit einem 
spezialisierten template. Allerdings kompiliert das nicht und weiß nicht 
warum.
1
no matching function for call to 'debugging(Debug&)'
2
     |   debugging (debugMode);
1
enum class Debug : uint8_t {RELEASE, DEBUG};
2
Debug debugMode = Debug::DEBUG;
3
4
void setup(void) {
5
  Serial.begin(9600);
6
}
7
8
void loop(void) {
9
  debugging(debugMode);
10
}
11
12
template<Debug T>
13
void debugging()
14
{
15
  if (T == Debug::DEBUG) {
16
    Serial.println("Debugausgabe");
17
  }
18
}


Meine Denkweise.

Der Datentyp heißt Debug.
Die Variable debugMode mit dem Datentyp Debug hat den Wert Debug::DEBUG.
Ich übergebe die Variable debugMode der Funktion debugging als 
Parameter.

Das template ist mittels Datentyp <Debug T> spezialisiert.
Danach erfolgt nur noch der Vergleich.

Wo ist mein Denkfehler?

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> ich habe das gleich einmal in meiner Arduino IDE probiert mit einem
> spezialisierten template. Allerdings kompiliert das nicht und weiß nicht
> warum.

> Die Variable debugMode mit dem Datentyp Debug hat den Wert Debug::DEBUG.
> Ich übergebe die Variable debugMode der Funktion debugging als
> Parameter.

Du hast aber keine Funktion mit der entsprechenden Signatur.

> Das template ist mittels Datentyp <Debug T> spezialisiert.

Du hast nur ein allg. template und keine Spezialisierung.

> Wo ist mein Denkfehler?

Ich denke, es fehlen die Grundlagen zu templates. 
non-type-template-parameter müssen Compilezeit-Konstanten sein, denn sie 
parametrieren ja einen Datentyp.
1
const Debug debugMode = Debug::DEBUG;
2
3
template<Debug T>
4
void debugging() {
5
  if (T == Debug::DEBUG) {
6
  }
7
}
8
9
int main() {
10
    debugging<debugMode>();
11
}

So compiliert der Code, aber was willst Du damit erreichen? Den oben 
beschriebenen gewünschten Effekt doch nicht. Wie soll Deine Funktion 
variable Texte ausgeben? Oder wie möchtest Du das gestalten?

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> ich habe das gleich einmal in meiner Arduino IDE probiert mit einem
> spezialisierten template.

Wenn es nur um das Abschalten der Ausgabe geht, kann man es natürlich 
auch ganz ohne templates lösen.
1
namespace {
2
    void no_printf(...) {}
3
}
4
5
constexpr auto debug_printf = printf;
6
//constexpr auto debug_printf = no_printf;
7
8
int main() {
9
    debug_printf("bla %d \n", 42);
10
}

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich wollte das machen und auf die Magie von template hoffen.
1
// 1746/194 Bytes
2
3
enum class Debug : uint8_t {RELEASE, DEBUG};
4
const Debug debugMode = Debug::DEBUG;
5
6
template<class T>
7
void debugging(T mode)
8
{
9
  if (mode == Debug::DEBUG) {
10
    Serial.println("Debug");
11
  }
12
}
13
14
void setup(void) {
15
  Serial.begin(9600);
16
  debugging(debugMode);
17
}
18
19
void loop(void) {
20
  
21
}

oder mit Spezialisierung
1
// 1746/194 Bytes
2
3
enum class Debug : uint8_t {RELEASE, DEBUG};
4
const Debug debugMode = Debug::DEBUG;
5
6
template<Debug mode>
7
void debugging()
8
{
9
  if (mode == Debug::DEBUG) {
10
    Serial.println("Debug");
11
  }
12
}
13
14
void setup(void) {
15
  Serial.begin(9600);
16
  debugging<debugMode>();
17
}
18
19
void loop(void) {
20
  
21
}

Beide Varianten verbrauchen jedoch gleich viel Flash/Ram. Das alles ohne 
template verbraucht genau den gleichen Flash/Ram. Was mich jetzt 
wundert. Entweder kann das der Compiler auch so schon wegoptimieren und 
das ist alles schon optimal. Oder die Magie von template greift noch 
nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Ich sehe immer noch keine template-spezialisierung.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Beide Varianten verbrauchen jedoch gleich viel Flash/Ram. Das alles ohne
> template verbraucht genau den gleichen Flash/Ram. Was mich jetzt
> wundert. Entweder kann das der Compiler auch so schon wegoptimieren und
> das ist alles schon optimal. Oder die Magie von template greift noch
> nicht.

Wie ich oben schon schrieb: jeder Compiler wird ein Laufzeit-if mit 
einer Konstanten wegoptimieren.

Und auf welche zusätzliche Magie hast Du gehofft?

Es ging ja auch darum, ohne viel Schreibarbeit beliebigen Text 
auszugeben. Das ist mit Deinem Ansatz so noch nicht möglich.

von DPA (Gast)


Lesenswert?

Ist zwar nicht ideal für uc, aber ich mache in C manchmal sowas:

debug_print.h:
1
extern int debug_print(const char*);

debug_print_default.c
1
#include <debug_print.h>
2
3
__attribute__((weak)) int debug_print(const char* msg){
4
  return 0;
5
}

debug_print_stdout.c
1
#include <debug_print.h>
2
3
int debug_print(const char* msg){
4
  return puts(msg);
5
}

Jenachdem, ob ich debug_print_stdout.c dazulinke oder nicht, hab ich 
dann debug ausgaben, oder auch nicht. Auf nem richtigen PC kann man die 
debug_print_stdout.c aber auch als shared library preloaden, wenn man 
die mit -fPIC kompiliert hatte. Gibt aber diverse variationen davon.

von Veit D. (devil-elec)


Lesenswert?

Wilhelm M. schrieb:
> Ich sehe immer noch keine template-spezialisierung.

Ich meine das ist doch die template Spezialisierung. Exakter Datentyp 
wird vorgegeben statt allgemeiner typename/class. Das template kann nur 
mit diesen Datentyp gebaut werden. Wie sieht bei dir ein spezialisiertes 
Funktions-Template aus?
1
template<Debug mode>
2
void debugging()
3
{
4
  ...
5
}

Wegen der erhofften Magie. Ja stimmt, wenn ich alles außer der 
Serial.print Zeile auskommentiere bleiben auch 1746/194 Bytes stehen. 
Weniger geht also nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Wilhelm M. schrieb:
>> Ich sehe immer noch keine template-spezialisierung.
>
> Ich meine das ist doch die template Spezialisierung.

Nein.

> Exakter Datentyp
> wird vorgegeben statt allgemeiner typename/class. Das template kann nur
> mit diesen Datentyp gebaut werden.

Du hast einfach ein allg. template mit einem 
non-type-template-parameter.

> Wie sieht bei dir ein spezialisiertes
> Funktions-Template aus?

Nicht nur bei mir ;-)
Schnapp Dir ein Buch und liest mal nach ...

z.B.: 
https://www.amazon.de/Templates-Complete-Guide-David-Vandevoorde/dp/0321714121

>
1
> template<Debug mode>
2
> void debugging()
3
> {
4
>   ...
5
> }
6
>

Ein Spezialisierung setzt ein allg. template voraus und definiert dann 
eine Ausnahme zum allg. Fall für spezielle DT oder Konstanten.

Etwa:
1
template<typename T>
2
T minimum(T a, T b) {
3
     return (a < b) ? a : b;
4
}
5
template<>
6
std::string minimum<std::string>(std::string a, std::string b) {
7
     return (a.size() < b. size()) ? a.size() : b.size();
8
}

Hier wird dann eben die Ausnahme abweichend für std::string formuliert: 
statt die lexikographische Ordnung wird die Stringlänge benutzt.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

Danke für das Bsp. und Erklärung. Ich stelle fest das ich den Begriff 
"Spezialisierung" anders aufgefasst und verstanden habe. Das template in 
deinem Bsp. bleibt unspezifiert - template eben. Nur der Aufruf wird 
spezialisiert.

Wenn ich das template betrachte kann ich keine Spezialisierung erkennen, 
eben weil es allgmein bleibt. Das ist mein Gedankenproblem. Denn ein 
allgemeines template soll ja für alle Datentypen verwendbar sein, 
deswegen kann ein allgemeines template in meinen Augen nicht 
spezialisiert sein. Kannst du mir gedanklich folgen?

Jetzt weiß ich das der Aufruf mit konkreten Datentypen die 
Spezialisierung ist. Halte ich zwar immer noch für etwas seltsam bzw. 
die Formulierung ist seltsam, weil das der Standardfall ist und deswegen 
in meinen Augen keine Spezialisierung sein kann. Aber nun gut, es ist 
wie es ist.

Wegen dem Buch. Gibt es ein gutes template Buch auch in deutsch? Hast du 
eine Empfehlung? Möchte nicht dumm sterben.  :-)   Habe bedenken das 
sich zusätzliche Probleme mit meiner Übersetzung einschleichen.

von Eric B. (beric)


Lesenswert?

Oder einfach in C99:
1
#ifdef DEBUG
2
#define debug(...) printf(__VA_ARGS__)
3
#else
4
#define debug(...)
5
#endif
und compilieren mit -DDEBUG oder halt ohne...

von leo (Gast)


Lesenswert?

Eric B. schrieb:
> Oder einfach in C99:
> #ifdef DEBUG
> #define debug(...) printf(_VA_ARGS_)

Warum einfach und universell, wo es denn doch auch in nur C++ und 
laenger geht, wobei man nicht genau weiss, bei welcher Optimierung 
welcher Compiler das ev. wegoptimieren darf und kann ;-)

leo

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das habe ich mich auch schon öfters gefragt.  :-)

Aber wenn man das in C++ nicht auf die Spitze treibt und einfach hält, 
hält man sich von den Fallstricken der #defines und sein Gevolke fern, 
womit andere Probleme lauern. Die Welt ist nicht perfekt.  :-)

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.