Das mag eine Frage des Stils sein, aber es gibt eine gewisse Kluft in unserem Entwicklerteam und ich habe mich gefragt, ob jemand andere Ideen zu diesem Thema hat ...
Grundsätzlich haben wir einige Debug-Print-Anweisungen, die wir während der normalen Entwicklung deaktivieren. Persönlich mache ich lieber Folgendes:
//---- SomeSourceFile.cpp ----
#define DEBUG_ENABLED (0)
...
SomeFunction()
{
int someVariable = 5;
#if(DEBUG_ENABLED)
printf("Debugging: someVariable == %d", someVariable);
#endif
}
Einige Mitglieder des Teams bevorzugen jedoch Folgendes:
// #define DEBUG_ENABLED
...
SomeFunction()
{
int someVariable = 5;
#ifdef DEBUG_ENABLED
printf("Debugging: someVariable == %d", someVariable);
#endif
}
... welche dieser Methoden klingt für Sie besser und warum? Mein Gefühl ist, dass das erste sicherer ist, weil immer etwas definiert ist und keine Gefahr besteht, dass es andere Definitionen an anderer Stelle zerstören könnte.
c++
c
if-statement
coding-style
c-preprocessor
Jon Cage
quelle
quelle
#if
können Sie#elif
im Gegensatz zu auch auch konsistent verwenden#ifdef
. Also, anstatt nur zu verwenden#define BLAH
, verwenden Sie#define BLAH 1
mit#if BLAH
, etc ...Antworten:
Meine anfängliche Reaktion war
#ifdef
natürlich , aber ich denke, dass dies#if
tatsächlich einige bedeutende Vorteile hat - hier ist der Grund:Erstens können Sie
DEBUG_ENABLED
in Präprozessor- und kompilierten Tests verwenden. Beispiel - Oft möchte ich längere Zeitüberschreitungen, wenn das Debuggen aktiviert ist. Daher#if
kann ich dies mit schreiben... anstatt ...
Zweitens sind Sie in einer besseren Position, wenn Sie von einer
#define
zu einer globalen Konstante migrieren möchten .#define
s werden normalerweise von den meisten C ++ - Programmierern missbilligt.Und drittens sagen Sie, Sie haben eine Kluft in Ihrem Team. Ich vermute, dies bedeutet, dass verschiedene Mitglieder bereits unterschiedliche Ansätze gewählt haben und Sie standardisieren müssen. Eine
#if
Entscheidung, die die bevorzugte Wahl ist, bedeutet, dass der verwendete Code#ifdef
kompiliert und ausgeführt wird, selbst wenn erDEBUG_ENABLED
falsch ist. Und es ist viel einfacher, Debug-Ausgaben aufzuspüren und zu entfernen, die erzeugt werden, wenn dies nicht der Fall sein sollte, als umgekehrt.Oh, und ein kleiner Punkt zur Lesbarkeit. Sie sollten in der Lage sein, true / false anstelle von 0/1 in Ihrem zu verwenden
#define
. Da es sich bei dem Wert um ein einzelnes lexikalisches Token handelt, benötigen Sie keine Klammern.anstatt
quelle
#ifdef
ist, dass es mit Dingen funktioniert, die nicht definiert sind. ob sie nicht absichtlich oder wegen eines Tippfehlers definiert sind oder was Sie haben.#if DEBUG_ENBALED
ist kein vom Präprozessor erkannter Fehler. WennDEBUG_ENBALED
nicht definiert, wird es0
in#if
Direktiven auf das Token erweitert .Sie sind beide schrecklich. Tun Sie stattdessen Folgendes:
Wenn Sie dann Debug-Code benötigen, geben Sie ihn ein
D();
. Und Ihr Programm ist nicht mit abscheulichen Labyrinthen von verschmutzt#ifdef
.quelle
#ifdef
prüft nur, ob ein Token definiert istdann
quelle
Wir hatten das gleiche Problem bei mehreren Dateien und es gibt immer das Problem, dass Leute vergessen, eine "Features Flag" -Datei einzuschließen (mit einer Codebasis von> 41.000 Dateien ist dies einfach).
Wenn Sie feature.h hatten:
Aber dann haben Sie vergessen, die Header-Datei in file.cpp aufzunehmen:
Dann haben Sie ein Problem, der Compiler interpretiert COOL_FEATURE in diesem Fall als "false" und definiert den Code nicht. Ja, gcc unterstützt ein Flag, das einen Fehler für undefinierte Makros verursacht ... aber der meiste Code von Drittanbietern definiert oder definiert keine Funktionen, sodass dies nicht so portabel wäre.
Wir haben eine tragbare Methode zur Korrektur dieses Falls sowie zum Testen des Status eines Features eingeführt: Funktionsmakros.
Wenn Sie die obige Funktion geändert haben.h in:
Aber dann haben Sie wieder vergessen, die Header-Datei in file.cpp aufzunehmen:
Der Präprozessor wäre aufgrund der Verwendung eines undefinierten Funktionsmakros fehlerhaft gewesen.
quelle
Für die bedingte Kompilierung sind #if und #ifdef fast gleich, aber nicht ganz. Wenn Ihre bedingte Kompilierung von zwei Symbolen abhängt, funktioniert #ifdef nicht so gut. Angenommen, Sie haben zwei bedingte Kompilierungssymbole, PRO_VERSION und TRIAL_VERSION. Möglicherweise haben Sie Folgendes:
Mit #ifdef wird das oben Genannte viel komplizierter, insbesondere wenn der # else-Teil funktioniert.
Ich arbeite an Code, der die bedingte Kompilierung ausgiebig verwendet, und wir haben eine Mischung aus #if & #ifdef. Wir neigen dazu, # ifdef / # ifndef für den einfachen Fall und #if zu verwenden, wenn zwei oder mehr Symbole ausgewertet werden.
quelle
#if defined
was istdefined
es ein Schlüsselwort oder?Ich denke, es ist eine Frage des Stils. Keiner hat wirklich einen offensichtlichen Vorteil gegenüber dem anderen.
Konsistenz ist wichtiger als jede bestimmte Wahl, daher würde ich empfehlen, dass Sie sich mit Ihrem Team zusammensetzen, einen Stil auswählen und dabei bleiben.
quelle
Ich selbst bevorzuge:
Da es einfacher ist, Code zu erstellen, der nach der entgegengesetzten Bedingung sucht, ist dies viel einfacher zu erkennen:
vs.
quelle
#if ! defined
, dass!
er prominenter und schwer zu übersehen ist.Es ist eine Frage des Stils. Ich empfehle jedoch eine präzisere Vorgehensweise:
Sie tun dies einmal und verwenden dann immer debug_print (), um entweder zu drucken oder nichts zu tun. (Ja, dies wird in beiden Fällen kompiliert.) Auf diese Weise wird Ihr Code nicht mit Präprozessoranweisungen verstümmelt.
Wenn Sie die Warnung "Ausdruck hat keine Wirkung" erhalten und diese entfernen möchten, finden Sie hier eine Alternative:
quelle
#if
Sie haben die Möglichkeit, den Wert auf 0 zu setzen, um die Funktionalität auszuschalten, während Sie weiterhin feststellen, dass der Schalter vorhanden ist.Persönlich bin ich immer
#define DEBUG 1
so, dass ich es entweder mit einem #if oder #ifdef fangen kannquelle
#define DEBUG 1
. Nicht#define DEBUG=1
#if und #define MY_MACRO (0)
Die Verwendung von #if bedeutet, dass Sie ein "Define" -Makro erstellt haben, dh etwas, das im Code durchsucht wird, der durch "(0)" ersetzt werden soll. Dies ist die "Makro-Hölle", die ich in C ++ nicht gerne sehe, weil sie den Code mit möglichen Code-Änderungen verschmutzt.
Beispielsweise:
gibt den folgenden Fehler auf g ++:
Nur ein Fehler.
Dies bedeutet, dass Ihr Makro erfolgreich mit Ihrem C ++ - Code interagiert hat: Der Aufruf der Funktion war erfolgreich. In diesem einfachen Fall ist es amüsant. Aber meine eigene Erfahrung mit Makros, die lautlos mit meinem Code spielen, ist nicht voller Freude und Erfüllung, also ...
#ifdef und #define MY_MACRO
Die Verwendung von #ifdef bedeutet, dass Sie etwas "definieren". Nicht, dass du ihm einen Wert gibst. Es ist immer noch umweltschädlich, aber zumindest wird es "durch nichts ersetzt" und von C ++ - Code nicht als verzögerte Code-Anweisung angesehen. Der gleiche Code oben, mit einer einfachen Definition, es:
Gibt die folgenden Warnungen:
So...
Fazit
Ich würde lieber ohne Makros in meinem Code leben, aber aus mehreren Gründen (Definieren von Header Guards oder Debuggen von Makros) kann ich nicht.
Aber zumindest möchte ich sie mit meinem legitimen C ++ - Code so wenig interaktiv wie möglich gestalten. Was bedeutet, dass Sie #define ohne Wert verwenden, #ifdef und #ifndef verwenden (oder sogar #if, wie von Jim Buck vorgeschlagen), und vor allem, wenn Sie ihnen Namen geben, die so lang und so fremd sind, dass niemand, der bei klarem Verstand ist, sie verwenden wird es "durch Zufall", und das wird in keiner Weise legitimen C ++ - Code beeinflussen.
Post Scriptum
Jetzt, während ich meinen Beitrag erneut lese, frage ich mich, ob ich nicht versuchen sollte, einen Wert zu finden, der niemals richtig sein wird, um C ++ zu meiner Definition hinzuzufügen. Etwas wie
Das könnte mit #ifdef und #ifndef verwendet werden, aber Code darf nicht kompiliert werden, wenn er innerhalb einer Funktion verwendet wird ... Ich habe dies erfolgreich in g ++ versucht und es gab den Fehler:
Interessant. :-)
quelle
Das ist überhaupt keine Frage des Stils. Auch die Frage ist leider falsch. Sie können diese Präprozessor-Direktiven nicht im Sinne von besser oder sicherer vergleichen.
bedeutet "wenn Makro definiert ist" oder "wenn Makro existiert". Der Wert des Makros spielt hier keine Rolle. Es kann was auch immer sein.
wenn immer mit einem Wert vergleichen. Im obigen Beispiel ist dies der implizite Standardvergleich:
Beispiel für die Verwendung von #if
Sie können jetzt entweder die Definition von CFLAG_EDITION in Ihren Code einfügen
oder Sie können das Makro als Compiler-Flag setzen. Auch hier sehen .
quelle
Das erste scheint mir klarer zu sein. Es scheint natürlicher, es zu einer Flagge zu machen als definiert / nicht definiert.
quelle
Beide sind genau gleichwertig. Bei der idiomatischen Verwendung wird #ifdef nur verwendet, um die Definiertheit zu überprüfen (und was ich in Ihrem Beispiel verwenden würde), während #if in komplexeren Ausdrücken verwendet wird, wie z. B. #if defined (A) &&! Defined (B).
quelle
Ein wenig OT, aber das Ein- und Ausschalten der Protokollierung mit dem Präprozessor ist in C ++ definitiv nicht optimal. Es gibt nette Protokollierungstools wie log4cxx von Apache, die Open Source sind und die Verteilung Ihrer Anwendung nicht einschränken. Sie ermöglichen es Ihnen auch, die Protokollierungsstufen ohne Neukompilierung zu ändern, haben einen sehr geringen Overhead, wenn Sie die Protokollierung deaktivieren, und geben Ihnen die Möglichkeit, die Protokollierung in der Produktion vollständig zu deaktivieren.
quelle
Früher habe ich verwendet
#ifdef
, aber als ich zur Dokumentation zu Doxygen wechselte, stellte ich fest, dass auskommentierte Makros nicht dokumentiert werden können (oder zumindest Doxygen eine Warnung ausgibt). Dies bedeutet, dass ich die derzeit nicht aktivierten Feature-Switch-Makros nicht dokumentieren kann.Obwohl es möglich ist, die Makros nur für Doxygen zu definieren, bedeutet dies, dass auch die Makros in den nicht aktiven Teilen des Codes dokumentiert werden. Ich persönlich möchte die Funktionsschalter zeigen und ansonsten nur dokumentieren, was aktuell ausgewählt ist. Darüber hinaus macht es den Code ziemlich chaotisch, wenn es viele Makros gibt, die nur definiert werden müssen, wenn Doxygen die Datei verarbeitet.
Daher ist es in diesem Fall besser, immer die Makros zu definieren und zu verwenden
#if
.quelle
Ich habe immer #ifdef und Compiler-Flags verwendet, um es zu definieren ...
quelle
Alternativ können Sie eine globale Konstante deklarieren und anstelle des Präprozessors #if das C ++ if verwenden. Der Compiler sollte die nicht verwendeten Zweige für Sie optimieren, und Ihr Code wird sauberer.
Hier ist, was C ++ Gotchas von Stephen C. Dewhurst über die Verwendung von # if's sagt.
quelle
Es gibt einen Unterschied zwischen verschiedenen Möglichkeiten, eine bedingte Definition für den Treiber anzugeben:
Ausgabe:
Dies bedeutet, dass dies
-DA
ein Synonym für ist-DA=1
und wenn der Wert weggelassen wird, kann dies zu Problemen bei der#if A
Verwendung führen.quelle
Ich mag es,
#define DEBUG_ENABLED (0)
wenn Sie mehrere Debug-Ebenen möchten. Beispielsweise:Erleichtert das Debuggen von Speicherlecks, ohne dass all diese Protokollzeilen Sie beim Debuggen anderer Dinge behindern.
Das
#ifndef
Umrunden der Definition erleichtert auch die Auswahl einer bestimmten Debug-Ebene in der Befehlszeile:Wenn dies nicht
#ifdef
der Fall wäre, würde ich einen Vorteil daraus ziehen, da das Compiler / Make-Flag von dem in der Datei überschrieben würde. Sie müssen sich also nicht darum kümmern, den Header zurückzusetzen, bevor Sie das Commit ausführen.quelle