捕获异常后确定异常类型?

时间:2009-02-18 17:10:32

标签: c++ exception exception-handling

有没有办法确定异常类型,甚至知道你用catch all捕获了异常?

示例:

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

9 个答案:

答案 0 :(得分:29)

简答:不。

长答案:

如果从公共基类型(比如std :: exception)派生所有异常并明确捕获它,那么您可以使用它来从异常中获取类型信息。

但是你应该使用catch的特性来捕获特定类型的异常,然后从那里开始工作。

catch(...)的唯一真正用途是:

  • Catch:并抛弃异常(停止异常转义析构函数)。
  • Catch:记录一个unknwon异常事件并重新抛出。

编辑: 您可以通过dynamic_cast<>()或通过typid()提取类型信息 虽然如上所述这不是我推荐的东西。使用案例陈述。

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

答案 1 :(得分:24)

您实际上可以确定catch(...)中的类型,但它不是很有用:

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

答案 2 :(得分:8)

没有标准的,可移植的方式来做到这一点。这是在GCC和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;
}

输出:

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

答案 3 :(得分:6)

如果您需要根据它们的不同来处理异常,那么您应该捕获特定的异常。如果存在所有需要以相同方式处理的异常组,则从公共基类派生它们并捕获基类将是可行的方法。利用语言的力量和范例,不要与他们作斗争!

答案 4 :(得分:1)

没有。

这样做至少会要求您能够访问当前的异常。我不相信有这样做的标准方法。

一旦有了异常实例,就必须使用类型检查算法。 C ++没有固有的支持。充其量你必须有一个大的if / elseif语句与dynamic_cast's来检查类型。

答案 5 :(得分:0)

我尝试过各种方式;这对我有用:

从子类化 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     );

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

然后您可以创建一些例外情况。

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

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

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

明确通知编译器您的函数可能会抛出异常 或者程序可能会在抛出的点终止,数据可能会丢失或损坏 如果当时正在使用资源。

“确保你可以并且确实抓住任何可以投掷的东西。”

(我使用泛型 runtime_error 因为抛出和捕获它涵盖了所有 我的例外以及系统的例外情况。)

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

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

然后在你的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 ... }

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

runtime-error 类在c ++标准库中得到了很好的支持, 和编译器在内部了解它,以及如何优化内存和调度, 因此,您可以安全,自信地在不同的代码库中使用它们。该代码是可移植的,并与许多不同的编译器和体系结构兼容。

在catch子句中单独捕获每个错误可能更好,也更快一些,从更具体到更通用,如果你觉得一系列字符串匹配是对cpu和内存的可怕浪费(编译器优化了这些) 。

<stdexcept>在两组中为您提供了几种例外情况:

  • 逻辑错误:

    logic_error
    domain_error
    invalid_argument
    length_error
    out_of_range
    
  • 运行时错误:

    runtime_error
    range_error
    overflow_error
    underflow_error
    

其中一些语言的使用语法略有不同。

C ++中的常规智慧说你的例外应该相对“平淡”, 意味着应避免使用特定类别的异常的大型层次结构 赞成短通用但信息丰富的通用编程任务。网络系统逻辑,高等数学等特定领域的任务可能会受益于特异性,但这可以通过使用通用运行时/逻辑异常制作智能错误字符串来轻松实现。

最后,我的观点是:你可以实现所有这一切 仅投掷并捕获runtime_error

您不必创建一整套高度特定的异常 (就像java一样)为每个类,每个处理一个特定的错误。

答案 6 :(得分:0)

这个问题前一段时间被问过,我提供这个答案作为9年前接受的答案的伴侣。我必须同意那位受访者的回答,“......不是很有用。”此外,它打开了一个异常的大门,这个异常曾被处理过,未经处理。为了说明,让我以受访者的答案为基础

except

这种方法的替代方案如下:

addCleanup

第二种方法似乎与第一种方法相当,具有专门处理setUpTestContextDecorator然后捕捉其他所有内容的优势。这仅作为替代方案提供。

请注意,根据C ++草案2011-02-28,段落15.3,项目5,“如果存在,...处理程序应该是其try块的最后一个处理程序。

答案 7 :(得分:0)

前提是c ++ 11可用,

\b\d{3}(\D)\d{2}\1\d{4}\b

答案 8 :(得分:-2)

如果您使用的是Visual C ++(托管),则可以使用GetType()方法获取异常类型并从那里处理它。

E.g。

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

该字符串将包含类似“System.ArgumentOutOfRangeException”的内容。