2010-07-27 15 views
9

Ho cambiato leggermente titolo perché ho pensato che fosse una domanda più appropriata.C++, questa dichiarazione goto è giustificata?

Vuoi refactoring (sembra legittimo uso di goto)? Se, come dovresti refactoring il seguente codice per rimuovere go to statement?

if (data.device) { 
    try { 
     ... 
    } 
    catch(const std::exception&) { goto done; } 
    ... // more things which should not be caught 
done: ; 
} 

dichiarazione completa

#ifdef HAVE_GPU 
      // attempt to use GPU device 
      if (data.device) { 
       try { 
        Integral::Gpu eri(S, R, Q, block.shell()); 
        eri(basis.centers(), quartets, data.device); 
       } 
       // if GPU fails, propagate to cpu 
       catch(std::exception) { goto done; } 
       data.device += size; 
       host_index.extend(block_index); 
       block_index.data.clear(); 
      done: ; 
      } 
#endif 

ringraziamento

dopo aver visto la preferenza della maggior parte, ho deciso di andare con la bandiera, ma con il signor York commento.

Grazie a tutti

+2

Difficile dire senza vedere cosa c'è nei blocchi, ma un'altra possibilità è spostare il codice all'interno di if in una funzione. (Potrebbe anche migliorare la leggibilità, forse no.) Quindi puoi tornare presto se succede qualcosa di brutto. – GManNickG

+4

Catch con riferimento const, per favore. –

+0

@GMan ha aggiunto la dichiarazione completa. La funzione potrebbe essere eccessiva, poiché non c'è chiusura. – Anycorn

risposta

15
if (data.device) 
{ 
    bool status = true; 

    try 
    { 
     ... 
    } 
    catch(std::exception) 
    { 
     status = false; 
    } 

    if (status) 
    { 
    ... // more things which should not be caught 
    } 
} 
+0

, ma quale preferiresti? Stato o goto? – Anycorn

+1

@aaa: Potrei non essere la persona migliore da chiedere dato che sono un antigotoo non pentito. :-) In precedenza, ieri, ero stato svalutato per aver risposto a una domanda con "Ecco come lo faresti senza goto: ..." Ma le mie ragioni hanno più a che fare con la scalabilità e l'ortogonalità rispetto a una semplice avversione per la parola chiave. –

+5

Perché le persone insistono nell'introdurre nuove variabili e rami solo per evitare un goto? Smetti di bere l'aiuto kool dei documenti "Considerato Nocivo" (meglio ancora, in realtà leggi il foglio e vedrai che Dijkstra non salva mai usarli) e in realtà metti un po 'di pensiero critico su quello che stai facendo. Hai aggiunto più stati e più rami al tuo codice solo per soddisfare "Non ho usato' goto'. " –

2

Non riesci a cogliere l'eccezione al di fuori del se?

+0

altra logica lo renderebbe molto brutto, quindi preferirei non rispondere – Anycorn

3
if (data.device) { 
    bool ok = true; 
    try { 
     ... 
    } 
    catch(std::exception) { ok = false; } 

    if(ok) { 
     ... // more things which should not be caught 
    } 
} 
+0

Stessa risposta come mostrato sopra in una delle risposte precedenti. –

7

si può prendere l'eccezione e rigenerare una deroga specifica che può essere gestito al di fuori del blocco condizionale.

// obviously you would want to name this appropriately... 
struct specific_exception : std::exception { }; 

try { 
    if (data.device) { 
     try { 
      // ... 
     } 
     catch(const std::exception&) { 
      throw specific_exception(); 
     } 

     // ... more things which should not be caught ... 
    } 
} 
catch (const specific_exception&) { 
    // handle exception 
} 
+1

grazie. Ci ho pensato, ma a mio parere aggiunge più complessità di goto/flag – Anycorn

+0

@aaa: Questo è, a mio parere, molto più pulito di entrambe le opzioni. In questo caso un 'goto' è del tutto ingiustificato e se si utilizza un flag di stato si finisce per mescolare due diversi meccanismi di segnalazione degli errori (flag di stato ed eccezioni) in un singolo blocco di codice, che aumenta notevolmente la complessità e rende più difficile motivo per il codice. –

+0

Qual è il significato di * goto è giustificato/ingiustificato *? Il comportamento di 'goto' è ben definito e (credo) farà ciò che ti aspetteresti, o no? Sono d'accordo che aggiungere un controllo bool è * non * il modo di andare comunque. –

1

Che ne dici di utilizzare solo un flag e aggiungere un'istruzione condizionale?

int caught = 0; 
if (data.device) { 
    try { 
     /* ... */ 
    } catch (std::exception e) { caught = 1; } 
    if (!caught) { 
     // more stuff here 
    } 
    // done: ... 
} 
+5

Hai qualcosa contro il tipo 'bool'? –

8

Primo: goto non è il male di per sé. Refactoring per il puro gusto di non avere le lettere "goto" nel tuo codice non ha senso. Refactoring a qualcosa che ottiene la stessa cosa fatta più pulito di un goto va bene. Cambiare un cattivo design in uno che è migliore e non ha bisogno di un goto o di un sostituto per esso va bene, anche.

Detto questo, direi che il tuo codice sembra esattamente quello per cui è stato finalmente inventato. Troppo triste C++ non ha qualcosa di simile ... quindi forse la soluzione più semplice è lasciarla così.

+0

Hell yeah! Vai! Via! Via! Via! a! – Gianni

+3

Sfortunatamente finalmente() non è la soluzione corretta qui dato che il codice viene usato solo quando non ci sono eccezioni, infine d'altra parte significa che dovrebbe essere sempre eseguito (anche quando sono presenti delle eccezioni). Sebbene sia infine necessario per linguaggi come C# e Java, non è richiesto per C++ in quanto esistono tecniche migliori per implementare la stessa cosa. Anche se sono d'accordo con tutti gli altri punti. Non aggiustare ciò che non è rotto. –

+1

Voto positivo per l'uso corretto e l'ortografia di "per sé". –

2

Quando c'è un gruppo di codice che deve essere terminato in base a qualche condizione, il mio costrutto preferito è utilizzare un ciclo "do {} while (0)" e "interruzione" quando appropriato. Non so quale pausa; lo farò in una presa, però. Un "goto" potrebbe essere la soluzione migliore se "break" non funzionerà.

+0

Questo è peggio di un 'goto' in ogni modo. – Thomas

+0

+1 È un po 'pessimo come il 'goto', ma è meglio che aggiungere una variabile extra e dover aggiungere condizioni nel codice. E sono propenso a pensare che entrambi siano meglio di lanciare un nuovo tipo di eccezione ... Continuo a preferire il 'goto', ma diamine questa risposta merita non meno degli altri :) –

1
catch(std::exception) { return; } 

Questo dovrebbe fare il trucco. Naturalmente, presumo che lo done sia effettivamente alla fine di una funzione.

Se è necessario eseguire codice aggiuntivo quando si verifica un'eccezione, restituire uno stato o generare un'eccezione ad un livello più appropriato di astrazione (vedere la risposta di James).

sto immaginando qualcosa di simile:

doStuff(...) { 
    bool gpuSuccess = doGPUStuff(...); 
    if (!gpuSuccess) { 
     doCPUStuff(...); 
    } 
} 
+0

E salta tutto il resto della funzione. – GManNickG

+0

Quale potrebbe essere la cosa giusta da fare, o almeno da refactoring. –

+2

Non penso che 'done:' è attualmente la fine di una funzione - è seguita dal codice per usare la CPU piuttosto che la GPU (che deve aver fallito o essere stata compilata). –

4

Perché non spostare il codice aggiuntivo all'interno del blocco try ?:

#ifdef HAVE_GPU 
      // attempt to use GPU device 
      if (data.device) { 
       try { 
        Integral::Gpu eri(S, R, Q, block.shell()); 
        eri(basis.centers(), quartets, data.device); 
        data.device += size; 
        host_index.extend(block_index); 
        block_index.data.clear(); 
       } 
       // if GPU fails, propagate to cpu 
       catch(std::exception) { /* handle your exception */; } 
      } 
#endif 
+1

Il codice OP ha chiarito che le prime due righe contengono un punto di rottura nominale a cui è consentito lanciare. Il tuo esempio ingoierebbe ogni eccezione. 'Host_index.extend() 'ora può fallire silenziosamente. –

+0

Fondamentalmente il tuo modo di dire, inserire la quantità minima di codice all'interno di un blocco try possibile? –

+0

Se l'ha chiarito, non era nella domanda. Nota il commento "gestisci la tua eccezione" nella cattura. Spetta all'OP scrivere questo codice. –

4

Penso che una variante di questo potrebbe funzionare per voi.

// attempt to use GPU device 
if (data.device) 
{ 
    try 
    { 
     Integral::Gpu eri(S, R, Q, block.shell()); 
     eri(basis.centers(), quartets, data.device); 

     data.device += size; 
     host_index.extend(block_index); 
     block_index.data.clear(); 
    } 
    catch (const std::bad_alloc&) 
    { 
     // this failure was not because 
     // of the GPU, let it propagate 
     throw; 
    } 
    catch(...) 
    { 
     // handle any other exceptions by 
     // knowing it was the GPU and we 
     // can fall back onto the CPU. 
    } 
} 

// do CPU 

Se si potesse modificare la libreria GPU e dare tutte le eccezioni GPU alcuni di base come gpu_exception, il codice diventa molto più semplice:

// attempt to use GPU device 
if (data.device) 
{ 
    try 
    { 
     Integral::Gpu eri(S, R, Q, block.shell()); 
     eri(basis.centers(), quartets, data.device); 

     data.device += size; 
     host_index.extend(block_index); 
     block_index.data.clear(); 
    } 
    catch (const gpu_exception&) 
    { 
     // handle GPU exceptions by 
     // doing nothing and falling 
     // back onto the CPU. 
    } 

    // all other exceptions, not being 
    // GPU caused, may propagate normally 
} 

// do CPU 

Se inferi di questi lavori, penso che la cosa migliore è Steve's answer.

4

Uso leggermente diverso di una bandiera. Penso che sia più ordinato di quello di Amardeep.

Preferisco utilizzare un flag per indicare se propagare l'eccezione, di un flag per indicare se l'ultima cosa che ha funzionato, perché l'intero punto delle eccezioni è quello di evitare i controlli per se l'ultima cosa che ha funzionato - l'idea è di scrivere un codice tale che se siamo arrivati ​​così lontano, tutto ha funzionato e siamo pronti a continuare.

#ifdef HAVE_GPU 
    // attempt to use GPU device 
    if (data.device) { 
     bool dont_catch = false; 
     try { 
      ... 
      dont_catch = true; 
      ... // more things which should not be caught 
     } catch (...) { 
      if (dont_catch) throw; 
     } 
    } 
#endif 
+0

Oh, buona idea. :) – GManNickG

+1

@GMan: modifica apportata - è un comportamento diverso rispetto al codice del questionario, naturalmente, ma salva un argomento rispetto al valore rispetto al riferimento ;-) –

2

mi sto perdendo qualcosa, o non sarebbe equivalente a spostare la parte tra il catch e done: etichetta all'interno del blocco try?

#ifdef HAVE_GPU 
      // attempt to use GPU device 
      if (data.device) { 
       try { 
        Integral::Gpu eri(S, R, Q, block.shell()); 
        eri(basis.centers(), quartets, data.device); 
        data.device += size; 
        host_index.extend(block_index); 
        block_index.data.clear(); 
       } 
       // if GPU fails, propagate to cpu 
       catch(std::exception) {} 
      } 
#endif 
+1

+1: * Se * questo è ciò che l'OP vuole fare, è il soluzione più semplice. –

+0

Sì, ti manca qualcosa. Ora, se qualcosa che era fuori dal tentativo prima di tirare, la sua eccezione viene appena mangiata. – GManNickG

1

Basta rompere fuori nella sua funzione/metodo (tra cui tutto quello che segue) e utilizzare la parola chiave return. Finché tutte le variabili hanno distruttori, sono allocate allo stack (o puntate intelligenti se inevitabili), quindi stai bene. Sono un grande fan delle funzioni/metodi di early-exit piuttosto che il controllo continuo delle flag.

ad esempio:

void myFunc() 
{ 
    String mystr("heya"); 
    try 
    { 
     couldThrow(mystr); 
    } 
    catch(MyException& ex) 
    { 
     return; // String mystr is freed upon returning 
    } 

    // Only execute here if no exceptions above 
    doStuff(); 
} 

In questo modo, è difficile sbagliare

+0

concordato. Mettilo in una funzione e torna quando vuoi, bene, ritorna. – jalf

1

La ragione Goto è di buon occhio oggi è che abbiamo queste cose di fantasia chiamati "funzioni", invece. Avvolgi il codice GPU nella sua funzione, che può tornare presto se non riesce.

Quindi chiamarlo dalla funzione originale.

Dal momento che probabilmente hanno bisogno di condividere alcune variabili (data, host_index e block_index, sembra), metterli in una classe, e rendere i due funzioni membri di esso.

void RunOnGpu(){ 
     // attempt to use GPU device 
     if (data.device) { 
      try { 
       Integral::Gpu eri(S, R, Q, block.shell()); 
       eri(basis.centers(), quartets, data.device); 
      } 
      // if GPU fails, propagate to cpu 
      catch(std::exception) { return; } 
      data.device += size; 
      host_index.extend(block_index); 
      block_index.data.clear();  
} 
void DoStuff() { 
#ifdef HAVE_GPU 
    RunOnGpu(); 
#endif 
} 
Problemi correlati