13

独自の例外を実装した C++ プログラムの作業を終えたところです (ただし、std::exception から派生しています)。1 つの例外が連鎖反応を引き起こし、エラーが上方に伝播し、他の例外が発生する場合に私が適用した方法は、モジュール (読み取りクラス) の各適切なステップでエラー メッセージを連結することです。つまり、古い例外自体が削除され、新しい例外が作成されますが、エラー メッセージが長くなります。

これは私の小さなプログラムではうまくいったかもしれませんが、最終的には自分のアプローチにあまり満足できませんでした. 1 つには、行番号 (現時点では適用されませんが) とファイル名は、最後の例外を除いて保持されません。実際、その情報は最初の例外で最も重要です。

これは、例外を連鎖させることでより適切に処理できたと思います。つまり、古い例外は新しい例外のコンストラクターで提供されます。しかし、それはどのように実装されますか?例外がメソッドからスコープ外に出たときに例外が消滅し、それによって例外ポインターを使用できなくなるのではないでしょうか? また、例外が派生クラスである可能性がある場合、例外をコピーして保存する方法は?

これは最終的に、C++ での例外の連鎖が結局のところ良いアイデアであるかどうかを検討するように導きました。おそらく、1 つの例外を作成し、それに追加のデータを追加する必要があります (私が行ってきたように、おそらくはるかに優れた方法で)?

これに対するあなたの反応は何ですか?ある種の「例外トレース」を保持するために、別の例外によって引き起こされた例外を連鎖させる必要がありますか?また、それをどのように実装する必要がありますか? -- それとも、単一の例外を使用し、追加のデータを追加する必要がありますか? また、それはどのように行うべきですか?

4

4 に答える 4

6

この質問がされてから、C++11 の標準に顕著な変更が加えられました。例外に関する議論ではこれを常に見逃していますが、例外をネストする次のアプローチでうまくいきます。

使用std::nested_exceptionしてstd::throw_with_nested

StackOverflow hereおよびhereで説明されています。ネストされた例外を再スローする適切な例外ハンドラーを記述するだけで、デバッガーや面倒なログを必要とせずに、コード内の例外のバックトレースを取得する方法について説明されています。

任意の派生例外クラスでこれを行うことができるため、このようなバックトレースに多くの情報を追加できます! GitHubの私の MWE もご覧ください。バックトレースは次のようになります。

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
于 2017-10-26T08:32:22.370 に答える
2

catchによる再スローを除いて、データを受け取るブロックよりも長持ちさせたい場合は、例外オブジェクトからチェーンにデータをコピーする必要がありますthrow;。(これには、たとえば、そのcatchブロックが。を介して終了する場合が含まれますthrow obj;。)

これは、保存するデータをヒープに配置し、例外内のプライベートデータにswap( C ++ 0xで)実装することで実行できます。move

もちろん、例外を除いてヒープを使用する場合は注意が必要です…しかし、最近のほとんどのOSでは、メモリのオーバーコミットによりnew、良くも悪くも、スローが完全に防止されます。十分なメモリマージンと完全なメルトダウン時にチェーンから例外を削除することで、チェーンを安全に保つことができます。

struct exception_data { // abstract base class; may contain anything
    virtual ~exception_data() {}
};

struct chained_exception : std::exception {
    chained_exception( std::string const &s, exception_data *d = NULL )
        : data(d), descr(s) {
        try {
            link = new chained_exception;
            throw;
        } catch ( chained_exception &prev ) {
            swap( *link, prev );
        } // catch std::bad_alloc somehow...
    }

    friend void swap( chained_exception &lhs, chained_exception &rhs ) {
        std::swap( lhs.link, rhs.link );
        std::swap( lhs.data, rhs.data );
        swap( lhs.descr, rhs.descr );
    }

    virtual char const *what() const throw() { return descr.c_str(); }

    virtual ~chained_exception() throw() {
        if ( link && link->link ) delete link; // do not delete terminator
        delete data;
    }

    chained_exception *link; // always on heap
    exception_data *data; // always on heap
    std::string descr; // keeps data on heap

private:
    chained_exception() : link(), data() {}
    friend int main();
};

void f() {
    try {
        throw chained_exception( "humbug!" );
    } catch ( std::exception & ) {
        try {
            throw chained_exception( "bah" );
        } catch ( chained_exception &e ) {
            chained_exception *ep = &e;
            for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
                std::cerr << ep->what() << std::endl;
            }
        }
    }

    try {
        throw chained_exception( "meh!" );
    } catch ( chained_exception &e ) {
        for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
            std::cerr << ep->what() << std::endl;
        }
    }
}

int main() try {
    throw chained_exception(); // create dummy end-of-chain
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

出力(適切に不機嫌):

bah
humbug!
meh!
于 2010-08-22T23:35:20.560 に答える
1

これを見たいと思うかもしれません: http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/boost-exception.html

MSがC#で行ったこととは多少異なるアプローチですが、要件に一致しているようです。

于 2010-08-23T06:05:30.720 に答える
0

もう1つのアイデアは、関連データを例外オブジェクトに追加してから、ベアthrow;ステートメントを使用して再スローすることです。この場合、スタック情報は保持されていると思います。そのため、例外の元のソースは引き続きわかりますが、テストすることをお勧めします。

スタック情報がまったく利用可能かどうかは実装で定義されているので、その実装は、裸のthrow;ステートメントの後に何らかの形で保存されるかどうかでさらに大きく異なります。

于 2010-08-22T22:43:55.073 に答える