web-dev-qa-db-ja.com

プログラムを適切に終了します。例外の使用

質問:エラーメッセージを表示して閉じるだけの場合、例外を使用してプログラムを終了する適切な方法はありますか(プログラムの奥深くにいる可能性があるため)?代わりにexit()のようなものを明示的に呼び出すことはできますか?

私が現在していること:

私はゲームプロジェクトに取り組んでおり、そのようなアクションを必要とするエラーが発生した場合にプログラムを終了するための最良の方法を見つけようとしています。たとえば、テクスチャを読み込めない場合は、エラーメッセージを表示してプログラムを終了します。

私は現在、次のような例外を除いてこれを行っています:

int main()
{
   Game game;
   try
   {
       game.run();
   }
   catch (BadResolutionException & e)
   {
       Notification::showErrorMessage(e.what(), "ERROR: Resolution");
       return 1;
   }
   catch (BadAssetException & e)
   {
       Notification::showErrorMessage(e.what(), "ERROR: Assets");
       return 1;
   }
   catch (std::bad_alloc & e)
   {
       Notification::showErrorMessage(e.what(), "ERROR: Memory");
       return 1;
   }
   return 0;
}

Bad_allocを除くすべては、runtime_errorから派生した私自身が定義した例外です。

手動でリソースをクリーンアップする必要はなく、動的割り当てにstd :: unique_ptrを使用しています。エラーメッセージを表示してプログラムを閉じるだけです。

例外の調査/代替:

SOや他の場所でたくさんの投稿を調べて、例外を使わない、例外を使う、しかしあなたがそれらを間違って使うなど、他の人が何かを言うのを見ました。私も調べましたexit()のようなものを明示的に呼び出す。

exit()を使用するのは良いことのように聞こえますが、すべてをクリーンアップするメインまでコールスタックを経由しないことを読みました(これが再び見つかる場合リンクを投稿します)。さらに、 http://www.cplusplus.com/reference/cstdlib/exit/ によると、複数のスレッドがアクティブな場合、これは使用しないでください。 2番目のスレッドを少なくとも1回は短時間作成することを期待していますが、そのスレッドでエラーが発生する可能性があります。

例外を使用しないは、ゲームに関連するいくつかの返信で言及されました https://gamedev.stackexchange.com/questions/103285/how -industy-games-handle-their-code-errors-and-exceptions

使用例外はここで議論されました: http://www.quora.com/Why-do-some-people-recommend-not- using-exception-handling-in-C++

私が読んだ他の多くの情報源がありますが、それらは私が見た最新のものでした。

個人的な結論:

エラー処理と例外の使用に関する経験が限られているため、正しい方向に進んでいるかどうかはわかりません。上記のコードに基づいて、例外を使用するルートを選択しました。例外を除いてこれらのケースに取り組むべきであることに同意する場合、私はそれを正しく使用していますか?

23
Programmer001

一般に、すべての例外をmainに伝播させることをお勧めします。これは主に、スタックが適切に巻き戻され、すべてのデストラクタが呼び出されていることを確認できるためです( この回答 を参照)。また、このように物事を行う方が組織化されていると思います。プログラムがどこで終了するかは常にわかっています( プログラムがクラッシュしない限り )。また、より一貫性のあるエラー報告が容易になります(例外処理ではしばしば無視される点です。例外を処理できない場合は、ユーザーがその理由を正確に知っていることを確認する必要があります)。常にこの基本的なレイアウトから始める場合

int main(int argc, const char **argv)
{
    try {
         // do stuff
         return EXIT_SUCCESS;
    } catch (...) {
        std::cerr << "Error: unknown exception" << std::endl;
        return EXIT_FAILURE;
    }
}

そうすれば、大した間違いはありません。エラー報告を改善するために、特定のcatchステートメントを追加できます(追加する必要があります)。

マルチスレッド時の例外

標準ライブラリ機能を使用してC++ 11でコードを非同期に実行するには、std::asyncstd::threadの2つの基本的な方法があります。

まず簡単なもの。 std::asyncstd::futureを返します。これは、指定された関数でスローされたキャッチされなかった例外をキャプチャして保存します。将来std::future::getを呼び出すと、例外が呼び出し元のスレッドに伝播します。

auto fut = std::async(std::launch::async, [] () { throw std::runtime_error {"oh dear"}; });
fut.get(); // fine, throws exception

一方、std::threadオブジェクトの例外がキャッチされない場合、std::terminateが呼び出されます。

try {
    std::thread t {[] () { throw std::runtime_error {"oh dear"};}};
    t.join();
} catch(...) {
    // only get here if std::thread constructor throws
}

これに対する1つの解決策は、std::exception_ptrstd::threadオブジェクトに渡して、例外を渡すことです。

void foo(std::exception_ptr& eptr)
{
    try {
        throw std::runtime_error {"oh dear"};
    } catch (...) {
        eptr = std::current_exception();
    }
}

void bar()
{
    std::exception_ptr eptr {};

    std::thread t {foo, std::ref(eptr)};

    try {
        // do stuff
    } catch(...) {
        t.join(); // t may also have thrown
        throw;
    }
    t.join();

    if (eptr) {
        std::rethrow_exception(eptr);
    }
}

より良い方法はstd::package_taskを使用することですが:

void foo()
{
    throw std::runtime_error {"oh dear"};
}

void bar()
{
    std::packaged_task<void()> task {foo};
    auto fut = task.get_future();

    std::thread t {std::move(task)};
    t.join();

    auto result = fut.get(); // throws here
}

ただし、std::threadを使用する正当な理由がない限り、std::asyncを優先してください。

17
Daniel

回復不能なエラーをキャッチし、この方法でプログラムをシャットダウンしても問題はありません。実際、それは例外がどのように使用されるべきかです。ただし、通常の状況では、例外を使用してプログラムのフローを制御するという境界線を越えないように注意してください。これらは常に、エラーが発生したレベルで適切に処理できないエラーを表す必要があります。

exit()を呼び出しても、呼び出した場所からスタックが巻き戻されることはありません。きれいに終了したい場合は、すでに行っていることが理想的です。

8
David

あなたはすでに答えを受け入れましたが、私はこれについて何かを追加したいと思いました:

代わりにexit()のようなものを明示的に呼び出すことはできますか?

Exitを呼び出すことはできますが、(おそらく)そうすべきではありません。

std::exitは、単に「アプリケーションに何もすることがない」ではなく、「今すぐ終了!」を表現したい状況のために予約する必要があります。

例として、癌治療で使用されるレーザーのコントローラーを作成する場合、問題が発生した場合の最優先事項は、レーザーをシャットダウンしてstd::exit-またはおそらくstd::terminate(アプリケーションがハングしたり、遅くなったり、クラッシュしたりしても、患者が死亡しないようにするため)。

アプリケーションフローの制御に例外を使用しない方法と同様に、通常の状態でアプリケーションを停止するためにexitを使用しないでください。

6
utnapistim

エラーメッセージを表示して閉じるだけの場合、例外を使用してプログラムを終了する適切な方法はありますか(プログラムの奥深くにいる可能性があることを考慮して)?

はい。これが例外を使用する理由です。コードの奥深くでエラーが発生し、より高いレベルの何かがそれを処理します。あなたの場合、最高レベルで。

例外とエラーコードの賛成/反対の議論があり、これは良い読み物です:

例外またはエラーコード

代わりにexit()のようなものを明示的に呼び出すことはできますか?

可能ですが、ログコードが重複する可能性があります。また、将来、例外を別の方法で処理することにした場合は、すべての終了呼び出しを変更する必要があります。別のメッセージが必要だった、または別のプロセスにフォールバックしたいとします。

別の同様の質問:

c ++でのexit()の正しい使用法?

また、すべての(C++)例外を処理するわけではないため、アプローチに欠陥があります。あなたはこのようなものが欲しいです:

int main()
{
    Game game;
    try
    {
        game.run();
    }
    catch (BadResolutionException & e)
    {
        Notification::showErrorMessage(e.what(), "ERROR: Resolution");
        return 1;
    }
    catch (BadAssetException & e)
    {
        Notification::showErrorMessage(e.what(), "ERROR: Assets");
        return 1;
    }
    catch (std::bad_alloc & e)
    {
        Notification::showErrorMessage(e.what(), "ERROR: Memory");
        return 1;
    }
    catch (...)
    {
        // overload?
        Notification::showErrorMessage("ERROR: Unhandled");
        return 1;
    }

    return 0;
}

すべての*例外を処理しない場合、有用なことを何も言わずにゲームを終了させることができます。

すべての例外を処理することはできません。このリンクを参照してください:

C++がすべての例外をキャッチ

3
Class Skeleton

ドキュメントから:

[[noreturn]] void exit(int status);呼び出しプロセスの終了プロセスを正常に終了し、プログラムを終了するための定期的なクリーンアップを実行します。

通常のプログラム終了は、以下を(同じ順序で)実行します。スレッドの保存期間を持つ現在のスレッドに関連付けられているオブジェクトは破棄されます(C++ 11のみ)。静的ストレージ期間を持つオブジェクトは破棄され(C++)、atexitに登録されている関数が呼び出されます。すべてのCストリーム(の関数で開く)が閉じられ(バッファリングされている場合はフラッシュされ)、tmpfileで作成されたすべてのファイルが削除されます。制御はホスト環境に戻されます。

自動ストレージを備えたオブジェクトはexit(C++)を呼び出しても破棄されないことに注意してください。

ステータスがゼロまたはEXIT_SUCCESSの場合、正常終了ステータスがホスト環境に返されます。ステータスがEXIT_FAILUREの場合、失敗した終了ステータスがホスト環境に返されます。それ以外の場合、返されるステータスはシステムとライブラリの実装によって異なります。

上記のクリーンアップを実行しない同様の関数については、quick_exitを参照してください。

2
Nactus