Un programma C ++ dovrebbe catturare tutte le eccezioni e impedire che le eccezioni si ribaltino su main ()?

28

Una volta sono stato avvisato che un programma C ++ dovrebbe alla fine catturare tutte le eccezioni. Il ragionamento fornito all'epoca era essenzialmente che i programmi che consentono alle eccezioni di espandersi al di fuori di main() entrano in uno strano stato zombie. Mi è stato detto questo diversi anni fa e, a posteriori, credo che il fenomeno osservato sia dovuto alla lunga generazione di dump core eccezionalmente grandi dal progetto in questione.

All'epoca questo sembrava strano ma convincente. Era del tutto assurdo che C ++ dovesse "punire" i programmatori per non aver colto tutte le eccezioni, ma le prove precedenti mi sembravano confermare. Per il progetto in questione, i programmi che lanciavano eccezioni non rilevate sembravano entrare in uno strano stato di zombi - o come sospetto che la causa fosse ora, un processo nel mezzo di un deposito core indesiderato è insolitamente difficile da fermare.

(Per chi si chiede perché questo non era più ovvio in quel momento: il progetto ha generato una grande quantità di output in più file da più processi che oscuravano effettivamente qualsiasi tipo di messaggio aborted (core dumped) e in questo caso particolare, post- L'esame mortem delle core dump non era un'importante tecnica di debug, quindi non ci si era mai preoccupati delle discariche di base: i problemi con un programma di solito non dipendevano dallo stato accumulato da molti eventi nel tempo da un programma longevo, ma dagli input iniziali per un programma di breve durata (< 1 ora) quindi era più pratico eseguire nuovamente un programma con gli stessi input da una build di debug o in un debugger per ottenere maggiori informazioni.)

Al momento, non sono sicuro che esista un vantaggio o uno svantaggio di cattura delle eccezioni solo al fine di impedire che le eccezioni lascino main() .

Il piccolo vantaggio a cui posso pensare di consentire alle eccezioni di risalire oltre main() è che fa sì che il risultato di std::exception::what() venga stampato sul terminale (almeno con i programmi compilati con gcc su Linux). D'altra parte, questo è banale da raggiungere catturando invece tutte le eccezioni derivate da std::exception e stampando il risultato di std::exception::what() e se è opportuno stampare un messaggio da un'eccezione che non deriva da std::exception allora deve essere catturato prima di lasciare main() per stampare il messaggio.

Il modesto svantaggio che posso pensare per consentire alle eccezioni di risalire oltre main() è che possono essere generati dump core indesiderati. Per un processo che utilizza una grande quantità di memoria questo può essere piuttosto fastidioso e il controllo del comportamento di dumping di base da un programma richiede chiamate di funzioni specifiche del sistema operativo. D'altra parte, se si desidera un core dump e exit, questo potrebbe essere ottenuto in qualsiasi momento chiamando std::abort() e un'uscita senza core dump può essere raggiunta in qualsiasi momento chiamando std::exit() .

Aneddoticamente, non penso di aver mai visto il messaggio predefinito what(): ... stampato da un programma ampiamente distribuito al momento del crash.

Quali sono gli argomenti forti a favore o contro che consentono alle eccezioni del C ++ di risalire oltre main() ?

Modifica: ci sono molte domande generali sulla gestione delle eccezioni su questo sito. La mia domanda riguarda specificamente le eccezioni C ++ che non possono essere gestite e sono arrivate fino a main() - forse è possibile stampare un messaggio di errore ma è un errore di interruzione immediato.

    
posta Praxeolitic 30.01.2016 - 09:59
fonte

7 risposte

24

Un problema nel lasciare che le eccezioni vadano oltre il main è che il programma terminerà con una chiamata a std::terminate che il comportamento predefinito è chiamare std::abort . È solo implementazione definita se lo sbobinamento dello stack viene eseguito prima di chiamare terminate in modo che il programma possa terminare senza chiamare un singolo distruttore! Se hai qualche risorsa che ha davvero bisogno di essere ripristinata da una chiamata distruttore, sei in un sottaceto ...

    
risposta data 30.01.2016 - 14:27
fonte
27

Il motivo principale per cui non si lascia eccezioni da main è perché altrimenti si perde ogni possibilità di controllare come viene segnalato il problema ai propri utenti.

Per un programma che non è destinato a essere utilizzato da molto tempo o distribuito ampiamente, può essere accettabile che gli errori imprevisti vengano segnalati in qualunque modo il sistema operativo decida di farlo (ad esempio, mostrando un errore in-faccia finestra di dialogo su Windows).

Per i programmi che vendi, o che sono forniti al pubblico da un'organizzazione che ha una reputazione da difendere, è generalmente una buona idea segnalare in modo positivo che hai riscontrato un problema inaspettato e provare a salvare come gran parte dei dati dell'utente possibile. Non perdere il lavoro di mezza giornata del tuo utente e non si blocca inaspettatamente, ma chiuderlo in modo semi-garbato è generalmente molto meglio per la tua reputazione aziendale rispetto a quella alternativa.

    
risposta data 30.01.2016 - 10:48
fonte
11

TL; DR : Che cosa dicono le specifiche?

Una deviazione tecnica ...

Quando viene lanciata un'eccezione e nessun gestore è pronto per questo:

  • è l'implementazione definita se lo stack è svolto o meno
  • std::terminate è chiamato, che per default si interrompe
  • a seconda della configurazione dell'ambiente, l'interruzione può lasciare o non lasciare un rapporto di arresto anomalo dietro

Quest'ultimo può essere utile per bug molto infrequenti (perché la loro riproduzione è un processo che fa perdere tempo).

Il fatto di cogliere tutte le eccezioni o meno è, in definitiva, una questione di specifiche:

  • è specificato come segnalare gli errori degli utenti? (valore non valido, ...)
  • è specificato come segnalare errori funzionali? (directory di destinazione mancante, ...)
  • è specificato come segnalare errori tecnici? (assertion firing up, ...)

Per ogni programma di produzione, questo dovrebbe essere specificato, e dovresti seguire le specifiche (e forse sostenere che sia cambiato).

Per lanciare rapidamente programmi che possono essere usati solo da tecnici (voi, i vostri compagni di squadra), va bene. Ti consiglio di lasciarlo in crash e configurare l'ambiente per ottenere un rapporto o meno in base alle tue esigenze.

    
risposta data 30.01.2016 - 14:40
fonte
4

Un'eccezione che si cattura ti dà l'opportunità di stampare un bel messaggio di errore o anche di provare a recuperare l'errore (probabilmente solo rilanciando l'applicazione).

Tuttavia, in C ++ un'eccezione non contiene informazioni sullo stato del programma quando è stato lanciato. Se lo prendi, tutto questo stato è dimenticato, mentre se lascia il programma in crash, lo stato di solito è ancora lì e può essere letto dal dump del programma, rendendo più facile il debug.

Quindi è un trade-off.

    
risposta data 30.01.2016 - 12:40
fonte
4

Nel momento in cui sai che devi interrompere, vai avanti e chiama std::terminate già per ridurre qualsiasi ulteriore danno.

Se sai che puoi rilassarti in sicurezza, fallo invece. Ricorda che lo stack-unwinding non è garantito quando un'eccezione non viene mai catturata, quindi prendi e rilancia.

Se è possibile segnalare / registrare l'errore in modo sicuro meglio che il sistema lo farà da solo, andare avanti.
Ma sii davvero sicuro di non inavvertitamente peggiorare le cose mentre lo fai.

Spesso è troppo tardi per salvare i dati quando si rileva un errore irrecuperabile, anche se dipende dall'errore specifico.
Ad ogni modo, se il tuo programma è stato scritto per un rapido recupero, ucciderlo potrebbe essere il modo migliore per terminarlo anche se è solo un normale arresto.

    
risposta data 30.01.2016 - 14:22
fonte
1

Schiantarsi con grazia è una buona cosa per la maggior parte del tempo - ma ci sono dei compromessi. A volte è una cosa buona in crash. Dovrei menzionare che sto principalmente pensando al debugging nel molto grande. Per un programma semplice - anche se questo potrebbe essere ancora utile, non è neanche lontanamente utile quanto lo è con un programma molto complesso (o con diversi programmi complessi che interagiscono).

Non vuoi andare in crash in pubblico (anche se questo è davvero inevitabile - i programmi si bloccano, e un programma non-crash veramente matematicamente verificabile non è quello di cui stiamo parlando qui). Pensa a Bill Gates BSODing nel bel mezzo di una demo - male, giusto? Tuttavia, possiamo provare a capire perché siamo andati in crash e non ci siamo più fermati allo stesso modo.

Ho attivato una funzionalità di Segnalazione errori di Windows che crea arresti anomali locali in caso di eccezioni non gestite. Funziona a meraviglia se hai i file dei simboli associati alla tua build (in crash). È interessante notare che, poiché ho creato questo strumento, voglio bloccare altro , perché apprendo da ogni crash. Quindi posso correggere gli errori e fare meno crash.

Quindi per ora voglio che i miei programmi arrivino fino in cima e cadano in crash. In futuro, potrei voler mangiare con garbo tutte le mie eccezioni, ma non se posso farle funzionare per me.

Puoi leggere ulteriori informazioni su Local Crash Dumps qui se sei interessato: Raccolta dei dump della modalità utente

    
risposta data 05.02.2016 - 01:07
fonte
-6

In definitiva, se un'eccezione compare oltre a main (), causerà il crash dell'applicazione e nella mia mente un'app non dovrebbe mai arrestarsi. Se è corretto bloccare un'applicazione in un punto, perché non dovunque? Perché preoccuparsi della gestione delle eccezioni? (Sarcasmo, questo non lo sta davvero suggerendo ...)

Potresti avere un try / catch globale che stampa un messaggio elegante che informa l'utente che si è verificato un errore irreversibile e che è necessario inviare un'email a IT in esso o qualcosa di simile, ma l'arresto anomalo è completamente non professionale e inaccettabile. È banale da prevenire e puoi informare un utente su cosa è appena successo e su come sistemare le cose in modo che non accada di nuovo.

L'arresto è rigorosamente all'ora amatoriale.

    
risposta data 30.01.2016 - 11:11
fonte

Leggi altre domande sui tag