Determining exception type after the exception is

2020-01-27 15:45发布

Is there a way to determine the exception type even know you caught the exception with a catch all?

Example:

try
{
   SomeBigFunction();
}
catch(...)
{
   //Determine exception type here
}

9条回答
孤傲高冷的网名
2楼-- · 2020-01-27 16:11

If you're using Visual C++ (managed), you can use the GetType() method to get the type of exception and handle it from there.

E.g.

try
    {
        // Run the application
        Application::Run(mainForm);
    }
    catch (Exception^ e)
    {
        String^ exception_type = e->GetType()->ToString();
        throw;
    }

The string will contain something like "System.ArgumentOutOfRangeException".

查看更多
淡お忘
3楼-- · 2020-01-27 16:13

Short Answer: No.

Long Answer:

If you derive all your exceptions from a common base type (say std::exception) and catch this explicitly then you can use this to get type information from your exception.

But you should be using the feature of catch to catch as specific type of exception and then working from there.

The only real use for catch(...) is:

  • Catch: and throw away exception (stop exception escaping destructor).
  • Catch: Log an unknwon exception happend and re-throw.

Edited: You can extract type information via dynamic_cast<>() or via typid() Though as stated above this is not somthing I recomend. Use the case statements.

#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";
    }
}
查看更多
Anthone
4楼-- · 2020-01-27 16:13

I've tried various ways; this works for me:

Begin by subclassing runtime_error :

/*----------------------------------------------------------------------*/    
/* 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     );

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

Then you may create some instances of your exceptions.

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

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

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

Explicitly notify the compiler that your function may throw an exception or the program will probably terminate at the point thrown, and data could be lost or corrupted if resources are in use at the time.

"Make sure you can and do catch anything that you can throw."

(I use the generic runtime_error because throwing and catching it covers all of my exceptions plus the systems' ones as well.)

/*----------------------------------------------------------------------*/
/* 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;
 }

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

then in your 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 ... }

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

The runtime-error class has good support in the c++ standard libraries, and compilers know about it internally, and how to optimize memory and dispatch, so you can use them over different code bases safely and confidently. The code is portable and compatible with many different compilers and architectures.

It may be preferable and somewhat faster to catch each error separately in a catch clause, from more specific to more generic,if you feel a series of string matches is a terrible waste of cpu and memory (the compiler optimizes these though ).

<stdexcept> gives you several kinds of exceptions in 2 groups:

  • Logic errors:

    logic_error
    domain_error
    invalid_argument
    length_error
    out_of_range
    
  • Runtime errors:

    runtime_error
    range_error
    overflow_error
    underflow_error
    

usage syntax is slightly different for some of them.

Conventional Wisdom in C++ says that your exceptions should be relatively "flat", meaning that large hierarchies of specific categories of exceptions should be eschewed in favor of short generic but informative ones for general programming tasks. Domain specific tasks like network system logic, higher maths, etc. may benefit from specificity, but that can be achieved handily by making intelligent error strings with generic runtime/logic exceptions.

Lastly, My Point is: You can achieve all of this by throwing and catching only runtime_error.

You don't have to create a whole trick-bag of highly specific exceptions (like java does) for each class, each handling one specific error.

查看更多
登录 后发表回答