例外はC ++でチェーンする必要がありますか? [複製
-
30-09-2019 - |
質問
この質問にはすでに答えがあります:
- C ++例外チェーンを実装する適切な/エレガントな方法? 4つの回答
- C ++で内部例外をシミュレートする方法 6回の回答
C ++ - 独自の例外を実装したプログラムの作業を終えたところです(ただし、STD ::例外から派生しました)。 1つの例外が連鎖反応を引き起こし、エラーを上向きに伝播し、他の例外を生み出すときに適用した練習は、モジュールを横切る各適切なステップでエラーメッセージを連結することです(クラスを読む)。つまり、古い例外自体が削除され、新しい例外が作成されますが、エラーメッセージが長くなります。
これは私の小さなプログラムのために機能したかもしれませんが、最終的には自分のアプローチにあまり満足していませんでした。 1つは、最後の例外を除いて、行番号(現時点では適用されていませんが)とファイル名は保持されません。そして実際、その情報は最初の例外で最も関心があります。
私はこれが例外を一緒にチェーンすることによってよりよく処理された可能性があると考えています。 IE古い例外は、新しい例外のコンストラクターに提供されます。しかし、それはどのように実装されますか?例外は、メソッドから範囲外に出たときに死ぬわけではありません。例外が派生したクラスのものにできる場合、例外をコピーして保存する方法は?
これにより、最終的には、C ++で例外をチェーンすることが非常に良い考えであるかどうかを検討することになります。おそらく、1つの例外を作成してから、追加のデータを追加する必要があります(私がやっているように、おそらくはるかに良い方法で)?
これに対するあなたの反応はどうですか?別のものによって引き起こされる例外を一緒に連鎖させて、一種の「例外トレース」を保持する必要がありますか? - または、単一の例外を使用し、それに添付された追加データを使用する必要があります - そして、それはどのように行うべきですか?
解決
例外オブジェクトからデータをコピーしてチェーンにコピーする必要があります。 catch
Rethrow byを除いて、それを受け取るブロック throw;
. 。 (たとえば、その場合は含まれます catch
a throw obj;
.)
これは、ヒープに保存するデータを配置し、実装することで実行できます swap
(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!
他のヒント
この質問が求められているため、C ++ 11で標準に注目すべき変更が加えられています。私は例外に関する議論でこれを継続的に見逃していますが、以下のアプローチ、ネスティングの例外は、トリックを行います。
使用する std::nested_exception
と std::throw_with_nested
StackOverFlowで説明されています ここ と ここ, 、どうすればよいですか あなたの例外についてバックトレースを取得します デバッガーや面倒なロギングを必要とせずにコード内で、ネストされた例外をrethり回る適切な例外ハンドラーを書くだけで。
派生した例外クラスでこれを行うことができるので、そのようなバックトレースに多くの情報を追加できます!あなたも私を見ることができます 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"
あなたはこれを見たいかもしれません: http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/boost-exception.html
MSがC#で行ったこととは多少異なるアプローチですが、要件と一致するようです。
別のアイデアは、関連するデータを例外オブジェクトに追加して、裸を使用することです throw;
それを再投与するための声明。この場合、スタック情報は保持されていると思うので、例外の元のソースをまだ知っていますが、テストは良い考えです。
スタック情報がまったく利用可能であるかどうかは実装が定義されているので、その実装は、それが裸の後に保存されているかどうかによってさらに大きく異なるので賭けます throw;
声明。