Esistono casi reali per C ++ senza eccezioni? [chiuso]


40

In Quando utilizzare C su C ++ e C ++ su C? c'è una dichiarazione scritta. per codificare le dimensioni / eccezioni C ++:

Jerry risponde (tra gli altri punti):

(...) tende ad essere più difficile produrre eseguibili veramente minuscoli con C ++. Per sistemi molto piccoli, raramente scrivi molto codice e il extra (...)

a cui ho chiesto perché sarebbe stato, a cui Jerry ha risposto:

la cosa principale è che C ++ include la gestione delle eccezioni, che (almeno di solito) aggiunge un minimo alla dimensione eseguibile. La maggior parte dei compilatori ti permetterà di disabilitare la gestione delle eccezioni, ma quando lo fai il risultato non è più abbastanza C ++. (...)

di cui non ho davvero dubbi a livello tecnico nel mondo reale.


Pertanto sono interessato (puramente per curiosità) a sentire da esempi del mondo reale in cui un progetto ha scelto C ++ come lingua e quindi ha scelto di disabilitare le eccezioni. (Non solo "non utilizzare" le eccezioni nel codice utente, ma disabilitarle nel compilatore, in modo da non poter generare o catturare eccezioni.) Perché un progetto ha scelto di farlo (usando ancora C ++ e non C, ma no eccezioni) - quali sono / sono stati i motivi (tecnici)?


Addendum: per coloro che desiderano elaborare le loro risposte, sarebbe bello dettagliare come vengono gestite le implicazioni delle non eccezioni:

  • Le raccolte STL ( vector, ...) non funzionano correttamente (errore di allocazione non può essere segnalato)
  • new non posso lanciare
  • I costruttori non possono fallire

1
JSF C ++. Getti ed eccezioni non si mescolano.
Coder

1
Alcune informazioni illuminanti sui problemi con le eccezioni (e C ++ in generale) 250bpm.com/blog:4
Ambroz Bizjak,

1
@AmbrozBizjak - Citerò DeadMG da un commento al post a cui ti colleghi: quote: "Mi sembra che tu non capisca le eccezioni." Sono d'accordo. L'autore ha chiaramente incasinato la gestione delle eccezioni, guardando gli esempi che fornisce in quel post.
Martin Ba

Risposte:


35

Quasi tutti i giochi per console sono in C ++ con l'eccezione disattivata, anche oggi. Di fatto è l'impostazione predefinita per i compilatori C ++ destinati a tali console. A volte alcune funzionalità C ++ non sono garantite per funzionare correttamente su quei compilatori, come l'ereditarietà multipla (sto pensando ad un compilatore predefinito console molto noto, ad esempio).


Inoltre, un altro esempio è l' hardware SDK di Arduino che utilizza gcc senza eccezioni attivato in C ++ e altre cose come nessuna STL fornita .


Ci sono alcune ragioni tecniche, buone o cattive, qualunque sia, non è un mio consiglio ma ragioni che ho sentito:

  1. La maggior parte delle console sono sistemi realmente integrati con memoria e tempi di elaborazione limitati. Forse non sarà così vero nelle future console, ma quelle attuali sono ancora piuttosto restrittive rispetto a un PC. Alcune console portatili sono in genere più difficili da programmare rispetto a qualsiasi smartphone, ad esempio l'NDS. La funzione di eccezione aggiunge memoria e un po 'di velocità, anche se non la si utilizza. Puoi controllarti, è vero anche su PC.
  2. I videogiochi su console non possono andare in crash. Devono essere testati in modo da evitare qualsiasi incidente o vicolo cieco, qualsiasi spettacolo. Ecco perché i produttori di console chiedono che i giochi vengano controllati pesantemente prima della pubblicazione. Ciò significa anche che la gestione delle eccezioni aggiunge costi che non sono davvero utili in caso di giochi per console. È meglio nello smartphone, ad esempio perché potrebbero esserci alcuni modi per ripristinare o aggiungere un codice per inviare il problema via e-mail. Le piattaforme chiuse come la maggior parte delle console non lo consentono. Quindi un sistema di eccezione non è davvero necessario dopo tutto. "Devi solo farlo funzionare correttamente". ;)
  3. La gestione delle eccezioni, quando non si accettano errori e arresti anomali, significa che è necessario implementare una strategia di gestione degli errori. Tale sistema potrebbe essere abbastanza complesso da consentire a qualcuno di lavorare molto tempo per renderlo utile. Gli sviluppatori di giochi non hanno il lusso di lavorare su funzionalità ritenute utili in caso di crash ...
  4. Il compilatore non lo consente (ancora). Sì, succede.

Penso che le eccezioni possano essere utili anche nei giochi, ma è vero che sui giochi per console non è davvero utile.


Aggiornare:

Sto aggiungendo un altro esempio sorprendente qui: LLVM / CLang non usa eccezioni né RTTI per i seguenti motivi :

Nel tentativo di ridurre il codice e le dimensioni dell'eseguibile, LLVM non utilizza RTTI (es. Dynamic_cast <>) o eccezioni. Queste due funzionalità del linguaggio violano il principio C ++ generale di "si paga solo per ciò che si utilizza", causando un gonfiamento eseguibile anche se le eccezioni non vengono mai utilizzate nella base di codice o se RTTI non viene mai utilizzato per una classe. Per questo motivo, li disattiviamo globalmente nel codice.

Detto questo, LLVM fa ampio uso di una forma di RTTI lanciata a mano che utilizza modelli come isa <>, cast <> e dyn_cast <>. Questa forma di RTTI è opt-in e può essere aggiunta a qualsiasi classe. È anche sostanzialmente più efficiente di dynamic_cast <>.

CLang è ben noto per la sua velocità di compilazione ed errori espliciti, ma anche per il suo unico raro compilatore che ha un codice davvero facile da seguire.


9
(3): hai bisogno di una strategia di gestione degli errori a prescindere da cosa, se vuoi andare con (2). I problemi sorgeranno e il software della console deve fallire in modo morbido e con grazia. Non è ovvio per me che evitare eccezioni lo renda completamente più facile.
David Thornley,

6
Tutti ottimi esempi di ambienti di runtime strettamente controllati in cui gli errori sono gestiti correttamente e non ci sono eventi "eccezionali".
Patrick Hughes,

1
@DavidThornley Perché supponi che il sistema della console gestirà qualche tipo di errore, ma non lo farà. Beh, forse lo farà su PS3 o XBox, ma non sarà permesso superare i test del costruttore di console. Ad ogni modo, il firmware della maggior parte delle console avanzate non è flessibile come si potrebbe pensare. Un gioco per console deve avere accesso a quasi tutto l'hardware della console, quindi puoi pensare a un gioco in esecuzione su una console quasi come se fosse anche il "sistema operativo" della console ... più abbiamo potenti console, meno è vero. Quindi sono d'accordo con te sul fatto che in alcuni casi sembra strano.
Klaim,

2
Questa è una buona risposta, vorrei anche aggiungere che anche se un'eccezione gorgogliasse e presentasse all'utente un errore, che cosa dovrebbe fare al riguardo con un D-pad? L'unica vera soluzione per l'utente finale è un riavvio.
anon

2
Ho rimosso l'esempio folle perché qualcuno nella loro squadra ha detto di aumentare NO ECCEZIONE non intende significare "nessun uso di eccezione" ma "nessuna eccezione alle regole". Vedi permalink.gmane.org/gmane.comp.lib.boost.devel/231377
Klaim

9

Jerry ha detto: ... il risultato non è più abbastanza C ++ , mentre la mia metafora è che è chiaramente C ++, solo un dialetto leggermente diverso perché i programmi utilizzano altre forme, convenzioni e stili scritti.

Ecco i miei motivi principali per disabilitarli:

Compatibilità binaria

Attraversare i confini della lingua e della traduzione non è universalmente ben definito o indefinito. Se si desidera garantire che il programma funzioni nel dominio del comportamento definito, sarà necessario mettere in quarantena le eccezioni nei punti di uscita del modulo.

Dimensione eseguibile

Ecco le dimensioni binarie di un programma senza eccezioni che ho scritto, creato senza e con le eccezioni abilitate:

Senza eccezioni:

  • eseguibile + dipendenze: 330
  • eseguibile messo a nudo finale (build di rilascio): 37

Con eccezioni:

  • eseguibile + dipendenze: 380
  • eseguibile messo a nudo finale (build di rilascio): 44

Promemoria: è una raccolta di librerie e programmi che contengono zero tiri / catture. La bandiera compilatore fa consentire eccezioni nella libreria standard C ++. Pertanto, il costo nel mondo reale è più del 19% visto in questo esempio.

Compilatore: apple gcc4.2 + llvm. Dimensioni in MB.

Velocità

Nonostante il termine "eccezioni a costo zero", aggiungono comunque un certo sovraccarico anche quando non viene mai lanciato nulla. Nel caso precedente, si tratta di un programma critico per le prestazioni (elaborazione del segnale, generazione, presentazione, conversioni, con set di dati / segnali di grandi dimensioni, ecc.). Le eccezioni non sono una caratteristica necessaria in questo progetto, mentre le prestazioni sono molto importanti.

Correttezza del programma

Sembra una strana ragione ... Se il lancio non è un'opzione, devi scrivere programmi relativamente severi, corretti e ben testati per garantire che il tuo programma venga eseguito correttamente e che i client utilizzino correttamente le interfacce (se mi dai una cattiva discussione o lo fai non controllare un codice di errore, quindi ti meriti UB). Il risultato? La qualità dell'implementazione migliora notevolmente e i problemi vengono risolti rapidamente.

Semplicità

Le implementazioni di gestione delle eccezioni non sono spesso aggiornate. Aggiungono anche molta complessità perché un'implementazione può avere molte molte molte sequenze di uscita. È più semplice leggere e mantenere programmi molto complessi quando usano un piccolo insieme di strategie di uscita ben definite, tipizzate, che si diffondono e sono gestite dal cliente. In altri casi, le implementazioni potrebbero nel tempo implementare più tiri o le loro dipendenze potrebbero introdurli. I clienti non possono difendersi facilmente o appropriatamente da tutte queste uscite. Scrivo e aggiorno molte librerie, ci sono frequenti evoluzioni e miglioramenti. Tentare di mantenere tutto in sincronia con le sequenze di uscita delle eccezioni (in una base di codice di grandi dimensioni) non sarebbe un buon uso del tempo e probabilmente aggiungerebbe molto rumore e cruft. A causa della maggiore correttezza del programma e di più test,

Storia / Codice esistente

In alcuni casi, non sono mai stati introdotti per motivi storici. Una base di codice esistente non li utilizzava, la modifica dei programmi potrebbe richiedere anni-uomo e rendere veramente brutto il mantenimento a causa della sovrapposizione di convenzioni e implementazioni.

Svantaggi

Naturalmente, ci sono degli aspetti negativi, i più grandi sono: incompatibilità (incl. Binario) con altre librerie e il fatto che dovrai implementare una buona quantità di programmi per adattarsi a questo modello.


+1 informazioni eccellenti! (anche se non sono d'accordo con il paragrafo Semplicità)
Martin Ba,

Sarebbe interessante se hai solo costruttori no-fail e come gestisci l'allocazione (- fallimento).
Martin Ba,

@Martin 1) Il disaccordo va abbastanza bene. Comprendo che la maggior parte degli sviluppatori non è d'accordo con la disabilitazione delle eccezioni per molteplici ragioni. Per semplicità, si accompagna alla correttezza del programma. Problemi / stati non validi semplicemente non possono viaggiare lontano. Ciò significa che controllano gli errori e escono con grazia. Il post di jheriko fa eco a questo.
justin

1
@Martin 2b) l'allocazione è un po 'più complessa. Innanzitutto, il numero di allocazioni di heap è ridotto in numero e aumentato in termini di dimensioni - per iniziare, relativamente poche allocazioni di heap. In secondo luogo, le allocazioni passano attraverso allocatori personalizzati. Se inizialmente non viene fornita un'allocazione per l'allocatore (ad es. mallocRitorni 0), l'allocatore inserisce una while (1)che ha un cambio di contesto, seguito da un altro tentativo di allocazione. In questa base di codice, era possibile che il nuovo tramite l'allocatore potesse restituire 0, ma l'implementazione più recente ha funzionato bene. (segue)
justin

2
sì, esiste un wrapper compatibile stl sulle interfacce dell'allocatore personalizzato. tecnicamente, il programma non si interromperà in fase di rilascio; introdurrà switch di contesto (ad es. consentire a un altro thread di funzionare che si spera di liberare un po 'di memoria), riprovare e registrare se fallisce - per sempre. i test unitari possono essere eseguiti per giorni senza problemi. l'unica vera minaccia sono le enormi allocazioni, molte delle quali verranno catturate prima della richiesta. ancora una volta, i tassi di guasto del sistema sono anche sul radar a questo punto; esiste la possibilità che falliranno prima dell'implementazione di base in tale scenario.
justin

7

Google non approva le eccezioni nella loro Guida allo stile C ++ , principalmente per motivi storici:

A prima vista, i vantaggi dell'utilizzo delle eccezioni superano i costi, soprattutto nei nuovi progetti. Tuttavia, per il codice esistente, l'introduzione di eccezioni ha implicazioni su tutto il codice dipendente. Se le eccezioni possono essere propagate oltre un nuovo progetto, diventa anche problematico integrare il nuovo progetto nel codice esistente senza eccezioni. Poiché la maggior parte del codice C ++ esistente su Google non è pronto a gestire le eccezioni, è relativamente difficile adottare un nuovo codice che generi eccezioni.

Dato che il codice esistente di Google non è tollerante alle eccezioni, i costi di utilizzo delle eccezioni sono leggermente superiori ai costi di un nuovo progetto. Il processo di conversione sarebbe lento e soggetto a errori. Non crediamo che le alternative disponibili alle eccezioni, come codici di errore e asserzioni, presentino un onere significativo.

Il nostro consiglio di non utilizzare le eccezioni non si basa su motivi filosofici o morali, ma pratici. Poiché vorremmo utilizzare i nostri progetti open source su Google ed è difficile farlo se tali progetti utilizzano eccezioni, dobbiamo sconsigliare anche le eccezioni nei progetti open source di Google. Le cose sarebbero probabilmente diverse se dovessimo rifare tutto da capo.

C'è un'eccezione a questa regola (nessun gioco di parole previsto) per il codice di Windows.

(Enfasi dell'editor)


5

Qt non usa quasi mai eccezioni. Gli errori in Qt sono indicati con codici e segnali di errore. Il motivo dichiarato ufficialmente è:

All'avvio di Qt non erano disponibili eccezioni per tutti i compilatori che dovevano essere supportati da Qt. Oggi stiamo cercando di mantenere coerenti le API, quindi i moduli che hanno una cronologia di non utilizzo delle eccezioni generalmente non riceveranno nuovo codice utilizzando le eccezioni aggiunte.

Perché QT usa così poche eccezioni?

Oggi, una critica comune a Qt è che la sicurezza delle sue eccezioni non è completa.


1
Grazie. Buone informazioni, anche se mi chiedo se la maggior parte delle basi di codice QT abbia probabilmente le eccezioni abilitate nel compilatore ...
Martin Ba

4

Symbian C ++ (utilizzato su alcuni telefoni cellulari Nokia) non utilizza eccezioni, almeno non direttamente, poiché i compilatori C ++ non li hanno implementati in modo affidabile quando Symbian è stato sviluppato per la prima volta.


1
Questo non vale per le versioni moderne di Symbian. I TRAP e le foglie di Symbian sono implementati come vere eccezioni C ++ , ma EPOC32 e le versioni precedenti di Symbian si basavano su altri mezzi (setjmp / longjmp se ricordo bene).
otto

1
@OttoHarju: Questo è ciò che intendevo per "almeno non direttamente".
Keith Thompson,

3

Io / mai / uso eccezioni. Ci sono una serie di ragioni per questo, ma le due ragioni principali sono che non ho mai avuto bisogno di loro per produrre codice robusto e che riducono le prestazioni in fase di esecuzione.

Ho lavorato su un codice di produzione che utilizza e disabilita le eccezioni: il codice che consentiva le eccezioni era uniformemente peggiore. In alcuni punti sono state utilizzate eccezioni per il controllo del flusso reale anziché per la gestione degli errori, che è molto pesante, anti-prestazioni e difficile da eseguire il debug. In generale, i problemi di debug nel codice compilato con eccezioni erano più difficili che nel codice privo di eccezioni, in parte dovuto allo stack e alle difficoltà intrinseche del meccanismo di eccezione, ma molto più di questo era a causa del codice pigro che veniva incoraggiato come risultato della disponibilità della gestione delle eccezioni.

Non c'è nulla di gravemente sbagliato nelle eccezioni stesse / se non ti preoccupi delle prestazioni / e non hai il tempo di fare qualcosa in modo corretto - sono una funzionalità linguistica per la gestione degli errori e un buon sostituto per un meccanismo di gestione degli errori adeguato. Tuttavia, ci sono quasi sempre / migliori / modi per gestire gli errori - come con la propria logica (è difficile elaborarlo - è quasi un buon senso). Se non è un tuo errore, ma proviene da una libreria (ad esempio la libreria standard), allora devi rispettare la scelta delle eccezioni o avere qualche crash, ma metterei sempre in dubbio quella scelta. Non ho mai visto una situazione in cui le eccezioni fossero in realtà la soluzione migliore.

Le asserzioni sono più debuggabili, i codici di errore sono meno pesanti ... tra i due, se usati correttamente, si diventa più facili da leggere, eseguire il debug e mantenere il codice che è più veloce. Vince a tutto tondo ...


1
No. È più facile abusare delle eccezioni, ma a parte ciò le eccezioni funzionano bene. È altrettanto facile ragionare, a condizione che tutte le tue funzioni siano eccezionalmente sicure (e di solito si tratta solo di usare RTTI per tutte le risorse, cosa che dovresti comunque fare). Fare correttamente i codici di errore può essere estremamente noioso e può comportare l'interruzione del programma in una pila di codici di gestione degli errori; in tal caso, le eccezioni sono migliori. Ricorda, il fatto che io faccia diversamente da te non è una prova conclusiva del fatto che non mi interessi fare le cose nel modo giusto.
David Thornley,

... per quanto riguarda il motivo per cui RTTI è un male che è tutta un'altra storia - l'RTTI integrato in ogni compilatore che ho visto è banalmente battibile - se hai persino bisogno di RTTI. Tutto dipende dal contesto - queste cose come RTTI ed eccezioni possono essere grandiose per le app RAD e aziendali - non hanno posto nel mondo dell'informatica ad alte prestazioni (ad es. Giochi). Non ho mai visto un motore di gioco di produzione che utilizzava nessuno dei due bersagli in cui potevano essere spenti ...
jheriko

2
Scusate, RAII è ciò che intendevo. Se non lo stai usando, non solo le eccezioni verranno incasinate. (Tuttavia, i cast dinamici funzionano molto bene nel mio lavoro e devo preoccuparmi delle prestazioni.)
David Thornley,

Argomento interessante: penso che le eccezioni consentano un errore più conciso nella gestione della codifica, ma i codici di errore sono più robusti perché consentono di gestire gli errori localmente (è possibile controllare immediatamente il codice di ritorno nel chiamante mentre l'eccezione può far scoppiare lo stack di chiamate prima che vengano rilevate e a volte non vengono catturati, provocando un arresto. A meno che non si usi catch (...)).
Giorgio,

3

Nello standard di codifica C ++ di Joint Strike Fighter di Bjarne et. al., le eccezioni sono vietate, a causa delle difficili esigenze in tempo reale dei caccia.

JSF ++ è per applicazioni in tempo reale e critiche per la sicurezza (software di controllo di volo). Se un calcolo impiega troppo tempo qualcuno potrebbe morire. Per questo motivo, dobbiamo garantire i tempi di risposta e non possiamo, con l'attuale livello di supporto degli strumenti, fare eccezioni. In quel contesto, anche l'allocazione gratuita del negozio è vietata! In realtà, le raccomandazioni di JSF ++ per la gestione degli errori simulano l'uso delle eccezioni in previsione del giorno in cui abbiamo gli strumenti per fare le cose nel modo giusto, cioè usando le eccezioni.

Citato dalle FAQ C ++ di Bjarne .

Ricorda, C ++ probabilmente esegue la più ampia varietà di software di tutte le lingue ...


JSF ++ vieta anche l'uso di "nuovo" (oltre al posizionamento nuovo) e "elimina". Qual è una risposta alla domanda "come gestire i nuovi fallimenti senza eccezioni" ...
armb,

@armb: Sì. Vedi "In quel contesto, anche l'allocazione gratuita del negozio è vietata!" sopra.
Macke,

2

È abbastanza importante nella progettazione di librerie C ++. Spesso con un'interfaccia C è un po 'brutto gettare un'eccezione dalla tua libreria di terze parti al client. Fai notare che se la tua biblioteca lancia, stai tagliando fuori un insieme di clienti che l'avrebbero usata, dato che aveva una garanzia no-throw (per qualsiasi motivo che il cliente ha per una restrizione sulle eccezioni).

Personalmente ho visto abusare delle eccezioni quando viene detto alla squadra di "lanciare un'eccezione" ogni volta che succede qualcosa di non terribile. Ovviamente vedi l'errore qui: l'eccezione è stata lanciata nel codice prima che qualcuno capisse cosa farsene. Quel progetto ha avuto alcuni arresti anomali in quanto questi lanci profondi si sollevavano di tanto in tanto.


1

Forse a questo proposito, vale la pena menzionare Embedded C ++ . Il C ++ incorporato è una variante del C ++ progettata (ovviamente abbastanza) per i sistemi embedded. È fondamentalmente un sottoinsieme corretto di C ++, con (tra le altre cose) modelli, spazi dei nomi e gestione delle eccezioni rimossi.

Dovrei aggiungere che mentre EC ++ ha fatto un po 'di schizzi quando era nuovo, sembra che per lo più siano andati abbastanza tranquilli. Non sono sicuro che le persone abbiano perso interesse o che il loro primo tentativo sia stato semplicemente così perfetto che nessuno ha visto alcun motivo per rovinarlo per circa un decennio.<closed captioning for the humor impaired>Yeah, right!</closed captioning>


Guardando le domande e risposte - caravan.net/ec2plus/question.html - direi che è praticamente morto.
Martin Ba,

1

Un buon esempio è la programmazione in modalità kernel.

Puoi usare C ++ lì per un codice più pulito ma senza eccezioni. Non esiste una libreria di runtime per loro e la gestione delle eccezioni usa troppa memoria dello stack che è molto limitata nel kernel (ho sperimentato eccezioni C ++ nel kernel di Windows NT e il lancio e lo svolgimento delle eccezioni consumano metà dello stack disponibile - molto facile ottenere lo overflow dello stack e va in crash l'intero sistema).

In genere si definiscono il proprio newe gli deleteoperatori. Inoltre ho trovato utili placement newriferimenti avalori ae c ++ 11. Non è possibile utilizzare STL o altre librerie in modalità utente C ++ che si basano su eccezioni.


0

Quando si tenta di mantenere la memoria eseguibile inattiva. Generalmente eseguito su sistemi incorporati (o mobili). Per le app desktop non è mai necessario, tuttavia sui server potrebbe essere considerato se si desidera più RAM possibile per il server Web o per sql.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.