C ++ゲームDLLで例外をスローしますか?長所と短所


8

ゲーム開発に関連してC ++で例外を使用することの長所短所は何ですか。

Googleスタイルガイドによると、例外はさまざまな理由で使用されていません。ゲーム開発に関連する同じ理由はありますか?

C ++の例外は使用しません ...-google-styleguide.googlecode.com

考慮すべきいくつかの問題。

  • 複数のプロジェクトで使用されるライブラリの開発との関係。
  • 単体テスト、統合テストなどにどのように影響しますか?
  • サードパーティのライブラリを使用するとどうなりますか。

3
奇妙なことに誰にもプロがいなかった
デビッド・ヤング

回答:


7

また、C ++で例外がどのように機能するかの詳細をすべて知らない場合、例外を使用することにはいくつかの厄介な結果があります。多くのゲームはC ++で記述されているため、これが要因になる可能性があります。

たとえば、C ++では、デストラクタで例外をスローすると重大な結果が生じる可能性があります。複数のアクティブな例外が発生している状況に陥る可能性があるため、あらゆる種類の未定義の動作とクラッシュを引き起こす可能性があります。(この詳細については、Effective C ++の項目#8を参照してください。)


4
効果的なC ++の場合は+1。話しながら第3版を読みます。例外は私には問題ありませんが、どこで使用するか、どこで使用するかについて非常に厳格であり、サードパーティのコードが同じことを保証できます。
Anthony

9

例外に関するソフトウェアの見解に関するJoel。

現在どの回答にもない興味深い見解、

推論は、例外がコードのあるポイントから別のポイントに突然ジャンプするという点で、1960年代以降有害と考えられている「gotoの例外」と同じだと私は考えています。実際、それらはgotoのものよりも著しく悪いです:

  1. それらはソースコードでは見えません。例外をスローする場合とスローしない場合がある関数を含むコードのブロックを見ると、どの例外がどこからスローされるかを確認する方法はありません。これは、注意深いコード検査でさえ潜在的なバグを明らかにしないことを意味します。

  2. 関数の作成可能な出口点が多すぎます。正しいコードを書くためには、関数を通るすべての可能なコードパスについて本当に考える必要があります。例外を発生させてその場でキャッチしない関数を呼び出すたびに、関数が突然終了し、データが一貫性のない状態になるか、他のコードパスではなかったために、突然のバグが発生する可能性があります。について考える。

http://www.joelonsoftware.com/items/2003/10/13.html


ハレルヤ...とても参考になりました。私はまさにこの理由のために例外を伴う嫌悪を抱いてきましたが、明らかにそれは今日物事を行うための好ましい方法です。別の見解を与える素晴らしい記事を見るのは素晴らしい
Toad

1
+1例外は、遅れて失敗するリスクが高すぎるため、つまり、ゲームがすでに出荷されている場合です。
martinpi 2010

ポイント番号2に完全に同意します。例外を全体的に使用する言語と戦うことはありませんが、エラー状態を処理するための「正しい」方法とも見なしません。
Kylotan

5

例外はサポートされていないため、少なくとも1つの最新のコンソール開発環境では推奨されません。

私が知っているほとんどのコンソール開発者は、実行可能ファイルにオーバーヘッドが追加されていることと、単純に不要であるという哲学のため、とにかくそれらを使用しないことを好みます。RTTIも同じように表示されます。


どのコンソールでサポートされていませんか?
デビッドヤング

以前はSOでより具体的に明らかにされていましたが、NDAのために私は恥ずかしそうにしています。
Dan Olson、

2

私がゲームで取り組んだすべてのプロジェクトのうち、例外を使用したものはありませんでした。主な理由は、関数呼び出しのオーバーヘッドです。前述のように、RTTIと同様に、ほとんどのスタジオでは議論の対象ではありません。ほとんどのプログラマーはJava /学術界の出身なので、率直に言って、ほとんどの人はそうではありません。
条件を主張するだけなので、単体テストには実際には影響しません。ライブラリの場合は、それを使用するすべての人に強制的に適用するため、例外なく例外はありません。
状況によっては処理が少し難しくなりますが、例外は悪用につながる可能性があるため、(imo)はより制御されたセットアップを強制します。エラー耐性は良いですが、それがずさんなコーディングにつながる場合はそうではありません。
ちなみに、これは例外処理を一度も使用したことがない人からのものです(まあ、ツールで1回か2回-それは私にはうまくいきませんでした。私が成長したのはそれだと思います)。


1
あなたは正しいですが、残念なことに、代替のエラー処理を使用するのではなく、ほとんどのゲームプログラマーはクラッシュまたはNULLを返すだけに戻ります(とにかくクラッシュにつながる可能性があります)。適切な代替品はありません。
tenpn

NULLまたはエラーコードを返すことは、戻りコードもチェックする限り、完全に合理的な代替手段です。
JasonD 2010

そうですが、ほとんどのゲームプログラマーはそうではありません。それが私のポイントです。
tenpn

1
これは、C ++言語の問題でありfunction();、エラーコードが無視されているかどうかがすぐにわからないというコード行が表示された場合に発生します。これがおそらく、戻り値を無視できる言語が、例外を優先するように強制しようとする理由です。
Kylotan

1
エラー引数の強制はもはや問題ではありません。例外を使用しない最新のコードErrorは軽量のクラスに依存しており、無視するとアサーションが発生します。ですから、それは本当に無視することはできず、例外を無視することしかできません。
サマウサ2015年

1

プロのゲーム開発者としての私にとって最も重要なことは、例外はそれらによってデバッグされる例外(ゲーム業界にはほとんどありません)のしくみを理解している人々、およびそれらを実行する基礎となるコード(これはとにかく枝分かれした混乱とあなたの順序プロセッサを殺すでしょう)あなたのコンパイラ/リンカーを提供する人々によって書かれなければなりませんでした。これの問題は、リソースが有限であるため、ゲーム開発者が使用するコードのより良い最適化と修正の記述に集中することです...通常、これは例外ではありません。新しいコンパイラを備えた最新のハードウェアに例外的なバグがある場合、誰に連絡しますか?コードですべてが大丈夫だと考える例外的な専門家、またはそう、すぐに、私たちが言うベンダー

例外は本質的に問題はありませんが、現実が理論の邪魔をするため、例外はありません。


0

私にとって例外処理は、すべてがRAIIに準拠し、本当に例外的なパス(例:破損したファイルが読み取られる)の例外を予約していて、モジュールの境界を越えてチーム全体がそれらに参加している場合に最適です。 ……

ゼロコストEH

最近のほとんどのコンパイラーはゼロコストの例外処理を実装しているため、通常の実行パスでエラー条件を手動で分岐するよりもさらに安価にできますが、それと引き換えに例外パスは非常に高価になります(コードの肥大化もありますが、おそらくそれ以上ではありません)考えられるすべてのエラーを手動で完全に処理した場合よりも)。例外がC ++で意図された方法で使用されている場合、スローが非常に高価であるという事実は問題になりません(通常の状況では発生しないはずの本当に例外的な状況の場合)。

副作用の逆転

そうは言っても、すべてをRAIIに準拠させるのは言うよりもはるかに簡単です。関数に格納されているローカルリソースを、それらをクリーンアップするデストラクタでC ++オブジェクトにラップするのは簡単ですが、ソフトウェア全体で発生する可能性のあるすべての副作用の元に戻す/ロールバックロジックを記述するのは簡単ではありません。std::vector完全に例外セーフにするための最も単純なランダムアクセスシーケンスのようなコンテナを書くのがどれほど難しいかを考えてみてください。次に、大規模なソフトウェア全体のすべてのデータ構造にわたってその難易度を乗算します。

基本的な例として、シーングラフにアイテムを挿入するプロセスで発生した例外を考えます。その例外から適切に回復するには、シーングラフでのこれらの変更を元に戻し、操作が発生していないかのようにシステムを状態に戻す必要がある場合があります。つまり、おそらくスコープガードから、例外が発生したときにシーングラフに挿入された子を自動的に削除します。

多くの副作用を引き起こし、多くの永続的な状態を処理するソフトウェアでこれを徹底的に行うことは、言うよりもはるかに簡単です。多くの場合、実際的な解決策は、物事を出荷するためにそれを気にしないことです。ある種の中央undoシステムを備えた適切な種類のコードベースと、おそらく永続的なデータ構造と副作用を引き起こす最小限の関数を使用すると、全面的に例外安全性を達成できる可能性があります...しかし、それはたくさんありますあなたがやろうとしていることがコードをどこでも例外的に安全にしようとしているなら、あなたが必要とするもの。

概念的には副作用の逆転は難しい問題ですが、C ++ではさらに難しくなるため、実際には何か問題があります。実際には、C ++の例外に対応するよりも、エラーコードに対応するCの副作用を元に戻す方が簡単な場合があります。理由の1つは、関数の特定のポイントがthrowC ++で潜在的に発生する可能性があるためです。type Tで動作する一般的なコンテナーを作成しようとしている場合、関連するものTがスローされる可能性があるため、これらの出口点がどこにあるかも予測できません。T等しいかどうかを比較することでもスローする可能性があります。コードではあらゆる可能性を処理する必要があり、C ++では可能性の数が指数関数的に増加しますが、Cでは可能性がはるかに少なくなります。

標準の欠如

例外処理のもう1つの問題は、中央標準の欠如です。うまくいけば、ロールバックが失敗することはないので、デストラクタがスローするべきではないことに誰もが同意することを望んでいる人もいますが、これは、ルールに違反した場合にソフトウェアを通常クラッシュさせる病理学的ケースをカバーしています。

比較演算子から決してスローしない(論理的に不変であるすべての関数は決してスローしてはならない)、ムーブクターからスローしないなど、より適切な標準が必要です。このような保証により、例外セーフなコードを非常に簡単に記述できます。ただし、そのような保証はありません。私たちは、すべてがスローする可能性があるルールに従う必要があります-今ではないとしても、将来的には。

さらに悪いことに、言語の背景が異なる人々は例外を非常に異なって見ます。Pythonでは、彼らは実際に、通常の実行パスで例外をトリガーすることを含む、この「見る前に飛躍する」哲学を持っています!次に、そのような開発者にC ++コードの記述を依頼すると、実際には例外的でないものに対して左右にスローされる例外を確認できます。一般的なコードを記述しようとすると、そのすべてを処理することは悪夢になる可能性があります。

エラー処理

エラー処理には、発生する可能性のあるすべてのエラーをチェックする必要があるという欠点があります。ただしglGetError、関数の出口点を大幅に減らすだけでなく、明示的にするために、後から少し遅れてエラーをチェックできる場合があるという利点もあります。エラーをチェックして伝播および回復する前に、コードを少し長く実行し続けることができる場合もあれば、例外処理よりも本当に簡単な場合もあります(特に副作用の逆転の場合)。しかし、ゲームのエラーもそれほど気にしないかもしれません。おそらく、破損したファイルやメモリ不足エラーなどの問題が発生した場合は、メッセージでゲームをシャットダウンするだけです。

複数のプロジェクトで使用されるライブラリの開発との関係。

DLLコンテキストでの例外の厄介な部分は、同じコンパイラーでビルドされていること、同じ設定を使用していることが保証されていない限り、あるモジュールから別のモジュールに例外を安全にスローできないことです。したがって、プラグインアーキテクチャのように記述している場合あらゆる種類のコンパイラ、そしておそらくLua、C、C#、Javaなどの異なる言語から使用される人々を対象としていますが、すべての例外を飲み込んでエラーに変換する必要があるため、例外が大きな迷惑になります。とにかくどこでもコード。

サードパーティのライブラリを使用するとどうなりますか。

dylibの場合、上記の理由により例外を安全にスローできません。彼らはエラーコードを使用する必要があります。つまり、ライブラリを使用すると、エラーコードを常にチェックする必要があります。静的にリンクされたC ++ラッパーライブラリでdylibをラップし、dylibからのエラーコードをスローされた例外(基本的にはバイナリからバイナリにスロー)に変換することができます。一般的に、dylibs /共有ライブラリを使用する場合、ほとんどのサードパーティのライブラリは煩わしくなく、エラーコードだけに固執するべきです。

単体テスト、統合テストなどにどのように影響しますか?

私は通常、コードの例外/エラーパスをテストするチームにそれほど遭遇しません。私はそれを使用していましたが、実際にコードをbad_alloc超堅牢にしたかったので適切に回復できるコードがありましたが、チームでそれを実行しているのが私だけの場合はあまり役に立ちません。結局私は面倒をやめた。チーム全体がコードを確実に例外やエラーから確実に回復できるようにするためのミッションクリティカルなソフトウェアを想像しますが、それは投資するのに長い時間です。時間はミッションクリティカルなソフトウェアでは理にかなっていますが、ゲームではないかもしれません。

愛憎

例外も私のC ++の愛/嫌いな機能の1つです。たとえば、コードを逆さまに記述する必要がある方法を変更するため、RAIIを利便性というよりも要件のようにする、言語の最もインパクトのある機能の1つです。 、Cは、特定のコード行が関数の暗黙の出口点になる可能性があるという事実を処理します。ときどき、言語に例外がないことを望みます。しかし、例外が本当に役立つと感じる場合もありますが、CとC ++のどちらでコードを書くかを選択する際に、例外が多すぎるためです。

標準委員会が、少なくともC ++からC ++コードまで、モジュール間で安全に例外をスローできるようにするABI標準の確立に重点を置いている場合、それらは指数関数的に私にとってより有用です。安全に複数の言語を使い分けられるとしたら、それはすばらしいことですが、それは一種の夢です。例外が指数関数的にさらに便利になるもう1つの理由は、例外noexceptが発生したときに呼び出すのではなく、スローできる機能を呼び出そうとしたときに、関数が実際にコンパイラエラーを引き起こした場合std::terminateです。これは、役に立たない(のicantexcept代わりにと呼ぶこともありますnoexcept)の隣にあります。たとえば、次のような場合、すべてのデストラクタが例外に対して安全であることを確認する方がはるかに簡単です。noexceptデストラクタがスローできるものを実行すると、実際にはコンパイラエラーが発生しました。それがコンパイル時に完全に決定するにはコストが高すぎる場合は、noexcept関数(すべてのデストラクタが暗黙的にそうでなければならない)が同様にnoexcept関数/演算子を呼び出すことだけが許可され、コンパイラエラーがそれらのいずれかからスローされるようにするだけです。


-2

私が見る限り、ゲーム開発で従来使用されていない主な理由が2つあります。

  • ほとんどのゲームプログラマーは、JavaとHaskellで学んだ卒業生か、例外的な経験のないCプログラマーです。したがって、彼らはそれらをかなり恐れており、それらを正しく使用する方法を知りません。
  • 例外がスローされたときに展開されるスタックは、パフォーマンスに影響を与えます(これは一種の知恵であり、一部の参照はすばらしいでしょう)。

3
Javaプログラマーは例外の使い方を知らないのですか?Javaは例外処理に基づいています。最も基本的なJavaプログラマーでさえ、Try、Catch、Finallyブロックを理解しています。例外をスローしたり、例外を処理したりしないと、Javaでプログラミングすることはできません。
David Young

4
さらに、単に巻き戻しをスタックします。例外は、すべての関数呼び出しにオーバーヘッドを追加します。codeproject.com/KB/cpp/exceptionhandler.aspx
Jonathan

@David Young私はより強い/弱い保証について、そして例外を使用することのより複雑な側面について、大卒者が経験されていないかもしれないことについて話しています。 Java例外を適切に使用する方法の学習に重点を置きません。しかし、あなたは正しいです。
tenpn

また、「恐れる」は間違った言葉かもしれません。:)
tenpn

3
彼らはしばしば「恐れる」の反対だと思います-厳密な「例外なし、例外なし」ルールがない場合、最近のJavaの卒業生は、初心者のCプログラマーのように、戻るよりも頻繁に例外をスローすることになります適切なループ不変式を記述するのではなく、gotoおよびbreakを使用してください。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.