2010-11-25 8 views
11

In meinem Code, den ich verwendet Standard-Fallback-Fälle zu schreiben wie die folgenden enthalten behauptet, mich zu schützen gegen das Vergessen Sie den Schalter für den Fall, SemantikAktiviert "default" Fallstörung Sprungtischoptimierung?

switch(mode) { 
case ModeA: ... ; 
case ModeB: ... ; 
case .. /* many of them ... */ 
default: { 
    assert(0 && "Unknown mode!"); 
    return ADummyValue(); 
} 
}; 

ändern ich, ob die künstliche Fall- Jetzt aktualisieren fragen, Back Check Default Case wird die Jump-Table-Generationen stören? Stellen Sie sich vor, dass "ModeA" und "ModeB" usw. aufeinanderfolgend sind, damit der Compiler in eine Tabelle umgewandelt werden kann. Da der "Standard" -Fall eine tatsächliche "return" -Anweisung enthält (da die Assert im Freigabemodus verschwindet und der Compiler über eine fehlende return-Anweisung meckert), scheint es unwahrscheinlich, dass der Compiler die Standardverzweigung optimiert.

Was ist der beste Weg, damit umzugehen? Ein Freund empfahl mir, "ADummyValue" durch eine Nullzeiger-Dereferenzierung zu ersetzen, so dass der Compiler bei Vorhandensein von nicht definiertem Verhalten auslassen könnte, um vor einer fehlenden return-Anweisung zu warnen. Gibt es bessere Möglichkeiten, dies zu lösen?

+0

die 'assert' Gegeben, ist es wahrscheinlich am besten, entweder' throw' oder 'terminate' statt' return'. –

Antwort

1

Ich sehe nur 1 Lösung für den Fall, dass die Optimierung tatsächlich gestört ist: der berüchtigte "#ifndef NDEBUG" rund um den Standardfall. Nicht der schönste Trick, aber klar in dieser Situation.

BTW: Haben Sie schon gesehen, was Ihr Compiler mit und ohne den Standardfall tut?

+0

Habe noch nicht den generierten Code angeschaut. –

+0

@Johannes: nun, zumindest Jerry Coffin. – stefaanv

1

Wenn Sie einen Zustand haben, der nie erreicht werden sollte, sollten Sie das Programm beenden, weil es gerade im Freigabemodus einen unerwarteten Zustand erreicht hat (Sie könnten diplomatischer sein und Benutzerdaten speichern und alles tun das andere nette Zeug vor dem Untergehen).

Und bitte nicht über Mikro-Optimierungen besessen, es sei denn, Sie haben tatsächlich (mit einem Profiler) gemessen, dass Sie sie brauchen.

+0

Aber ich möchte im Freigabemodus nicht nach dem unerreichbaren Zustand suchen. Meine Annahme ist, dass der Code keine Fehler enthält. Natürlich ist das wahrscheinlich eine falsche Annahme, aber es scheint mir notwendig zu sein, die Prüfung für den unerreichbaren Zustand loszuwerden (und dies ist sehr leistungskritisch: Name-Lookup-Routine, implizite Konvertierungsroutine usw. einer Skriptsprache-Laufzeit). –

1

Der beste Weg, damit umzugehen, ist nicht die Assert zu deaktivieren. Auf diese Weise können Sie auch mögliche Fehler im Auge behalten. Manchmal ist es besser, wenn die Anwendung mit einer guten Nachricht abstürzt und erklärt, was genau passiert ist, um dann weiter zu arbeiten.

3

Zumindest mit den Compilern, die ich angeschaut habe, ist die Antwort im Allgemeinen nein. Die meisten von ihnen werden eine switch-Anweisung wie folgt kompilieren entspricht in etwa den Code zu:

if (mode < modeA || mode > modeLast) { 
    assert(0 && "Unknown mode!"); 
    return ADummyValue(); 
} 
switch(mode) { 
    case modeA: ...; 
    case modeB: ...; 
    case modeC: ...; 
    // ... 
    case modeLast: ...; 
} 
+0

Was für eine Schande. Ich muss die Validierung für die Geschwindigkeit opfern :( –

+0

@Johannes: Würde ein Array von Funktionszeigern ähnlich wie Sprungtabellen funktionieren? Suchen Sie den Funktionszeiger vom Array und rufen Sie ihn auf. (Sprungtabelle besteht aus einem Registersprung und einem festen Sprung; Array von Funktionszeiger wäre eine Registerlast und ein Registersprung, nehme ich an?) – rwong

2

wenn Sie mit „default“ (ha !) <assert.h>, ohnehin zum NDEBUG Makro gebunden ist die Definition, also vielleicht nur

case nevermind: 
#if !defined(NDEBUG) 
    default: 
     assert("can" && !"happen"); 
#endif 
    } 
+0

das empfehle ich. Es wird keine Geschwindigkeit geopfert, Sie können immer noch auf unerklärte Typen testen, und Sie können immer noch Compiler-Warnungen erhalten –

0

Verwenden Compiler Erweiterungen:

// assume.hpp 
#pragma once 

#if defined _MSC_VER 
#define MY_ASSUME(e) (__assume(e), (e) ? void() : void()) 
#elif defined __GNUC__ 
#define MY_ASSUME(e) ((e) ? void() : __builtin_unreachable()) 
#else // defined __GNUC__ 
#error unknown compiler 
#endif // defined __GNUC__ 

-

// assert.hpp 
#include <cassert> 
#include "assume.hpp" 

#undef MY_ASSERT 
#ifdef NDEBUG 
#define MY_ASSERT MY_ASSUME 
#else // NDEBUG 
#define MY_ASSERT assert 
#endif // NDEBUG