【问题标题】:Determining exception type after the exception is caught?捕获异常后确定异常类型?
【发布时间】:2010-10-08 09:36:33
【问题描述】:

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

例子:

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

【问题讨论】:

  • 你能解释一下你为什么需要它吗?也许我们可以寻找替代方案?
  • 我显然永远不会从头开始编写代码,但在特定情况下我会很有用。旧代码。

标签: c++ exception exception-handling


【解决方案1】:

只要 c++11 可用,

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

【讨论】:

    【解决方案2】:

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

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

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

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

    第二种方法似乎等同于第一种方法,并且具有专门处理E1E2然后捕获其他所有内容的优势。这仅作为替代提供。

    请注意,根据 2011 年 2 月 28 日的 C++ 草案,第 15.3 段,第 5 项,“如果存在,...处理程序应为其 try 块的最后一个处理程序。"

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 请注意,您应该释放()从 __cxa_demangle 返回的指针,否则您会泄漏内存。此外,最好检查返回的值是否不为空(在内存不足的情况下捕获 std::bad_alloc 时可能会发生这种情况)。
      【解决方案4】:

      我尝试了各种方法;这对我有用:

      从子类化 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++标准库中有很好的支持, 编译器在内部知道它,以及如何优化内存和调度, 因此您可以安全而自信地在不同的代码库中使用它们。该代码是可移植的并且与许多不同的编译器和架构兼容。

      如果你觉得一系列字符串匹配是对 CPU 和内存的严重浪费(编译器会优化这些) .

      &lt;stdexcept&gt; 在 2 组中为您提供了几种异常:

      • 逻辑错误:

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

        runtime_error
        range_error
        overflow_error
        underflow_error
        

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

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

      最后,我的观点是:您可以通过以下方式实现所有这一切 仅抛出和捕获 runtime_error

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

      【讨论】:

      • 嗯.. 指定方法抛出的异常类型已经被弃用了一段时间。
      • 是的,它是这么说的。但实际上有一些问题在不指定异常类型时可能并不明显。问题是,实际上,任何方法都可以抛出任何东西: int 、 char* 、 objects 等。如果你指定方法 X 抛出 (A,B,C) 然后它抛出其他东西(比如 malloc 错误)然后 std ::unexpected 被调用,而不是寻找处理程序方法或调用 std::terminate。或者,如果你只是声明一个带有空 throw() 子句的方法,std::unexpected 会在任何异常上被调用。
      【解决方案5】:

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

      例如

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

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

      【讨论】:

      • 这不是 C++。它是 C++/CLI。这主要是 .NET 运行时的 C++ 语法。它提供 C++ 功能,但 CLI 部分与 C++ 完全不同。
      【解决方案6】:

      您实际上可以在 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";
                  }
              }
          }
      

      【讨论】:

      • 如果try { throw; } ... 部分位于catch (...) 块中调用的函数中,此技术实际上会很有用。
      • 这个技巧真的很有用。考虑在catch(...) 中调用异常处理程序。异常处理程序使用此技术重新抛出并确定类型。这允许将正常代码路径与异常(异常)代码路径分开。
      • 这可以简化。嵌套的目的是什么? ideone.com/HddLVr
      • @cp.engr 嵌套的目的是避免在主 catch(...) 块中有更多内容时重复。 ideone.com/GqBx56
      • @wjl,这种技术有名字吗?什么使它有用?在我看来,这相当于将 catch(...) 更改为一系列特定的 catch 块(在示例二中)。我的意思是,如果可能的结果(在本例中为 E1E2)事先不知道,则不允许确定异常类型。
      【解决方案7】:

      简短回答:不。

      长答案:

      如果你从一个公共基类型(比如 std::exception)派生所有异常并显式捕获它,那么你可以使用它从你的异常中获取类型信息。

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

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

      • 捕获:并丢弃异常(停止异常转义析构函数)。
      • Catch:记录发生的未知异常并重新抛出。

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

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

      【讨论】:

      • 已编辑以更清楚地表明您需要捕获基本异常。
      • "那么您可以使用它从您的异常中获取类型信息。"。如何?通过 dynamic_cast 之类的东西?
      • 此代码应与“#include ”一起运行以使用 typeid()。
      • 关于第二个技巧,接球再扔,我讨厌它。我正在使用 QtConcurrent,它捕获我在仿函数中抛出的内容,并将其转换为 UnknownException。嗯,我都知道~
      • @Tiina 我同意它的可怕。但是你能用... 捕获的异常怎么办?为什么要使用...,除非您只是打算 1)此时删除所有异常或 2)记录所有异常。否则,您将捕获已知类型的异常。
      【解决方案8】:

      如果您需要根据异常情况以不同方式处理异常,您应该捕获特定异常。如果有一组异常都需要以相同的方式处理,那么从公共基类派生它们并捕获基类将是可行的方法。利用语言的力量和范式,不要与它们对抗!

      【讨论】:

        【解决方案9】:

        没有。

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

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

        【讨论】:

          猜你喜欢
          • 2011-01-03
          • 2015-11-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多