2012-04-28 4 views
8

Diciamo che ho un'enumerazione.Cosa inserire in un'etichetta di default di un interruttore?

enum class ShapeName : char {TRIANGLE,CIRCLE,SQUARE}; 

e poi ho una funzione come questa:

void Function (ShapeName const shape){ 

    switch (shape){ 
     case ShapeName::TRIANGLE: 
      DoSomething1(); 
      break; 

     case ShapeName::CIRCLE: 
      DoSomething2(); 
      break; 

     case ShapeName::SQUARE: 
      DoSomething3(); 
      break; 

     default: 
      //THIS CODE BLOCK SHOULD NEVER BE EXECUTED! 
    } 

    return; 
} 

Anche se l'etichetta di default non dovrebbe mai essere eseguito, voglio rendere conto dei potenziali bug che possono sorgere se un programmatore aggiunge un altro valore 'ShapeName' e non è rappresentato nello switch.
Cosa vorresti che facessi?

1. Le asserzioni
ho potuto usare un'affermazione, ma quello che sto asserendo?

assert(false); //? 

2. Eccezioni
ho potuto un'eccezione, ma non credo che sarebbe molto buona pratica. Ho l'impressione che le eccezioni siano per eventi di runtime che non possono essere previsti a causa di determinati ambienti.

3. Esce
posso solo uscire immediatamente dal programma con un errore. Mi sembra l'idea migliore, ma non sono sicuro che sia una buona pratica. Ho pensato che il vantaggio delle asserzioni era che potevi disattivarle tutte quando eri pronto a spedire il programma. Quindi, tutto quel codice di asserzione non esisterebbe più.


Forse c'è un altro modo di cui non sono a conoscenza. Io uso un flag del compilatore che avverte dei valori non contati, ma voglio ancora sapere cosa gli altri raccomandano.

+0

4. Puoi dire al tuo compilatore di avvisare per questo? È un'opzione che mi aspetterei per le enumerazioni vecchio stile. –

+0

@SteveJessop g ++ -w -Wall -Wextra -Wswitch -Wswitch-default -Wswitch-enum -std = C++ 0x -o main main.cpp strano, non ho ricevuto alcun avviso con il codice precedente. Ho aggiunto un altro ShapeName e ho eliminato l'etichetta predefinita. Ancora niente! –

+0

Ratti. L'implementazione di C++ 11 non è ancora finita, quindi possiamo vivere nella speranza. –

risposta

9

Mi piace l'idea di affermare con un messaggio informativo. Prova questo:

assert (!"The default case of so-so switch was reached."); 

Questo restituisce sempre false, ma fornisce un messaggio che è possibile utilizzare.

Modifica:
Ho trovato la fonte da cui ho tratto questa idea dalla mia memoria; è nel seguente libro:
Standard di codifica C++ - 101 Regole e linee guida

+2

O se si non mi piace il modo in cui è scritto, 'assert (false &&" message ");' –

+3

E ricorda che gli asserti sono solitamente rimossi dalle build di "rilascio". –

+1

Per le build di rilascio, è anche possibile utilizzare l'equivalente del compilatore di ['__assume (0)'] (http://msdn.microsoft.com/en-us/library/1b3fsfxw.aspx) come suggerimento per l'ottimizzazione. – ildjarn

0

Immagino che per me dipenderebbe piuttosto da ciò che fanno i metodi DoSomething #.

Se causerà un arresto meno aggraziato in un secondo momento, è necessario interrompere il runtime quando questo viene chiamato con una spiegazione della "funzione chiamata con enum non valido: enumName" o qualcosa che dia alla notifica dello sviluppatore che non si è aggiornato questa dichiarazione di commutazione, piuttosto che lasciarli a chiedersi se qualcosa non funziona più tardi.

4

La mia preferenza per questo caso specifico, in cui si attiva un tag enum e si gestiscono tutti i casi, è di lasciare l'impostazione predefinita. In questo modo, con qualsiasi compilatore ragionevole, se qualcuno aggiunge un nuovo tag all'enum e non allo switch, riceverai un avviso in fase di compilazione sul tag che non viene gestito nello switch.

+0

Apparentemente GCC è irragionevole rispetto alle nuove classi di enumerazione C++ 11, sebbene l'interrogante non dica quale versione di GCC. –

+0

@SteveJessop gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 –

+0

@SteveJessop: gcc (provato 4.4.5, 4.5.2 e 4.6.3) mi dà "avvertimento: valore di enumerazione" ALTRO "non gestito in switch' "se rimuovo il valore predefinito e aggiungo un tag' OTHER' ... –

0

Risposta breve: utilizzare il polimorfismo per eliminare il problema.

Risposta lunga: Il modo più semplice per risolvere il problema alla radice (? Vale a dire Come prevenire istruzioni switch con le voci mancanti) sarebbe quella di evitare l'uso di istruzioni switch se c'è una possibilità di voci essere dimenticato. Le istruzioni switch sono utili in contesti locali, ma se la logica che rappresentano è duplicata in più posizioni, allora c'è la possibilità di dimenticare di aggiornarne una e ti stai preparando per gli errori di runtime.

class Shape 
{ 
    // functionality MUST be added for new shape types 
    // or a compile error will occur 
    virtual void DoSomething() const = 0; 
}; 

void Function (Shape const & shape){ 
    return shape.DoSomething(); 
} 

L'istruzione switch può ancora essere utile al sito di creazione:

enum class ShapeName : char {TRIANGLE,CIRCLE,SQUARE}; 

unique_ptr<Shape> MakeShape (ShapeName const shape){ 
    switch (shape){ 
     case ShapeName::TRIANGLE: 
      return unique_ptr<Shape>(new Triangle()); 

     case ShapeName::CIRCLE: 
      return unique_ptr<Shape>(new Circle()); 

     case ShapeName::SQUARE: 
      return unique_ptr<Shape>(new Square()); 
    } 

    throw std::runtime_error(); 
    // or whichever option you prefer from the other answers 
} 

ma questo è il posto solo esiste la logica. E anche questo può essere migliorato con il polimorfismo statico:

enum class ShapeName : char {TRIANGLE,CIRCLE,SQUARE,ELLIPSE}; 

template< ShapeName shape > 
unique_ptr<Shape> MakeShape(); 

template<> 
unique_ptr<Shape> MakeShape<ShapeName::TRIANGLE>() 
{ 
    return unique_ptr<Shape>(new Triangle()); 
} 

template<> 
unique_ptr<Shape> MakeShape<ShapeName::CIRCLE>() 
{ 
    return unique_ptr<Shape>(new Circle()); 
} 

template<> 
unique_ptr<Shape> MakeShape<ShapeName::SQUARE>() 
{ 
    return unique_ptr<Shape>(new Square()); 
} 

int main() 
{ 
    MakeShape<ShapeName::TRIANGLE>(); // okay 

    MakeShape<ShapeName::ELLIPSE>(); // compile error 
} 

Vedi Martin Folwer per maggiori informazioni.

+0

Perché un down-vote? Penso che per questo esempio forzato, sia la soluzione migliore. Le dichiarazioni switch possono essere un odore di codice, proprio per questo motivo. Il problema con 'default' è che non si ottiene un avviso del compilatore per un caso omesso; senza 'default' se sei nel tipo di switch in cui ogni caso fa un ritorno (ad es. factory), il compilatore si lamenterà della mancanza di ritorno alla fine della funzione. Penso che MSVC abbia avuto una volta una macro non raggiungibile per aiutare qui. –