web-dev-qa-db-fra.com

Détermination du type d'exception après la capture de l'exception?

Existe-t-il un moyen de déterminer le type d'exception même si vous savez que vous avez capturé l'exception avec un catch all?

Exemple:

try
{
   SomeBigFunction();
}
catch(...)
{
   //Determine exception type here
}
34
Brian R. Bondy

Vous pouvez effectivement déterminer le type dans une capture (...), mais ce n'est pas très utile:

#include <iostream>
#include <exception>

    class E1 : public std::exception {};
    class E2 : public std::exception {};

    int main() {
        try {
            throw E2();
        }
        catch( ... ) {
            try {
                throw;
            }
            catch( const E1 & e ) {
                std::cout << "E1\n";
            }
            catch( const E2 & e ) {
                std::cout << "E2\n";
            }
        }
    }
23
anon

Réponse courte: Non.

Longue réponse:

Si vous dérivez toutes vos exceptions à partir d'un type de base commun (par exemple, std :: exception) et que vous interceptez cela explicitement, vous pouvez l'utiliser pour obtenir des informations de type à partir de votre exception.

Mais vous devriez utiliser la fonctionnalité catch pour capturer comme type d’exception spécifique, puis travailler à partir de là.

La seule utilisation réelle de catch (...) est:

  • Catch: et jeter une exception (stop exception destructor echappant).
  • Capture: enregistre une exception inconnue et la relance.

Modifié: Vous pouvez extraire les informations de type via dynamic_cast <> () ou via typid () Bien que, comme indiqué ci-dessus, ce n’est pas quelque chose que je recommande. Utilisez les déclarations de cas.

#include <stdexcept>
#include <iostream>

class X: public std::runtime_error  // I use runtime_error a lot
{                                   // its derived from std::exception
    public:                         // And has an implementation of what()
        X(std::string const& msg):
            runtime_error(msg)
        {}
};

int main()
{
    try
    {
        throw X("Test");
    }
    catch(std::exception const& e)
    {
        std::cout << "Message: " << e.what() << "\n";

        /*
         * Note this is platform/compiler specific
         * Your milage may very
         */
        std::cout << "Type:    " << typeid(e).name() << "\n";
    }
}
28
Martin York

Il n'y a pas de méthode standard et portable pour le faire. Voici un moyen non-portable de le faire sur GCC et Clang

#include <iostream>
#include <cxxabi.h>

const char* currentExceptionTypeName()
{
    int status;
    return abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &status);
}

int main()
{
    try {
        throw std::string();
    } catch (...) {
        std::cout<<"Type of caught exception is "<<currentExceptionTypeName()<<std::endl;
    }

    return 0;
}

Sortie:

Type of caught exception is std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
7
user697683

Si vous devez gérer les exceptions différemment en fonction de ce qu'elles sont, vous devriez intercepter des exceptions spécifiques. S'il existe des groupes d'exceptions qui doivent tous être gérés de manière identique, il est préférable de les dériver d'une classe de base commune et de capturer la classe de base. Tirez parti du pouvoir et des paradigmes de la langue, ne vous battez pas contre eux!

6
Harper Shelby

Non. 

Cela exigerait à tout le moins que vous puissiez accéder à l'exception actuelle. Je ne crois pas qu'il existe un moyen standard de le faire. 

Une fois que vous avez eu l'instance d'exception, vous devrez utiliser un algorithme d'inspection de type. C++ n'a pas de support inhérent à cela. Au mieux, vous devriez avoir une grosse déclaration if/elseif avec dynamic_cast pour vérifier le type. 

1
JaredPar

J'ai essayé de différentes manières. cela fonctionne pour moi:

Commencez par sous-classer erreur d'exécution :

/*----------------------------------------------------------------------*/    
/* subclass runtime_error for safe exceptions in try/throw/catch        */

 #include <stdexcept>
/* a little preprocessor magic here -- makes a subclass of runtime_error*/

#define NEWERROR( NE )  class NE  : public runtime_error {              \
        public:  NE ( string const& error ) : runtime_error(error) {}  }


NEWERROR( FileError      );
NEWERROR( NetworkError   );
NEWERROR( StringError    );
NEWERROR( CofeeError     );

/*----------------------------------------------------------------------*/

Ensuite, vous pouvez créer certaines instances de vos exceptions. 

/*----------------------------------------------------------------------*/
/* some example pre-defined exceptions  */

FileError     ReadOnly                ( "ReadOnly"             );
FileError     FileNotFound            ( "FileNotFound"         );
NetworkError  TimeOutExceeded         ( "TimeOutExceeded"      );
NetworkError  HostNotFound            ( "HostNotFound"         );
CoffeeError   OutOfCoffee             ( "OutOfCoffee"          );

/*----------------------------------------------------------------------*/

Informez explicitement le compilateur que votre fonction peut générer une exception Ou que le programme se terminera probablement au moment de son lancement. Les données risquent d'être perdues ou corrompues Si des ressources sont utilisées à ce moment. 

"Assurez-vous de pouvoir attraper tout ce que vous pouvez lancer."  

(J'utilise le générique erreur d'exécution parce que lancer et attraper cela couvre la totalité de mes exceptions plus celles des systèmes également.)

/*----------------------------------------------------------------------*/
/* example function that may throw an exception */

#include <fstream>

ifstream& getFileStream (string fname) throw (runtime_error)
 {

    if ( fname == "" ) 
      throw StringError( "<getFileStream> fname:empty string" );
      // processing stops here if thrown

    try 
      {
       ifstream Inputfstream;  

       ifstream& ifsref = Inputfstream;

       // ifstream has its own <legacy> exception
       // mechanisms and procedures 
       ifsref.exceptions ( ifstream::failbit | ifstream::badbit );

       ifsref.open (fname , ifstream::in);  // could fail ==> ifstream::failure exception
      }
    catch (ifstream::failure e) 
      {
       throw FileError( fname + string(e.what() ) ); 
      }

    return ifsref;
 }

/*----------------------------------------------------------------------*/

alors dans votre try/catch

/*----------------------------------------------------------------------*/
catch (FileNotFound fnf) //catch a specific error
 {
  if (DEBUG) cerr << "[File Not Found Error: " << fnf.what() << "]" << endl;
  ... (handle it) ... 
 }  
catch (FileError fe) //catch a specific type
 {
  if (DEBUG) cerr << "[File Error: " << fe.what() << "]" << endl;
  ... (handle it) ... 
 }  
catch (runtime_error re ) // catch a generic type
 {
   if (DEBUG) cerr << "[Runtime error: " << re.what() << "]" << endl;          

    // determine type by string comparison
   if ( re.what() == string("ResourceNotavailable") )  ...
   if ( re.what() == string("NetWorkError")         )  ...   

  ...

}
catch ( ... )  // catch everything else 
 { ... exit, rethrow, or ignore ... }

/*----------------------------------------------------------------------*/

Le erreur d'exécution class a un bon support dans les bibliothèques standard c ++, et les compilateurs le savent en interne, et comment optimiser la mémoire et la répartition, afin que vous puissiez les utiliser sur différentes bases de code, en toute sécurité et en toute confiance. Le code est portable et compatible avec de nombreux compilateurs et architectures. 

Il peut être préférable et un peu plus rapide d’attraper chaque erreur séparément dans une clause catch, de plus spécifique à plus générique, si vous pensez qu'une série de correspondances de chaînes est un gaspillage terrible de cpu et de mémoire (le compilateur les optimise cependant). 

<stdexcept> vous donne plusieurs types d'exceptions dans 2 groupes:

  • Erreurs de logique:

    logic_error
    domain_error
    invalid_argument
    length_error
    out_of_range
    
  • Erreurs d'exécution:

    runtime_error
    range_error
    overflow_error
    underflow_error
    

la syntaxe d'utilisation est légèrement différente pour certains d'entre eux.

Selon Sagesse conventionnelle en C++, vos exceptions doivent être relativement "plates", ce qui signifie Que les grandes hiérarchies de catégories spécifiques d’exceptions doivent être évitées au profit de Plutôt que d’autres génériques mais informatifs pour les tâches de programmation générales. Les tâches spécifiques à un domaine, telles que la logique du système réseau, les calculs avancés, etc., peuvent tirer parti de la spécificité, mais cela peut être facilement réalisé en créant des chaînes d'erreur intelligentes avec des exceptions génériques d'exécution et de logique. 

Enfin, Mon point est: Vous pouvez obtenir tout cela en en jetant et en prenant seulement runtime_error .

Il n'est pas nécessaire de créer une panoplie complète d'exceptions très spécifiques (Comme le fait Java) pour chaque classe, chacune gérant une erreur spécifique. 

0
Chris Reid

à condition que c ++ 11 disponible,

bool throwing_func()
{
    // something is wrong...
    throw char('5');

    // ...

    return true;
}

void exception_handler(std::exception_ptr _Eptr)
{
    try 
    {
        if (_Eptr) {std::rethrow_exception(_Eptr);}
    }


    catch(int _Xi)
    {
        std::cout << "int\n";
    }

    catch(char _Xc)
    {
        std::cout << "char\n";
    }

    catch(const std::exception& _Xe)
    {
       std::cout << "std::exception " << _Xe.what() << "\n";
    }

    catch (...)
    {// develop more catch cases above to avoid what follows
        std::cout << "unhandled exception\n";
        // grande problema
    }
}

int main()
{

    try
    {
        throwing_func();
    }

    catch(...)
    {
        //Determine exception type here

         exception_handler(std::current_exception());
    }

    return 0;
}
0
user8315449

Cette question a été posée il y a quelque temps et je propose cette réponse pour accompagner la réponse acceptée d'il y a 9 ans. Je suis d'accord avec l'intimé pour dire que cette réponse, "... n'est pas très utile". En outre, cela ouvre la porte à une exception qui était autrefois gérée sans être gérée. Pour illustrer mon propos, permettez-moi de développer la réponse de l'intimé

#include <iostream>
#include <exception>

class E1 : public std::exception {};
class E2 : public std::exception {};
class E3 : public std::exception {};

int main() {
    try {
        throw E3();
    }
    catch( ... ) {
        try {
            // OOOPS!!! E3 is now unhandled!!!!!!
            throw;
        }
        catch( const E1 & e ) {
            std::cout << "E1\n";
        }
        catch( const E2 & e ) {
            std::cout << "E2\n";
        }
    }
}

Une alternative à cette approche serait la suivante:

#include <iostream>
#include <exception>

class E1 : public std::exception {};
class E2 : public std::exception {};
class E3 : public std::exception {};

int main() {
    try {
        throw E3();
    }
    catch( const E1 & e ) {
        std::cout << "E1\n";
    }
    catch( const E2 & e ) {
        std::cout << "E2\n";
    }
    catch( ... ) {
        std::cout << "Catch-all...";
    }
}

Cette seconde approche semble être équivalente à la première et présente l’avantage de traiter spécifiquement E1 et E2 et de capturer tout le reste. Ceci est offert uniquement comme alternative.

Veuillez noter que, selon le projet C++ du 2011-02-28, paragraphe 15.3, puce 5, "Si présent, un gestionnaire ... sera le dernier gestionnaire de son bloc try."

0
Andrew Falanga