Warum sollte man MACRO + 0! = 0 verwenden?

72

In meiner aktuellen Codebasis sehe ich folgendes Muster:

#if SOMETHING_SUPPORTED+0 != 0
...
#endif

Leider ist dies eine sehr alte Codebasis und niemand weiß, wie und warum sie begonnen hat. Ich denke, es hat in C angefangen und es wurde langsam mit Klassen in C konvertiert und jetzt tendiert es zu C ++

Ich kann keinen offensichtlichen Vorteil der Verwendung des vorherigen Konstrukts anstelle des "Klassikers" erkennen, aber vielleicht fehlt mir etwas:

#if SOMETHING_SUPPORTED
...
#endif

Wissen Sie, warum man #if MACRO+0 != 0statt verwenden sollte #if MACRO?

Mircea Ispas
quelle
4
Könnte
3
Das != 0ist sicherlich überflüssig
MM

Antworten:

70

Der Hinweis hier ist, dass die Codebasis sehr alt ist.

Dieser Trick existiert wahrscheinlich, weil der Code einmal auf einen Compiler mit einem sehr alten Präprozessor portiert wurde, der undefinierte Makros in Präprozessorbedingungen nicht als 0 behandelt #if.

Das heißt, ab 1989 ANSI C wurde standardisiert, dass, wenn wir haben:

#if foo + bar - xyzzy

die Richtlinie unterliegt Makro - Ersatz, dass dann , wenn er foo, baroder xyzzyMakros sind, sie ersetzt werden. Dann werden alle verbleibenden Bezeichner, die nicht ersetzt wurden, durch ersetzt 0. Wenn fooalso definiert ist als 42, aber barund xyzzyüberhaupt nicht definiert sind, erhalten wir:

#if 42 + 0 - 0

und nicht zum Beispiel schlechte Syntax:

#if 42 + -

oder ein anderes Verhalten, wie die Diagnose, dass es barnicht definiert wurde.

Auf einem Präprozessor, bei dem undefinierte Makros als Leerzeichen behandelt werden, wird #if SOMETHING_SUPPORTEDauf just erweitert #if, was dann fehlerhaft ist.

Nur so IDENT+0macht dieser Trick wirklich Sinn. Sie würden dies einfach nie tun wollen, wenn Sie sich darauf verlassen können, dass die Vorverarbeitung ISO C-konform ist.

Der Grund dafür ist, dass, wenn SOMETHING_SUPPORTEDerwartet wird, dass sie numerische Werte haben, es fälschlicherweise verstreut ist, sie einfach als Leerzeichen zu definieren. Idealerweise möchten Sie erkennen, wann dies geschehen ist, und die Kompilierung mit einer Diagnose beenden.

Zweitens, wenn Sie tun , eine solche Streu-brained Nutzung unterstützen Sie mit ziemlicher Sicherheit ein explizit definiert, aber leere Symbol zu verhalten , als ob es den Wert 1 hatte, nicht den Wert 0. Ansonsten sind Sie eine Falle zu schaffen. Jemand könnte dies in der Compiler-Befehlszeile tun:

 -DSOMETHING_SUPPORTED=$SHELL_VAR  # oops, SHELL_VAR expanded to nothing

oder im Code:

 #define SOMETHING_SUPPORTED  /* oops, forgot "1" */

Niemand wird ein oder für ein Symbol hinzufügen , um die von ihm gesteuerte Funktion auszuschalten ! Der Programmierer, der ein ohne das einfügt, wird vom Verhalten von überrascht sein#define-D#define SOMETHING_SUPPORTED1

 #if SOMETHING_SUPPORTED+0

Dadurch wird das Material übersprungen, das aktiviert werden sollte.

Aus diesem Grund vermute ich, dass nur wenige C-Programmierer, die dies lesen, jemals eine solche Verwendung gesehen haben, und ich vermute, dass dies nur eine Problemumgehung für das Verhalten des Präprozessors ist, dessen beabsichtigter Effekt darin besteht, den Block zu überspringen, wenn er SOMETHING_SUPPORTEDfehlt. Die Tatsache, dass es sich um eine "Programmierfalle" handelt, ist nur ein Nebeneffekt der Problemumgehung.

Um ein solches Präprozessorproblem zu umgehen, ohne eine Programmierfalle zu erstellen, muss irgendwo in der Übersetzungseinheit Folgendes vorhanden sein:

#ifndef SOMETHING_SUPPORTED
#define SOMETHING_SUPPORTED 0
#endif

und dann woanders einfach benutzen #if SOMETHING_SUPPORTED. Vielleicht kam dieser Ansatz dem ursprünglichen Programmierer nicht in den Sinn, oder vielleicht hielt dieser Programmierer diesen +0Trick für ordentlich und legte Wert auf seine Selbstbeherrschung.

Kaz
quelle
1
Sie liegen nicht falsch, aber ... alle Unixy-Compiler, von denen ich weiß, dass sie -DTHINGals Abkürzung behandelt werden -DTHING=1; Wenn Sie tatsächlich möchten, dass das Makro zu nichts erweitert wird, müssen Sie dies sagen -DTHING=.
zwol
1
@zwol Könnte passieren -DFOO=$FOO_ON, wenn die Shell- Variable wie erwartet auf nichts anstatt auf 0 oder 1 erweitert wird! (bearbeitet).
Kaz
@zwol In der Tat. Dies liegt wahrscheinlich daran, dass Build-Skripte auf einen einzelnen Anwendungsfall zugeschnitten sind und ständig mit nahezu denselben Eingaben arbeiten. Daher ist ihre Logik auf das erwartete Verhalten dieses Anwendungsfalls ausgerichtet. (Nicht anders als die Wegwerfskripte, die Leute für ihren eigenen Gebrauch schreiben, oder vielleicht sogar noch schlimmer).
Kaz
44

#if X+0 != 0unterscheidet sich von #if Xdem Fall, in Xdem leer definiert ist (Hinweis: Dies unterscheidet sich von dem Fall, dass es Xnicht definiert ist), z.

#define X

#if X          // error
#if X+0 != 0   // no error; test fails

Es ist sehr üblich, leere Makros zu definieren: Die Projektkonfiguration generiert möglicherweise einen allgemeinen Header, der eine Reihe von Zeilen enthält #define USE_FOO, #define USE_BARum vom System unterstützte Funktionen zu aktivieren usw.

Das != 0ist redundant, der Code hätte einfach sein können #if X+0.


Der Vorteil der Verwendung #if X+0besteht also darin, dass die XKompilierung fortgesetzt wird, wenn sie als leer definiert ist, wobei der Block übersprungen wird, anstatt einen Fehler auszulösen.

Ob dies eine gute Idee ist, ist umstritten. Ich persönlich würde sie #ifdeffür boolesche Makros wie USE_SOME_FEATUREund #iffür Makros verwenden, bei denen der Wert beispielsweise eine Reihe von Ganzzahlen sein kann. und ich möchte einen Fehler sehen, wenn ich versehentlich #ifetwas verwende, das als leer definiert ist.

MM
quelle
1
Ein Flag , wie -DNDEBUGwird definiert NDEBUGzu sein 1, nicht leer ist .
Dietrich Epp
@DietrichEpp du hast recht. -DNEBUG=würde es als leer definieren
MM
Ich habe noch nie einen Fehler gesehen. Das wird #ifmeiner Erfahrung nach nur als falsch bewertet (was wahrscheinlich immer noch nicht erwünscht ist). EDIT: Ich stehe korrigiert da, dies erzeugt tatsächlich einen Fehler. Ich bin mir nicht sicher, was ich in der Vergangenheit gesehen hatte. Vielleicht war es compilerspezifisch.
Cameron
35

Lass uns einen Tisch machen!

X       #if X     #if X+0 != 0
<undef> false     false
<empty> error     false
0       false     false
1       true      true
2       true      true
a       false     false
xyz     false     false
12a     error     error
12 a    error     error

Der einzige Unterschied, den wir (dank Kommentatoren) festgestellt haben, ist der Fall, in dem X definiert ist, aber keinen Wert hat (wie eine leere Zeichenfolge). Ich habe die +0 != 0Variante noch nie gesehen .

John Zwinck
quelle
1
@ MM: Ich denke auch. Ich teste es einfach. Es funktioniert wie erwartet. Also habe ich meine Antwort als falsch gelöscht.
Martin York
@ MM: Nein, ist es nicht. stackoverflow.com/questions/1643820/…
Dietrich Epp
@DietrichEpp diese Frage bezieht sich auf X undefiniert, nicht X leer. Ich denke, die Tabelle in dieser Antwort sollte "leer" in "undefiniert" umbenennen und eine neue Zeile für "leer" hinzufügen
MM
@LokiAstari vielleicht hast du etwas falsch gemacht, siehe hier
MM
Sie können andere Nichtübereinstimmungen finden, wenn Sie die Operator-Prioritätsregeln missbrauchen, z. B. mit #define X 1^2.
Ilmari Karonen
7

Es ist eine andere Art zu schreiben

 #if defined(MACRO) && MACRO != 0

Die +0 ist da, um sicherzustellen, dass das Ergebnis eine Zahl ist. Wenn MACROnicht definiert, wird der Syntaxfehler vermieden, der sich daraus ergeben würde #if MACRO != 0.

Zeug von schlechter Qualität, aber leg dich nicht damit an, es sei denn du musst.

Marquis von Lorne
quelle
#if defined (MACRO) && MACRO! = 0 => false, wenn das Makro nicht definiert ist #if MACRO + 0! = 0 => false, wenn das Makro nicht definiert ist, aber aus einem anderen Grund: 0 + 0! = 0 Sie haben das gleiche Ergebnis, aber sie sind nicht gleichbedeutend stackoverflow.com/questions/5085392/…
Mircea Ispas
@Felics Ich kann "sie haben das gleiche Ergebnis, aber sie sind nicht gleichwertig" keine nützliche Bedeutung zuschreiben. Ich habe nicht gesagt , sie waren eigentlich gleichwertig. Ihr Punkt bleibt dunkel.
Marquis von Lorne
Ich wollte darauf hinweisen, dass dies #if MACRO+0 !=0keine andere Schreibweise ist #if defined(MACRO) && MACRO != 0. Beispiel: #define MACROwürde zu einem Buildfehler für führen #if defined(MACRO) && MACRO != 0und würde gut kompilieren für #if MACRO+0 !=0
Mircea Ispas
8
Dies ist falsch: Danach wird der #define MACROOP-Code kompiliert, Ihre Version jedoch nicht
MM