C++ の例外エラーを移植可能に処理する
-
09-06-2019 - |
質問
私は Visual C++ アプリケーションを GCC に移植することに取り組んでいます (MingW と Linux 上に構築する必要があります)。
既存のコードでは、 __try { ... } __except(1) { ... }
いくつかの場所でブロックされているため、最小限のログ記録を行わずにプログラムが終了することはほとんどありません(おそらくメモリ不足タイプのエラーを除く)。
GCC で同様のことを行うためのオプションは何ですか?
編集:Visual Studio の /EH オプションへのポインターを教えていただきありがとうございます。ここで必要なのは、Linux で信号を処理する方法に関するいくつかの例です。見つけました このメッセージ 2002年から。
他にどのような信号がありますか SIGFPE
そして SIGSEVG
気をつけるべきでしょうか?(主に、から発生する可能性のあるものを気にします) 自分 何か間違ったことをしている)
報奨金情報:アプリケーションが終了する前に、できるだけ多くのエラー状態を自己ログに記録できるようにしたいと考えています。
どのような信号が発生する可能性がありますか?また、通常、その後エラー メッセージを記録することは不可能です。(メモリ不足ですが、他に何かありますか?)
コードが少なくとも Linux と MingW で同じように動作する移植可能な方法で、例外と (最も重要な) シグナルを処理するにはどうすればよいでしょうか。#ifdef でOKです。
失敗をログに記録するラッパー プロセスだけを使用しない理由は、パフォーマンス上の理由から、最後の瞬間までディスクへのデータの書き込みを保存し、何か問題が発生した場合は、事前にデータの書き込みを可能な限り試みたいためです。退出中。
解決
try { xxx } catch(...) { xxx } は移植性が高くなりますが、それほどキャッチできない可能性があります。コンパイラの設定や環境によって異なります。
デフォルトの VC++ 設定を使用すると、非同期 (SEH) エラーは C++ EH インフラストラクチャに配信されません。それらをキャッチするには、代わりに SEH ハンドラー (__try/__Except) を使用する必要があります。VC++ では、C++ エラー処理を通じて SEH エラーをルーティングできます。これにより、catch(...) で SEH エラーをトラップできるようになります。これには、null ポインタの逆参照などのメモリ エラーが含まれます。 詳細.
ただし、Linux では、Windows が SEH を使用するエラーの多くはシグナルによって示されます。これらは try/catch によってキャッチされることはありません。それらを処理するにはシグナルハンドラーが必要です。
他のヒント
MSFT 独自の拡張機能の代わりに C++ 標準例外を使用しないのはなぜでしょうか?C++ には例外処理の概念があります。
struct my_exception_type : public logic_error {
my_exception_type(char const* msg) : logic_error(msg) { }
};
try {
throw my_exception_type("An error occurred");
} catch (my_exception_type& ex) {
cerr << ex.what << endl;
}
C++ には「catchall」句もあるので、例外をログに記録したい場合は、次のラッパーを使用できます。
try {
// …
}
catch (...) {
}
ただし、このような一般的なラッパーを作成すると、処理コードをラッパーに挿入する必要があるため、これは C++ ではあまり効率的ではありません。 毎 (実際に例外がスローされない限り、例外処理に追加コストがかからない .NET のようなマネージド システムとは異なります)。
移植性を高めるために、試してみるべきことの 1 つは、ほとんどのバニラ例外に対して try-catch ブロックを使用し、致命的な終了条件に使用できる最小限のフックを持つように終了ハンドラー (set_terminate_handler) を設定することです。atexit ハンドラーや on_exit ハンドラーのようなものを追加してみることもできます。もちろん、これらの関数を入力するとき、実行環境が奇妙になったり破損したりする可能性があるため、どこまで正常な環境を想定しているかには注意してください。
最後に、通常の try-catch ペアを使用する場合は、関数の本体で try ブロックを開くのではなく、関数 try ブロックの使用を検討できます。
int foo(int x) try {
// body of foo
} catch (...) {
// be careful what's done here!
}
これらは C++ の比較的知られていない部分であり、場合によっては部分的な (小規模な) スタック破損が発生した場合でも回復できる可能性があります。
最後に、はい、どのシグナルを独自に継続的に処理できるか、またはどのシグナルで中止する可能性があるかを調査する必要があるでしょう。また、実装する処理メカニズムを減らしたい場合は、新しいオペレーターの非スロー バージョンを呼び出すことを検討するとよいでしょう。必要に応じて、浮動小数点例外を生成しないようにコンパイルします (FP の結果で isnan(.)、isfinite(.) をいつでもチェックして身を守ることができます)。
最後の注意点については、次の点に注意してください。Linux と Windows では、浮動小数点結果分類関数が異なるヘッダーに存在する可能性があることに気付きました。したがって、これらのインクルードを条件付きで設定する必要がある場合があります。
生意気な方は、setjmp と longjmp を使ってすべて書いてください (冗談です...)。
C++ 例外をキャッチする catch(...)
すでにあなたはトワイライトゾーンにいます。
によってキャッチされなかったエラーをキャッチしようとしています catch(...)
未定義の動作に真っ向から直面します。動作が保証されている C++ コードはありません。最小限のログ記録コードにより、代わりにミサイルが発射される可能性があります。
私がお勧めするのは、そうしようとさえしないことです catch(...)
. 。有意義かつ安全にログに記録できる例外のみをキャッチし、残りは OS に処理させます (存在する場合)。
根本原因に加えてエラー処理コードの失敗がある場合、事後デバッグは醜くなります。
使いやすく、移植可能で、リソースをほとんど使用しない方法の 1 つは、空のクラスをキャッチすることです。最初は奇妙に聞こえるかもしれませんが、非常に便利です。
これは、あなたの質問にも当てはまる別の質問用に作成した例です。 リンク
また、複数のキャッチを持つこともできます。
try
{
/* code that may throw exceptions */
}
catch (Error1 e1)
{
/* code if Error1 is thrown */
}
catch (Error2 e2)
{
/* code if Error2 is thrown */
}
catch (...)
{
/* any exception that was not expected will be caught here */
}