Präprozessor-Anweisungen innerhalb eines #defines-Makros
lima-city → Forum → Programmiersprachen → C/C++ und D
alte konvention
code
compiler
definition
endeffekt
folgendes tun
frage
jemand
konflikt
konvention
liebe spezialisten
nutzen
programm
sorge
tun
umweg
variation
wahre problem
wirklichen probleme
zweck
-
Hallo liebe Spezialisten,
ich habe heute eine echt präkere Frage, an der ich heute schon ein wenig geknabbert habe... (keine Sorge meine Zähne habe ich mir noch nicht daran ausgebissen).
Prinzipiell möchte ich Folgendes tun:
Ich möchte Ausgaben an die Standardkonsole über cout machen, jedoch soll mein Programm dies nur tun, wenn ein Makro DEBUG definiert wurde. Dies sieht ja dann ungefähr so aus:
#ifdef DEBUG cout << "X=" << x << endl; #endif
So weit so gut. Wird aber ziemlich aufwändig, wenn man jedesmal so gemacht werden muss (weil es viele Ausgaben gibt, die zwar zu Debug-Zwecken ok sind, aber später nicht mehr benötigt werden).
Ich habe mir gedacht, das könnte man evtl. komplett in eine #define-Anweisung packen... eine Variation kriege ich sogar kompiliert, sobald ich die aber auch irgendwo benutzen will, meckert der Kompiler. Mein Ansatz dazu sieht folgendermaßen aus:
#define MYCOUT (text) \ #ifdef DEBUG \ # cout << text; \ #endif
Wenn ich einen Aufruf starte, wie z.B.:
MYCOUT("Hallo Welt!");
Dann meckert der Kompiler (etwas in der Richtung wie: text ist nicht definiert bzw. hat keinen Datentyp).
Hat jemand eine Lösung dafür oder vielleicht noch eine Idee, die in eine andere Richtung geht, aber für mein Problem passend ist?
Ich danke euch im Voraus.
Viele Grüße
tangoal -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Hallo tangoal,
Du musst den Spieß einfach umdrehen:
#ifdef DEBUG # define MYCOUT(text) cout << (text) #else # define MYCOUT(text) #endif
-
Hm, merkwürdig. Deinen Ansatz darkpandemic habe ich vorher auch schon einmal probiert, hatte aber auch nicht geklappt. Erst dann bin ich auf die Idee das "andersherum" zu machen gekommen. Vielleicht hatte ich noch irgendeinen syntaktischen Fehler gemacht... Aber jetzt funktioniert deine Variante... wunderbar prächtig sogar, ich danke dir!
-
Man sollte defines so gut es geht immer vermeiden.
Warum?
Weil defines einfache Textersetzungen sind. Ausserdem sind defines immer global und können nicht im Namespace, den du hoffentlich verwendest, angelegt werden. Es spricht auch nichts gegen dieses Makro:
namespace my_debug { void debug_message(const std::string& msg) { #ifdef _DEBUG std::cout << msg << std::endl; #endif } } // Namespace
Vorteil: Die Funktion liegt komplett im Namespace und ist nicht global.
Wenn jetzt jemand anders als bsp MYCOUT als Makro hat und du deren Header einbindest, kann es zu heftigen Fehlern kommen.
Und wenn der Compiler hier keinen Syntaxfehler meldet, können solche Fehler schwer zu finden sein.
Deswegen: Verzichte auf Ersetzungs-Makros wo es nur geht. -
scorcher24 schrieb:
Vorteil: Die Funktion liegt komplett im Namespace und ist nicht global.
Wenn jetzt jemand anders als bsp MYCOUT als Makro hat und du deren Header einbindest, kann es zu heftigen Fehlern kommen.
Und wenn der Compiler hier keinen Syntaxfehler meldet, können solche Fehler schwer zu finden sein.
Deswegen: Verzichte auf Ersetzungs-Makros wo es nur geht.
Allzu häufig benutze ich Präprozessor-Anweisungen auch nicht. In diesem Fall finde ich diese einfache Textersetzung aber ziemlich günstig, mehr braucht man manchmal wirklich nicht. So bleiben zumindest keine Rückstände im Code übrig (bei deiner Variante bleibt immer noch ein Funktionsaufruf im Code übrig, auch wenn _DEBUG nicht definiert wurde). Auch wenn diese Funktion dann nix tut, gefällt mir diese Variante vom ästhetischen nicht so ganz, ist aber natürlich auch praktikabel.
Auch dass das Makro dann global verfügbar ist, ist in dem Fall auch ok und sogar günstig. Wie gesagt es sind "nur" Testausgaben, mit denen der Kontrollfluss und die Daten beim Debuggen kontrolliert werden können. Das mit dem Namespace ist ein guter Hinweis, danke dafür. Habe ich bisher noch nicht so auf dem Schirm gehabt, weil ich bisher eher "alleine" entwickle. In einem Team ist das natürlich unerlässlich...
Aber warum sollte es "zu heftigen Fehlern" kommen? Der Präprozessor merkt doch, wenn ein Makro schon definiert wurde, oder habe ich was verpasst? Und warum sollte der Compiler keine Syntaxfehler bemerken? Das Makro ist doch eine Textersetzung, die durchgeführt wird, bevor der Compiler zum Einsatz kommt... oder meinst du noch etwas anderes? -
Ein Beispiel.
Du schreibst eine Library. Du machst in einem Header ein ungünstiges Makro.
Schon hat der User im schlimmsten Fall dutzende Compilerfehler am Hals.
Beispiel aus der Praxis:
class Timer { void start(), void stop(); void sleep(); };
Bei diesem Code hatte ich dutzende Compilerfehler. Ich wusste nicht warum.
Des Rätsels Lösung:
Im Header "mysql.h" war ein Makro:
#define sleep // ....
Die Fehlermeldung war kryptisch und lies den Fehler nicht auf Anhieb vermuten. Sowas wie "Term does not evaluate to a function taking 0 parameter".
Verstehste was ich meine?^^
Nächster Fall:
#define my_cout(text) std:cout << text Klasse objekt; my_cout(objekt);
-> Crash, falls << überladen ist aber für einen anderen zweck.
Beitrag zuletzt geändert: 15.7.2011 20:54:26 von scorcher24 -
Aber der Compiler beschwert sich wenigstens... :-) Jah, gut... die Fehlermeldung ist schon irreführend, im Endeffekt hat sie dich aber auf irgendwelchen Umwegen irgandwann auf das wahre Problem gestoßen
Normalerweise schreibt man solche Definitionen auch komplett in Großbuchstaben, sodass sie nicht mit Variablennamen, Funktionsnamen, Klassennamen kollidieren. Ist eine ganz alte Konvention, und sollte man auch so nutzen, sonst passieren einem die gleichen Dinge wie dir. Der Ersteller der SQL-Klasse hat da wohl etwas gepennt...
Ok, es bleibt trotzdem noch der Konflikt, falls du für ein neues Makro einen vorhandenen Makronamen verwendest... Lässt sich eben nicht vermeiden. Bisher hatte ich noch keine wirklichen Probleme mit Makros... nutze aber auch nicht so häufig welche.
Zur weiteren Unterscheidung gibt es noch die Unterstriche vor und nach dem eigentlichen Makronamen, z.B. so:
_DEBUG_
oder
__DEBUG__
Dafür existiert auch Konventionen, ich weiß aber nicht genau wann welche Variation verwendet wird. Weißt das vielleicht jemand anders? -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage