kostenloser Webspace werbefrei: lima-city


Präprozessor-Anweisungen innerhalb eines #defines-Makros

lima-cityForumProgrammiersprachenC/C++ und D

  1. Autor dieses Themas

    tangoal

    Kostenloser Webspace von tangoal

    tangoal hat kostenlosen Webspace.

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

    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
  2. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

  3. Hallo tangoal,

    Du musst den Spieß einfach umdrehen:
    #ifdef DEBUG
    #    define MYCOUT(text) cout << (text)
    #else
    #    define MYCOUT(text)
    #endif
  4. Autor dieses Themas

    tangoal

    Kostenloser Webspace von tangoal

    tangoal hat kostenlosen Webspace.

    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!
  5. s********4

    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.
  6. Autor dieses Themas

    tangoal

    Kostenloser Webspace von tangoal

    tangoal hat kostenlosen Webspace.

    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?
  7. s********4

    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
  8. Autor dieses Themas

    tangoal

    Kostenloser Webspace von tangoal

    tangoal hat kostenlosen Webspace.

    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?
  9. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

Dir gefällt dieses Thema?

Über lima-city

Login zum Webhosting ohne Werbung!