C ++ intercepte toutes les exceptions

244

Existe-t-il un équivalent c ++ de Java

try {
    ...
}
catch (Throwable t) {
    ...
}

J'essaie de déboguer du code Java / jni qui appelle des fonctions Windows natives et la machine virtuelle continue de planter. Le code natif apparaît bien dans les tests unitaires et ne semble planter que lorsqu'il est appelé via jni. Un mécanisme générique de capture d'exceptions s'avérerait extrêmement utile.

Obediah Stane
la source
2
Notez que la plupart des plantages ne sont pas causés par des exceptions en C ++. Vous pouvez intercepter toutes les exceptions, mais cela n'empêchera pas de nombreux plantages.
Mooing Duck

Réponses:

335
try{
    // ...
} catch (...) {
    // ...
}

va intercepter toutes les exceptions C ++, mais il doit être considéré comme une mauvaise conception. Vous pouvez utiliser le nouveau mécanisme current_exception de c ++ 11, mais si vous n'avez pas la possibilité d'utiliser c ++ 11 (systèmes de code hérités nécessitant une réécriture), vous n'avez aucun pointeur d'exception nommé à utiliser pour obtenir un message ou un nom. . Vous souhaiterez peut-être ajouter des clauses catch distinctes pour les diverses exceptions que vous pouvez intercepter, et intercepter uniquement tout en bas pour enregistrer une exception inattendue. Par exemple:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}
Greg D
la source
68
Il est recommandé d'attraper les exceptions par référence const. Comme dans: catch (std :: exception const & ex) {/ * ... * /}
coryan
12
@coryan: Pourquoi est-ce une bonne pratique de capturer par référence const?
Tim MB
19
Éviter les copies inutiles est un avantage.
Greg D
21
-1: la suggestion que cela "interceptera toutes les exceptions en C ++" est trompeuse. Essayez de générer une erreur de division par zéro à l'intérieur du bloc try. Vous verrez qu'il générera une exception qui n'est pas interceptée, mais le code est clairement en C ++. Il serait plus utile de déclarer que cela "interceptera toutes les exceptions C ++", puis d'ajouter une mention d'exceptions structurées aux notes sur l'utilité limitée.
omatai
42
@omatai: corrigé, il interceptera toutes les exceptions C ++. La division par zéro est un comportement non défini et ne génère pas d'exception C ++.
Mooing Duck
151

Quelqu'un devrait ajouter que l'on ne peut pas intercepter les "plantages" dans le code C ++. Ceux-ci ne lèvent pas d'exceptions, mais font tout ce qu'ils veulent. Lorsque vous voyez un programme se bloquer en raison, par exemple, d'une déréférence de pointeur nul, il fait un comportement non défini. Il n'y en a pas std::null_pointer_exception. Essayer d'attraper des exceptions n'y aidera pas.

Juste pour le cas où quelqu'un lit ce fil et pense qu'il peut obtenir la cause des plantages du programme. Un débogueur comme gdb devrait être utilisé à la place.

Johannes Schaub - litb
la source
4
Eh bien, comme le souligne Shy, c'est possible avec le compilateur VC. Ce n'est pas une bonne idée, mais c'est possible.
Shog9
7
ouais avec SEH. mais pas avec des techniques c ++ standardes saines :) bien si vous vous en tenez aux fenêtres, vous pouvez presque tout faire :)
Johannes Schaub - litb
1
Mmm ... merci pour cette friandise. J'ai cherché la réponse pour savoir pourquoi mes exceptions de pointeur nul ne sont pas prises!
Dalin Seivewright
10
Vous pouvez intercepter les erreurs de segmentation avec SEH sur Windows et signal (2) / sigaction (2) sur les systèmes POSIX, qui couvre la grande majorité des systèmes utilisés aujourd'hui, mais comme la gestion des exceptions, ce n'est pas quelque chose qui devrait être utilisé pour le contrôle de flux normal. Il s'agit plutôt de «faire quelque chose d'utile avant de mourir».
Adam Rosenfield
1
@AdamRosenfield jusqu'à ce que vous ayez implémenté la try { .. } catch(...) { ... }capture en utilisant signal / sigaction, je ne l'appellerais pas "capture" :) Si dans un gestionnaire de signal, il est relativement difficile pour le programmeur de savoir où dans le code le crash s'est produit (je parle à propos de la détection par programme), par rapport à try / catch.
Johannes Schaub - litb
72

Voici comment vous pouvez effectuer une rétro-ingénierie du type d'exception de l'intérieur catch(...)si vous en avez besoin (peut être utile lors de la capture d'un inconnu dans une bibliothèque tierce) avec GCC:

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

et si vous pouvez vous permettre d'utiliser Boost, vous pouvez rendre votre section de capture encore plus simple (à l'extérieur) et potentiellement multiplateforme

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}
bobah
la source
58
try {
   // ...
} catch (...) {
   // ...
}

Notez que l' ...intérieur du catchest une vraie ellipse, c'est-à-dire. trois points.

Cependant, comme les exceptions C ++ ne sont pas nécessairement des sous-classes d'une Exceptionclasse de base , il n'existe aucun moyen de voir réellement la variable d'exception levée lors de l'utilisation de cette construction.

Greg Hewgill
la source
24
En C ++ 11, il y a: try {std :: string (). At (1); // cela génère un std :: out_of_range} catch (...) {eptr = std :: current_exception (); // capture}
Mohammad Alaggan
2
@bfontaine: Eh bien oui, mais je l'ai dit pour distinguer le catchspécificateur de l'espace réservé de code existant dans un commentaire ( // ...) qui n'est évidemment pas la syntaxe C ++.
Greg Hewgill
1
@GregHewgill: oui, c'était juste une sélection typographique.
bfontaine
1
@bfontaine: Assez juste. :)
Greg Hewgill
44

il n'est pas possible (en C ++) d'intercepter toutes les exceptions de manière portable. En effet, certaines exceptions ne sont pas des exceptions dans un contexte C ++. Cela inclut des choses comme la division par zéro erreur et autres. Il est possible de pirater et d'obtenir ainsi la possibilité de lever des exceptions lorsque ces erreurs se produisent, mais ce n'est pas facile à faire et certainement pas facile à corriger de manière portable.

Si vous voulez intercepter toutes les exceptions STL, vous pouvez le faire

try { ... } catch( const std::exception &e) { ... }

Ce qui vous permettra d'utiliser e.what(), qui renverra un const char*, qui peut vous en dire plus sur l'exception elle-même. C'est la construction qui ressemble le plus à la construction Java, vous avez demandé le plus.

Cela ne vous aidera pas si quelqu'un est assez stupide pour lancer une exception qui n'hérite pas std::exception.

Plus clair
la source
2
pourquoi n'est-ce pas en haut?
Ivan Sanz-Carasa
31

En bref, utilisez catch(...). Cependant, notez que cela catch(...)est destiné à être utilisé conjointement avec throw;essentiellement:

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

C'est la bonne façon de l'utiliser catch(...).

Mellester
la source
6
il vaut mieux utiliser RAII pour la gestion de la mémoire qui gère automatiquement ces situations d'exception.
paykoob
1
@paykoob Comment cela gère-t-il les cas où vous avez réussi à créer un nouveau foo mais qui a échoué sur une barre. Ou lorsque le constructeur de la barre essaie d'ouvrir un fichier mais échoue et lance donc. alors vous pourriez vous retrouver avec un foo
balançant
2
@MelleSterk La pile ne serait-elle pas toujours nettoyée dans ce cas, ce qui exécuterait Foole destructeur? Je pensais que c'était tout l'intérêt de RAII. Cependant, si vous avez besoin d'un pointeur sur un Fooplutôt que de simplement le créer Foosur la pile, vous devrez envelopper le pointeur dans quelque chose d'autre qui est déclaré sur la pile.
reirab
oui auto foo = std :: make_unique <Foo> (); barre automatique = std :: make_unique <Bar> (); // est d'une sécurité exceptionnelle et ne
fuira
Cette réponse mérite un vote, ne serait-ce que pour la discussion qu'elle a commencée :)
Cristik
21

il est possible de le faire en écrivant:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

Mais il y a un risque très peu visible ici: vous ne pouvez pas trouver le type d'erreur exact qui a été jeté dans le trybloc, alors utilisez ce genre de catchlorsque vous êtes sûr que quel que soit le type d'exception, le programme doit persister de la manière définie dans le catchbloc.

Infintyyy
la source
31
J'espère que vous avez obtenu une sorte de badge pour répondre à une question près de 5 ans après qu'une réponse supérieure ait été fournie!
4
@DrEval Comme vous le souhaitez;) stackoverflow.com/help/badges/17/necromancer?userid=2580505
Andreas
18

Vous pouvez utiliser

catch(...)

mais c'est très dangereux. Dans son livre Debugging Windows , John Robbins raconte une histoire de guerre à propos d'un bug vraiment méchant qui était masqué par une commande catch (...). Il vaut mieux attraper des exceptions spécifiques. Attrapez tout ce que vous pensez que votre bloc try peut raisonnablement lever, mais laissez le code lever une exception plus haut si quelque chose de vraiment inattendu se produit.

John D. Cook
la source
1
J'ai juste attrapé quelques utilisations de ceux-ci et j'ai poivré dans certains journaux à ce stade. Ne rien faire, sauf une exception, pose certainement des problèmes.
jxramos
15

Permettez-moi de mentionner ceci ici: le Java

try 
{
...
}
catch (Exception e)
{
...
}

peut ne PAS attraper toutes les exceptions! J'ai déjà vu ce genre de choses se produire auparavant, et cela provoque de l'insante; L'exception dérive de Throwable. Donc, littéralement, pour tout attraper, vous ne voulez PAS attraper d'exceptions; vous voulez attraper Throwable.

Je sais que cela semble compliqué, mais lorsque vous avez passé plusieurs jours à essayer de comprendre d'où provenait "l'exception non capturée" dans le code qui était entouré d'un bloc try ... catch (Exception e) " tu.

Paul Sonier
la source
2
Bien sûr, vous ne devez jamais intercepter des objets Error - si vous étiez censé les intercepter, ce seraient des exceptions. Les objets d'erreur sont des choses complètement fatales, comme manquer d'espace de tas, etc.
SCdF
1
Ni les exceptions d'exécution qui sont la plupart du temps des exceptions GoodProgrammerExpected !!!
OscarRyz
3
Nous avons eu un bug très sérieux causé par la capture d'un OutOfMemoryError en raison d'un bloc catch (Throwable) au lieu de le laisser tuer des choses ...
Trejkaz
1
Bien sûr, il se catch(Exception)peut que toutes les exceptions ne soient pas détectées en Java, vous le mélangez avec C # ... Java = catch(Thowable), C # = catch(Exception). Ne les confondez pas.
Chef Pharaon
2
@OscarRyz Cela ressemble à la CoderMalfunctionError(qui est en fait une vraie Errorsous-classe Java ... bien que cela ne signifie pas à quoi elle ressemble.)
reirab
9

Eh bien, si vous souhaitez intercepter toutes les exceptions pour créer un minidump par exemple ...

Quelqu'un a fait le travail sur Windows.

Voir http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus Dans l'article, il explique comment il a découvert comment intercepter toutes sortes d'exceptions et il fournit du code qui fonctionne.

Voici la liste que vous pouvez attraper:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

Et l'utilisation: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // faire cela pour un thread ch.SetThreadExceptionHandlers (); // pour chaque lancer


Par défaut, cela crée un minidump dans le répertoire courant (crashdump.dmp)

Réplique
la source
4

Un mécanisme générique de capture d'exceptions s'avérerait extrêmement utile.

Douteux. Vous savez déjà que votre code est cassé, car il plante. Manger des exceptions peut masquer cela, mais cela entraînera probablement des bogues encore plus méchants et plus subtils.

Ce que vous voulez vraiment, c'est un débogueur ...

Shog9
la source
13
Je ne suis pas d'accord, il y a beaucoup de cas dans les applications en temps réel où je préfère intercepter une exception inconnue, écrire quoi que ce soit dans un journal / poursuivre une action générique d'erreur, plutôt que de laisser l'application se bloquer.
f0ster
3
Je soupçonne plutôt que vous pensez à des cas où vous pouvez poursuivre une action générique contre les erreurs, en ignorant commodément ceux où la pile est mise à la poubelle ou la mémoire est épuisée et la gestion des erreurs générique ne réussira pas non plus. Rien de mal avec les erreurs de capture que vous pouvez récupérer, mais à mon humble avis, un fourre-tout ne devrait vraiment exister que sous forme isolée (pile séparée, mémoire pré-allouée), une logique soigneusement écrite appelée juste avant la fin du programme; si vous ne savez pas quel est le problème, vous ne pouvez pas être sûr qu'il peut être récupéré.
Shog9
1
C'est-à-dire installer un gestionnaire de signaux qui déroule certains journaux que vous créez pendant l'exécution pour comprendre où le programme s'est écrasé et, espérons-le, pourquoi.
Clair
3
  1. Pouvez-vous exécuter votre application Java utilisant JNI à partir d'une fenêtre de console (lancez-la à partir d'une ligne de commande java) pour voir s'il existe un rapport de ce qui a pu être détecté avant que la JVM ne plante. Lors de l'exécution directe en tant qu'application de fenêtre Java, il se peut que des messages n'apparaissent pas si vous exécutez à partir d'une fenêtre de console à la place.

  2. Deuxièmement, pouvez-vous bloquer votre implémentation de DLL JNI pour montrer que les méthodes de votre DLL sont entrées à partir de JNI, que vous revenez correctement, etc.?

  3. Juste au cas où le problème proviendrait d'une utilisation incorrecte de l'une des méthodes d'interface JNI du code C ++, avez-vous vérifié que quelques exemples JNI simples se compilent et fonctionnent avec votre configuration? Je pense en particulier à utiliser les méthodes de l'interface JNI pour convertir les paramètres au format C ++ natif et transformer les résultats des fonctions en types Java. Il est utile de les bloquer pour vous assurer que les conversions de données fonctionnent et que vous ne vous trompez pas dans les appels de type COM dans l'interface JNI.

  4. Il y a d'autres choses à vérifier, mais il est difficile d'en suggérer sans en savoir plus sur vos méthodes Java natives et ce que leur implémentation JNI essaie de faire. Il n'est pas clair que la capture d'une exception au niveau du code C ++ est liée à votre problème. (Vous pouvez utiliser l'interface JNI pour renvoyer l'exception comme Java, mais il n'est pas clair d'après ce que vous fournissez que cela va aider.)

orcmid
la source
2

Pour le vrai problème lié à l'impossibilité de déboguer correctement un programme qui utilise JNI (ou le bogue n'apparaît pas lors de son exécution sous un débogueur):

Dans ce cas, il est souvent utile d'ajouter des wrappers Java autour de vos appels JNI (c'est-à-dire que toutes les méthodes natives sont privées et que vos méthodes publiques de la classe les appellent) qui effectuent une vérification de base de la justesse (vérifiez que tous les "objets" sont libérés et les "objets" ne sont pas utilisés après la libération) ou la synchronisation (il suffit de synchroniser toutes les méthodes d'une DLL vers une seule instance d'objet). Laissez les méthodes du wrapper java enregistrer l'erreur et lever une exception.

Cela aidera souvent à trouver la vraie erreur (qui est étonnamment la plupart du temps dans le code Java qui n'obéit pas à la sémantique des fonctions appelées provoquant des doubles libérations désagréables ou similaires) plus facilement que d'essayer de déboguer un programme Java massivement parallèle dans un débogueur natif ...

Si vous connaissez la cause, conservez le code dans vos méthodes d'encapsuleur qui l'évite. Mieux vaut que vos méthodes wrapper lèvent des exceptions que votre code JNI plante la VM ...

mihi
la source
1

Eh bien, cela dépend vraiment de l'environnement du compilateur. gcc ne les attrape pas. Visual Studio et le dernier Borland que j'ai utilisé l'ont fait.

La conclusion à propos des plantages est donc que cela dépend de la qualité de votre environnement de développement.

La spécification C ++ indique que catch (...) doit intercepter toutes les exceptions, mais ce n'est pas le cas dans tous les cas.

Du moins d'après ce que j'ai essayé.

Jan
la source
1

Être conscient

try{
// ...
} catch (...) {
// ...
}

n'attrape que les exceptions au niveau de la langue, d'autres exceptions / erreurs de bas niveau comme Access Violationet Segmentation Faultne seront pas interceptées.

muaz
la source
Des choses comme la faute de segmentation ne sont pas réellement des exceptions, ce sont des signaux; ainsi, vous ne pouvez pas les attraper comme des exceptions typiques. Cependant, il existe des solutions de contournement comme celle-ci .
MAChitgarha