Determining exception type after the exception is caught?
Asked Answered
T

10

60

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
}
Tindle answered 18/2, 2009 at 17:10 Comment(2)
can u plz explain why u need it? maybe we can look out for alternatives?Firstrate
I'd obviously never do this coding something from scratch, but under the specific circumstances I'm under it would be useful. Legacy code.Tindle
C
36

You can actully determine type inside a catch(...), but it is not very useful:

#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";
            }
        }
    }
Comyns answered 18/2, 2009 at 19:24 Comment(5)
This technique actually CAN be useful if the try { throw; } ... part is in a function that is called in the catch (...) block.Occurrence
This technique is really useful. Think of calling an exception handler in the catch(...). The exception handler re-throws and determines type with this technique. This allows for a separation of normal code paths from abnormal (exceptions) code path.Entrenchment
This can be simplified. What is the purpose of the nesting? ideone.com/HddLVrPoole
@Poole The purpose of the nesting is to avoid repetition if there are more things in the main catch(...) block. ideone.com/GqBx56Spada
@wjl, does this technique have a name? What makes it useful? To me it looks equivalent to changing that catch(...) to a sequence of specific catch blocks (in the example two). I mean, it would not allow to determine the exception type if the possible outcomes (in this case E1 and E2) are not know a priori.Eloisaeloise
W
39

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";
    }
}
Wolsky answered 18/2, 2009 at 17:17 Comment(8)
Edited to make it clearer that you would need to catch the base exception.Wolsky
"then you can use this to get type information from your exception.". How? Via something like a dynamic_cast?Tindle
This code should run with " #include <typeinfo> " for use typeid().Corporative
About the 2nd tip, catch and rethrow, I hate it. I am using QtConcurrent, it catches what I throw in a functor, and turns it into an UnknownException. Well it is known to me~Cerebellum
@Cerebellum I agree its horrible. But what can you do with an exception you caught with ...? Why are you catching with ... unless you simply intend to 1) drop all exceptions at this point or 2) log all exceptions. Otherwise, you are catching a known type of exception.Wolsky
Why QtConcurrent catch any exception, it can simply let it happen. But in fact, it catch exception, and turns it into another one. :((Cerebellum
@Cerebellum I have no idea what QtConcurrent is or what it does (its not part of C++). The comment above is on catching via ... There is very little you can do when you don't know the exception, log/drop/ignore (turning it into another exception does not seem to add any real value).Wolsky
@MartinYork agree with your advice. :)Cerebellum
C
36

You can actully determine type inside a catch(...), but it is not very useful:

#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";
            }
        }
    }
Comyns answered 18/2, 2009 at 19:24 Comment(5)
This technique actually CAN be useful if the try { throw; } ... part is in a function that is called in the catch (...) block.Occurrence
This technique is really useful. Think of calling an exception handler in the catch(...). The exception handler re-throws and determines type with this technique. This allows for a separation of normal code paths from abnormal (exceptions) code path.Entrenchment
This can be simplified. What is the purpose of the nesting? ideone.com/HddLVrPoole
@Poole The purpose of the nesting is to avoid repetition if there are more things in the main catch(...) block. ideone.com/GqBx56Spada
@wjl, does this technique have a name? What makes it useful? To me it looks equivalent to changing that catch(...) to a sequence of specific catch blocks (in the example two). I mean, it would not allow to determine the exception type if the possible outcomes (in this case E1 and E2) are not know a priori.Eloisaeloise
C
31

There is no standard, portable way to do this. Here's a non-portable way to do it on GCC and 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;
}

Output:

Type of caught exception is std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
Cleavland answered 7/11, 2017 at 17:59 Comment(1)
Note that you should free() the pointer returned from __cxa_demangle, otherwise your are leaking memory. Also it would be good practice to check if the returned value is not null (this will likely happen when catching std::bad_alloc in case of an out of memory condition).Northamptonshire
T
8

provided that c++11 available,

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;
}
Telefilm answered 11/4, 2018 at 19:41 Comment(0)
D
5

If you need to handle exceptions differently based on what they are, you should be catching specific exceptions. If there are groups of exceptions that all need to be handled identically, deriving them from a common base class and catching the base class would be the way to go. Leverage the power and paradigms of the language, don't fight against them!

Desantis answered 18/2, 2009 at 17:17 Comment(0)
S
1

No.

Doing so would at the very least require you to be able to access the current exception. I do not believe there is a standard way of doing this.

Once you had the exception instance, you would have to use a type inspection algorithm. C++ doesn't have inherent support for this. At best you would have to have a big if/elseif statement with dynamic_cast's to check the type.

Sathrum answered 18/2, 2009 at 17:16 Comment(0)
P
0

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.

Psoas answered 18/2, 2009 at 17:10 Comment(2)
Hmm.. specifying the type of exceptions a method throws has been deprecated for a while now.Hyaline
Yes, so it says. But actually there are some problems that may not be obvious with not specifying the exception type. The problem is that in reality, any method can throw anything: int , char* , objects, etc. If you specify that method X throws (A,B,C) and then it throws something else (like a malloc error) then std::unexpected gets called instead of looking for a handler method or calling std::terminate. Or if you just declare a method with an empty throw() clause std::unexpected gets called on any exception.Psoas
S
0

This question was asked some time ago and I'm offering this answer as a companion to the accepted answer from 9 years ago. I'd have to concur with that respondent that that answer, "... is not very useful." Further, it opens the door to an exception which was once handled being unhandled. To illustrate, let me build upon the respondent's answer

#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";
        }
    }
}

An alternative to this approach would be the following:

#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...";
    }
}

This second approach seems to be tantamount to the first and has the advantage of specifically handling E1 and E2 and then catching everything else. This is offered only as an alternative.

Please note that, according to C++ draft of 2011-02-28, paragraph 15.3, bullet item 5, "If present, a ... handler shall be the last handler for its try block."

Samaritan answered 12/2, 2018 at 19:50 Comment(0)
V
0

Like most answers to questions such as this, there's an unspoken assumption that all the code involved was written by "us" or understood by "us" where "us" is the implementing developer. In cases were we are using legacy code, sparely documented library code or another team included libraries which we don't know about - AND FOLLOWED poor practices; we can be hit by almost anything in a catch all block.

The only viable option here is to play police detective and add catch (char*) catch (int) catch (long long) catch (std::exception) catch (vndr::exception) . . .

until one of them gets a hit.

It's a big Easter egg hunt.

I suggest checking all vendor documentation and including catch statements for their exception types, std exceptions and atomic types like int.

Good luck. I'm fighting this right now with what appears to be an eol error in istream - which is not throwing an std::exception.

Vereen answered 1/5 at 6:6 Comment(0)
F
-2

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".

Finedrawn answered 9/6, 2010 at 8:58 Comment(1)
It's not C++. It's C++/CLI. Which is mostly just a C++ syntax for .NET runtime. It offers C++ functionality, but CLI part is completely different from what C++ is.Maltreat

© 2022 - 2024 — McMap. All rights reserved.