#ifdef vs # if -, die für die Aktivierung / Deaktivierung Zusammenstellung von bestimmten Codeabschnitten besser / sicherer als Methode ist?

StackOverflow https://stackoverflow.com/questions/135069

Frage

Dies kann eine Frage des Stils, aber es ist ein bisschen von einer Kluft in unserem Entwicklerteam und ich fragte mich, ob jemand anderes hat irgendwelche Ideen zu diesem Thema ...

Im Grunde genommen haben wir einige Debug-print-Anweisungen, die wir während der normalen Entwicklung auszuschalten. Persönlich ziehe ich folgendes zu tun:

//---- SomeSourceFile.cpp ----

#define DEBUG_ENABLED (0)

...

SomeFunction()
{
    int someVariable = 5;

#if(DEBUG_ENABLED)
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

Einige der Mannschaft am liebsten folgende obwohl:

// #define DEBUG_ENABLED

...

SomeFunction()
{
    int someVariable = 5;

#ifdef DEBUG_ENABLED
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

... welche dieser Methoden besser klingt für Sie und warum? Mein Gefühl ist, dass die erste sicherer ist, weil es immer etwas definiert ist und es gibt keine Gefahr, die es anderswo andere definiert zerstören könnte.

War es hilfreich?

Lösung

Meine erste Reaktion war #ifdef, natürlich , aber ich denke, #if tatsächlich einige wesentliche Vorteile für diese hat - hier ist der Grund:

Sie können zunächst DEBUG_ENABLED in Präprozessor und zusammengestellt Tests verwenden. Beispiel - Oft, ich will mehr Timeouts, wenn Debug aktiviert ist, so #if verwendet wird, kann ich dies schreibe

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... statt ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif

Zweitens, du bist in einer besseren Position, wenn Sie von einem #define zu einer globalen Konstante migrieren möchten. #defines werden in der Regel von den meisten C ++ Programmierern verpönt.

Und Drittens, Sie sagen, Sie eine Kluft in Ihrem Team haben. Meine Vermutung ist, bedeutet dies, verschiedene Mitglieder haben bereits verschiedene Ansätze gewählt, und Sie müssen standardisieren. Urteil, dass #if ist die bevorzugte Wahl bedeutet, dass Code #ifdef mit kompiliert -und Run- auch wenn DEBUG_ENABLED falsch ist. Und es ist viel leichter aufzuspüren und Debug-Ausgabe zu entfernen, die erzeugt wird, wenn es nicht als umgekehrt sein sollte.

Oh, und eine kleinere Lesbarkeit Punkt. Sie sollten wahr / falsch, anstatt 0/1 in Ihrem #define nutzen können, und weil der Wert eine einzelne lexikalische Token ist, dann ist es das einzige Mal, Sie brauchen keine Klammern um ihn herum.

#define DEBUG_ENABLED true

statt

#define DEBUG_ENABLED (1)

Andere Tipps

Sie sind beide abscheulich. Stattdessen tun:

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif

Dann, wenn Sie Debug-Code benötigen, legt es in D();. Und Ihr Programm ist nicht mit häßlichen Labyrinthen von #ifdef verunreinigt.

#ifdef prüft nur, ob ein Token definiert ist, gegeben

#define FOO 0

dann

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"

Wir haben das gleiche Problem auf mehreren Dateien hatten, und es gibt immer das Problem mit den Menschen einer „kennzeichnet Flagge“ Datei (mit einer Code-Basis von> 41.000 Dateien ist es einfach zu tun).

Wenn Sie hatte feature.h:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H

Aber dann vergessen Sie die Header-Datei in file.cpp enthalten:

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif

Dann Sie ein Problem haben, interpretiert der Compiler in diesem Fall COOL_FEATURE nicht definiert als „falsch“ zu sein und nicht den Code aufzunehmen. Ja gcc tut ein Flag unterstützen, die einen Fehler für nicht definierte Makros verursacht ... aber die meisten 3. Code Partei entweder definiert oder Merkmale definiert nicht, so dass dies nicht tragbar wäre.

Wir angenommen haben, einen tragbaren Weg für diesen Fall zu korrigieren sowie die Prüfung für eine Funktion des Staates. Funktionsmakros

Wenn Sie den oben genannten feature.h geändert:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H

Aber dann vergessen haben, wieder die Header-Datei in file.cpp enthalten:

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif

Der Präprozessor wegen der Verwendung eines nicht definierten Funktion Makro errored aus hätte.

Für die Zwecke der Durchführung der bedingten Kompilierung, #if und #ifdef sind fast die gleiche, aber nicht ganz. Wenn Ihre bedingte Kompilierung auf zwei Symbole hängt dann #ifdef nicht so gut funktionieren. Zum Beispiel: Angenommen, Sie zwei bedingte Kompilierung Symbole, PRO_VERSION und TRIAL_VERSION, könnten Sie so etwas wie diese:

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif
Mit

#ifdef die oben viel komplizierter werden, vor allem des #else Teil immer zu arbeiten.

ich auf Code arbeiten, die ausführlich die bedingte Kompilierung verwendet und wir haben eine Mischung aus #if & #ifdef. Wir neigen dazu, # ifdef / # ifndef für den einfachen Fall und #if, wenn zwei oder mehr Symbole werden Auswertung zu verwenden.

Ich denke, es ist ganz eine Frage des Stils. Weder wirklich hat einen eindeutigen Vorteil gegenüber den anderen.

Konsistenz ist wichtiger als jeder besondere Wahl, würde ich so empfehlen, dass Sie zusammen mit Ihrem Team zu bekommen und einen Stil auszuwählen, und daran halten.

ich mich lieber:

#if defined(DEBUG_ENABLED)

Da es macht es einfacher, Code zu erstellen, die für die entgegengesetzte Bedingung sieht viel leichter zu erkennen:

#if !defined(DEBUG_ENABLED)

vs.

#ifndef(DEBUG_ENABLED)

Es ist eine Frage des Stils. Aber ich empfehle eine prägnante Art und Weise, dies zu tun:

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);

Sie tun dies einmal, dann verwenden Sie immer debug_print (), um entweder Druck oder nichts tun. (Ja, das ist in beiden Fällen kompilieren.) Auf diese Weise wird der Code nicht mit Präprozessordirektiven verstümmelt werden.

Wenn Sie die Warnung erhalten „Ausdruck hat keine Wirkung“ und will es los zu bekommen, ist hier eine Alternative:

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);

#if gibt Ihnen die Möglichkeit, es auf 0 zu setzen, die Funktionalität zu deaktivieren, während immer noch detektiert wird, dass der Schalter ist da.
Persönlich #define DEBUG 1 ich immer so kann ich es mit fangen entweder eine #if oder #ifdef

#if und #define MY_MACRO (0)

Mit #if bedeutet, dass Sie ein „definieren“ Makro erstellt, das heißt, etwas, das in dem Code gesucht werden wird ersetzt werden „(0)“. Dies ist die „Makro Hölle“ Ich hasse in C ++ zu sehen, weil sie den Code mit potentiellen Codeänderungen belastet.

Zum Beispiel:

#define MY_MACRO (0)

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

gibt die folgenden Fehler auf g ++:

main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|

Nur ein Fehler.

Was bedeutet, dass Ihr Makro erfolgreich mit Ihrem C ++ Code interagierten: Der Aufruf der Funktion erfolgreich war. In diesem einfachen Fall ist es amüsant. Aber meine eigene Erfahrung mit Makros leise mit meinem Code zu spielen, ist nicht voller Freude und fullfilment, so ...

#ifdef und #define MY_MACRO

#ifdef bedeutet, dass Sie etwas „definieren“. Nicht, dass Sie geben einen Wert. Es ist immer noch umweltfreundlich, aber zumindest wird es „durch nichts ersetzt“ wird, und nicht von C ++ Code als lagitimate Code-Anweisung gesehen. Der gleiche Code oben, mit einem einfachen definieren, ist es:

#define MY_MACRO

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

Gibt die folgenden Warnungen:

main.cpp||In function ‘int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function ‘int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|

So ...

Fazit

ich lieber ohne Makros in meinem Code leben würde, aber aus mehreren Gründen (definieren Kopfschutz oder Debug-Makros), kann ich nicht.

Aber zumindest, Ich mag sie die am wenigsten interaktive möglich mit meinem legitimen C ++ Code zu machen. Was bedeutet, #define ohne Wert verwendet wird, mit #ifdef und #ifndef (oder sogar #if wie von Jim Buck vorgeschlagen definiert), und vor allem, ihnen Namen so lange und so fremd niemanden in seinem / ihrem rechten Verstand geben wird verwenden es „zufällig“, und das wird in keiner Weise es legitim, C ++ Code beeinflussen.

Post Scriptum

Nun, da ich meinen Beitrag Re-Lektüre, frage ich mich, ob ich nicht etwas Wert zu finden versuchen sollte, die nicht immer jemals richtig C wird ++ meiner definieren hinzuzufügen. So etwas wie

#define MY_MACRO @@@@@@@@@@@@@@@@@@

, die mit #ifdef und #ifndef verwendet werden könnte, aber nicht Code lassen, wenn innerhalb einer Funktion verwendet kompiliert ... habe ich versucht, diese erfolgreich auf g ++, und es gab den Fehler:

main.cpp|410|error: stray ‘@’ in program|

Interessant. : -)

Die erste scheint mir klarer. Es scheint natürlicher es einen Flag macht im Vergleich zu definierten / nicht definiert ist.

Beide sind genau gleich. In idiomatischem Gebrauch wird nur prüfen Definiert (und was ich in Ihrem Beispiel verwenden würde) #ifdef verwendet, während #if in komplexeren Ausdrücken, wie #if defined (A) verwendet wird &&! Definiert (B).

Ein wenig OT, aber Ein- / Ausschalten mit dem Vorprozessor Anmeldung ist auf jeden Fall suboptimal in C ++. Es gibt schöne Logging-Tools wie Apache log4cxx , die Open-Source sind und nicht beschränken wie Sie Ihre Anwendung verteilen. Sie ermöglichen es, auch Protokolliergrade ohne erneute Kompilierung zu ändern, haben eine sehr niedrige Overhead, wenn Sie weg die Protokollierung, und geben Ihnen die Möglichkeit, die Protokollierung vollständig auszuschalten in der Produktion.

Das ist nicht eine Frage des Stils überhaupt. Auch die Frage ist leider falsch. Sie können diese Präprozessordirektiven im Sinne von besser vergleichen oder sicherer zu machen.

#ifdef macro

bedeutet „wenn Makro definiert“ oder „wenn Makro vorhanden ist“. Der Wert von Makro hier nicht wichtig. Es kann sein, was auch immer.

#if macro

, wenn immer auf einen Wert zu vergleichen. Im obigen Beispiel ist es der Standard-implizite Vergleich:

#if macro !=0

Beispiel für die Verwendung von #if

#if CFLAG_EDITION == 0
    return EDITION_FREE;
#elif CFLAG_EDITION == 1
    return EDITION_BASIC;
#else
    return EDITION_PRO;
#endif

Sie können nun entweder die Definition von CFLAG_EDITION setzen entweder im Code

#define CFLAG_EDITION 1 

oder Sie können das Makro als Compiler-Flag gesetzt. Auch sehen hier .

Ich habe #ifdef zu verwenden, aber wenn ich für die Dokumentation doxygen geschaltet, fand ich, dass kommentierte-out-Makros kann nicht dokumentiert werden (oder zumindest Doxygen erzeugt eine Warnung). Das bedeutet, dass ich nicht die Funktion Schalter Makros dokumentieren können, die momentan nicht aktiviert werden.

Obwohl es möglich ist, die Makros für Doxygen nur zu definieren, bedeutet dies, dass die Makros in den nicht aktiven Teilen des Codes werden dokumentiert, auch. Ich mag persönlich die Funktion Schalter zeigen und sonst nur dokumentieren, was zur Zeit ausgewählt ist. Darüber hinaus macht es den Code ziemlich chaotisch, wenn es viele Makros, die nur definiert werden müssen, wenn Doxygen die Datei verarbeitet werden.

Daher ist in diesem Fall ist es besser, immer die Makros zu definieren und #if verwenden.

Ich habe immer verwendet #ifdef und Compiler-Flags zu definieren ...

Alternativ können Sie eine globale Konstante deklarieren, und verwenden Sie den C ++, wenn statt der Präprozessor #if. Der Compiler sollte die nicht verwendeten Zweige weg für Sie optimieren und Ihr Code sauberer sein.

Hier ist, was C ++ Gotchas von Stephen C. Dewhurst sagt über # wenn die verwendet wird.

Es gibt einen Unterschied bei unterschiedlicher Art und Weise eines bedingten zu definieren, um den Fahrer angeben:

diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )

Ausgabe:

344c344
< #define A 
---
> #define A 1

Das bedeutet, dass -DA Synonym für -DA=1 und wenn der Wert nicht angegeben wird, dann kann es zu Problemen bei #if A Nutzung führen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top