2015-01-15 5 views
50

switch Aussagen super nützlich sein, aber mit einem gemeinsamen Fehler führen, dass ein Programmierer eine break-Anweisung vergessen:C++ Force-Compiler-Fehler/Warnung auf impliziten Durchfall in Schalter

switch(val) { 
    case 0: 
     foo(); 
     break; 
    case 1: 
     bar(); 
     // oops 
    case 2: 
     baz(); 
     break; 
    default: 
     roomba(); 
} 

Sie werden nicht offensichtlich eine Warnung erhalten, da manchmal ein Durchfallen ausdrücklich erwünscht ist. Ein guter Codierungsstil legt nahe, zu kommentieren, wenn Ihr Durchschlagen bewusst ist, aber manchmal ist das nicht ausreichend.

Ich bin ziemlich sicher, dass die Antwort auf diese Frage nein ist, aber: gibt es derzeit (oder in der Zukunft) eine Möglichkeit, den Compiler bitten zu können, einen Fehler (oder zumindest eine Warnung!) wenn Ihr case nicht mindestens eines von break; oder etwas zu der Wirkung von // fallthru hat? Es wäre schön, eine defensive Programmieroption für die Verwendung von switch-Anweisungen zu haben.

+1

Welchen Compiler benutzen Sie? Ich weiß, dass ich [Diskussion] gesehen habe (http://stackoverflow.com/questions/7703358/how-can-i-tell-gcc-to-warn-or-fail-on-switch-case-statements-without- a-break) des Hinzufügens einer solchen optionalen Warnung zu gcc. Nicht sicher, was daraus geworden ist. – CoryKramer

+0

klingt wie eine Änderung, die möglicherweise Legacy-Code brechen könnte, wenn es sich um etwas anderes als eine Warnung handelt. –

+0

@Cyber ​​ich gcc bin mit 4.7 – Barry

Antwort

65

Nun clang hat -Wimplicit-fallthrough, die ich nicht kannte, aber mit -Weverything gefunden. Also für diesen Code mir gibt es die Warnung folgende (see it live):

warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough] 
case 2: 
^ 
note: insert '[[clang::fallthrough]];' to silence this warning 
case 2: 
^ 
[[clang::fallthrough]]; 
note: insert 'break;' to avoid fall-through 
case 2: 
^ 
break; 

Die einzige Dokumentation ich für diese Flagge finden im Attribute Reference ist, die sagt:

The clang::fallthrough attribute is used along with the -Wimplicit-fallthrough argument to annotate intentional fall-through between switch labels. It can only be applied to a null statement placed at a point of execution between any statement and the next switch label. It is common to mark these places with a specific comment, but this attribute is meant to replace comments with a more strict annotation, which can be checked by the compiler.

und liefert ein Beispiel

case 44: // warning: unannotated fall-through 
g(); 
[[clang::fallthrough]]; 
case 55: // no warning 

Diese Verwendung eines attribute zu markieren: wie explizite durch~~POS=TRUNC markieren Der explizite Durchgriff hat den Nachteil, dass er nicht tragbar ist. Visual Studio einen Fehler erzeugen und gcc generiert die folgende Warnung:

warning: attributes at the beginning of statement are ignored [-Wattributes] 

was ein Problem ist, wenn Sie -Werror verwenden möchten.

habe ich versucht, dies mit gcc 4.9 und es sieht aus wie gcc diese Warnung nicht unterstützt:

error: unrecognized command line option '-Wimplicit-fallthrough'

Ab GCC 7, -Wimplicit-fallthrough unterstützt wird und __attribute__((fallthrough)) verwendet werden kann, die Warnungen zu unterdrücken, wenn fallthrough beabsichtigt ist. GCC erkennt in bestimmten Szenarien "Fall-through" -Kommentare, aber es kann verwechselt werden fairly easily.

Ich sehe keine Möglichkeit, eine solche Warnung für Visual Studio zu generieren.

Hinweis, Chandler Carruth erklärt, dass -Weverything nicht für den produktiven Einsatz ist:

This is an insane group that literally enables every warning in Clang. Don't use this on your code. It is intended strictly for Clang developers or for exploring what warnings exist.

aber es ist nützlich, um herauszufinden, was Warnungen vorhanden sind.

+10

Sehr cool. Gut gemacht, klingeln. – Barry

+2

Das ist der Grund, warum 'Weverything' (was die GCC-Leute anscheinend nicht nachahmen) so nützlich ist. Nein, Entwickler werden es wahrscheinlich nicht in den "offiziellen" Warnungen verwenden, die zum Kompilieren ihres Produktionscodes verwendet werden, aber es ist extrem hilfreich, ab und zu Codebasen zu starten, um zu sehen, ob wirklich hilfreiche Warnungen Ihnen fehlen. –

+0

@KyleStrand Ich stimme zu, ich habe ein paar nützliche Warnungen auf diese Weise gefunden. –

13

Ich schreibe immer eine break; vor jedem case wie folgt:

switch(val) { 
    break; case 0: 
     foo(); 
    break; case 1: 
     bar(); 
    break; case 2: 
     baz(); 
    break; default: 
     roomba(); 
} 

Auf diese Weise ist es viel deutlicher für das Auge, wenn ein break; fehlt.Die ursprüngliche break; ist überflüssig, aber es hilft, konsistent zu sein.

Dies ist eine herkömmliche switch Aussage habe ich einfach Leerzeichen in einer anderen Art und Weise verwendet, die Newline zu entfernen, die normalerweise nach einem break; und vor den nächsten case.

+10

Wie wäre es mit' # define CASE break; Fall? ;) – fredoverflow

+4

@FredOverflow: Das würde den Zweck des Bildens des Bruches vereiteln; s sichtlich hervorstechend. – Kundor

+3

Was tun Sie im Fallfall? Einfach die Pause auslassen? – nwp

0

Hier ist eine Antwort auf zwanghaft zu hassen.

Zuerst sind switch Aussagen fancy gotos. Sie können mit anderen Kontrollfluss kombiniert werden (berühmt, Duff's Device), aber die offensichtliche Analogie hier ist ein Goto oder zwei. Hier ist ein sinnloses Beispiel:

switch (var) { 
    CASE1: case 1: 
     if (foo) goto END; //same as break 
     goto CASE2; //same as fallthrough 
    CASE2: case 2: 
     break; 
    CASE3: case 3: 
     goto CASE2; //fall *up* 
    CASE4: case 4: 
     return; //no break, but also no fallthrough! 
    DEFAULT: default: 
     continue; //similar, if you're in a loop 
} 
END: 

Empfehle ich das? Nein. In der Tat, wenn Sie darüber nachdenken, einen Fall-Through zu kommentieren, dann ist Ihr Problem eigentlich etwas anderes.

Diese Art von Code hat macht es sehr klar, dass ein Durchfall im Fall passieren kann 1, aber wie die anderen Bits zeigen, ist dies eine sehr leistungsfähige Technik im Allgemeinen, die sich auch auf Missbrauch verleiht. Sei vorsichtig, wenn du es benutzt.

Eine break vergessen? Nun, dann wirst du auch gelegentlich vergessen, welche Anmerkung du auch immer auswählst. Vergessen Sie den Fall-through bei der Änderung einer switch-Anweisung zu berücksichtigen? Du bist ein schlechter Programmierer. Beim Ändern von Schalteranweisungen (oder wirklich irgendein Code), müssen Sie zuerst verstehen sie.


Ehrlich gesagt, ich sehr selten diese Art von Fehlern machen (zu vergessen, eine Pause) - auf jeden Fall weniger als ich anderen „gemeinsamen“ Programmierfehler (strenges Aliasing, zum Beispiel) gemacht. Um sicher zu sein, tue ich derzeit (und empfehle es Ihnen) einfach //fallthrough zu schreiben, da dies zumindest die Absicht verdeutlicht.

Darüber hinaus ist es eine Realität, die Programmierer akzeptieren müssen. Korrekturlesen Sie Ihren Code nach dem Schreiben und finden Sie das gelegentliche Problem mit dem Debuggen. So ist das Leben.

+5

Kompilieren Sie auch mit allen Warnungen deaktiviert, weil Sie Korrekturlesen als ausreichend erachten? – Barry

+9

Ich hasse das zwanghaft. – matsjoyce

+0

@Barry Korrekturlesen _was_ ausreichend. Es [kompiliert einfach] (http://ideone.com/1EtHwa) und tut was es soll. Ich nehme an, du meinst die unreferentiellen Etiketten, die ich aus Gründen der Klarheit bewusst eingefügt habe. Es tut mir nicht leid. – imallett

6

Hinweis: wenn Sie konsequent eine Leerzeile zwischen Fall Klauseln setzen, das Fehlen einer ‚Pause‘ wird der Code an einen Menschen sichtbarer Skimming:

switch (val) { 
    case 0: 
     foo(); 
     break; 

    case 1: 
     bar(); 

    case 2: 
     baz(); 
     break; 

    default: 
     roomba(); 
} 

Dies ist nicht so effektiv, wenn es eine Menge Code in einzelnen Fallsätzen, aber das neigt dazu, ein schlechter Code-Geruch an sich zu sein.