web-dev-qa-db-ja.com

例外がキャッチされた後で例外のタイプを判別しますか?

キャッチオールで例外をキャッチしたことを知っていても、例外タイプを判別する方法はありますか?

例:

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

実際には、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";
            }
        }
    }
25
anon

短い答え:いいえ。

長い答え:

すべての例外を共通の基本型(std :: exceptionなど)から派生させて明示的にキャッチした場合、これを使用して例外から型情報を取得できます。

ただし、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";
    }
}
31
Martin York

これを行うための標準的でポータブルな方法はありません。これは、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> >
13
user697683

例外に基づいて例外を異なる方法で処理する必要がある場合は、特定の例外をキャッチする必要があります。すべてを同じように処理する必要がある例外のグループがある場合、それらを共通の基本クラスから派生させ、基本クラスをキャッチするのが適切です。言語の力とパラダイムを活用し、それらと戦わないでください!

6
Harper Shelby

番号。

これを行うには、少なくとも、現在の例外にアクセスできる必要があります。これを行う標準的な方法があるとは思いません。

例外インスタンスを取得したら、型検査アルゴリズムを使用する必要があります。 C++には、これに対する固有のサポートはありません。せいぜい、タイプをチェックするには、dynamic_castを含む大きなif/elseifステートメントが必要です。

1
JaredPar

私はさまざまな方法を試しました。これは私にとってはうまくいきます:

まずサブクラス化 ランタイムエラー

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

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

関数が例外をスローするか、プログラムがスローされたポイントで終了する可能性があり、リソースがそのときに使用されている場合、データが失われたり破損したりする可能性があることをコンパイラに明示的に通知します。

「投げることができるものはすべてできることを確認してください。」

(私は一般的な ランタイムエラー それを投げて捕まえるのは私の例外のすべてとシステムの例外もカバーするからです。)

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

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

その後、あなたのトライ/キャッチで

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

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

の ランタイムエラー クラスはc ++標準ライブラリで優れたサポートを提供しており、コンパイラは内部的にそれを知っており、メモリとディスパッチを最適化する方法を知っているので、異なるコードベースで安全かつ確実にそれらを使用できます。コードは移植可能であり、多くの異なるコンパイラおよびアーキテクチャと互換性があります。

一連の文字列の一致がCPUとメモリのひどい無駄であると感じる場合は、より具体的から一般的なものまで、catch句で各エラーを個別にキャッチする方が望ましい場合があり、多少速くなります(コンパイラはこれらを最適化します)。

<stdexcept>は、2つのグループでいくつかの種類の例外を提供します。

  • 論理エラー:

    logic_error
    domain_error
    invalid_argument
    length_error
    out_of_range
    
  • ランタイムエラー:

    runtime_error
    range_error
    overflow_error
    underflow_error
    

使用法の構文は、一部で若干異なります。

C++の従来のWisdomは、例外は比較的「フラット」である必要があると述べています。つまり、特定の例外カテゴリの大きな階層は、一般的なプログラミングタスクでは一般的ではあるが有益なものを優先して避ける必要があります。ネットワークシステムロジック、より高い数学などのドメイン固有のタスクは、特定性の恩恵を受ける可能性がありますが、一般的なランタイム/ロジックの例外を使用してインテリジェントなエラー文字列を作成することで簡単に実現できます。

最後に、 私のポイントは:これはすべてrunning_errorのみをスローしてキャッチするによって実現できます。

クラスごとに非常に具体的な例外(Javaのように)のような)の完全なトリックバッグを作成する必要はなく、それぞれが1つの特定のエラーを処理します。

0
Chris Reid

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

この質問は少し前に尋ねられたものであり、私はこの回答を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...";
    }
}

この2番目のアプローチは最初のアプローチと同等であるように思われ、E1E2を具体的に処理してから、他のすべてをキャッチするという利点があります。これは代替手段としてのみ提供されます。

2011-02-28、段落15.3、箇条書き項目5のC++ドラフトによれば、「存在する場合、...ハンドラーは、そのtryブロックの最後のハンドラーである必要があります。 "

0
Andrew Falanga