web-dev-qa-db-ja.com

「例外を使用しない」キャンプにいる場合、標準ライブラリをどのように使用しますか?

注:私はここで悪魔の擁護者やそのようなものを演奏していません-私はこのキャンプに自分自身がないので、本当に純粋に興味があります。

標準ライブラリのほとんどの型には、例外をスローできる変更関数(たとえば、メモリ割り当てが失敗した場合)または例外をスローできる非変更関数(たとえば、範囲外のインデックス付きアクセサー)があります。それに加えて、多くの無料の関数は例外をスローできます(たとえば、operator newおよびdynamic_cast<T&>)。

どのように実用的に「例外を使用しない」という文脈でこれに対処しますか?

  • neverスローできる関数を呼び出しようとしていますか? (それがどのようにスケーリングされるかはわかりませんので、これが当てはまる場合、どのようにこれを達成するのか非常に興味があります)

  • 標準ライブラリを投げても大丈夫ですか。「例外を使用しません」を「throwourコードからの例外であり、catchother's code "からの例外?

  • コンパイラスイッチを介して例外処理を完全に無効にしますか?もしそうなら、標準ライブラリの例外を投げる部分はどのように機能しますか?

  • [〜#〜] edit [〜#〜]コンストラクター、失敗するか、慣例により、失敗時にエラーコードを返すことができる専用のinit関数を持つ2ステップ構成を使用しますか(コンストラクターができない)、または何か他のことをしますか?

[〜#〜] edit [〜#〜]質問の開始から1週間後の軽微な説明...以下のコメントおよび質問の内容の多くはwhy =例外の側面と「他の何か」。私の興味はそれではありませんが、whenあなたが「何か他のこと」を行うことを選択した場合、howdo例外をスローしますか?

59
Johann Gerell

私は自分自身と世界の隅々に答えます。私はc ++ 14(コンパイラーのサポートが改善されると17になります)を作成します。ルールセットは次のとおりです。

  • 例外なし
  • いいえrtti
  • ランタイムディスパッチなし
  • (ほとんど)継承なし

メモリはプールされ、事前に割り当てられているため、初期化後にmalloc呼び出しはありません。データ構造は不滅または簡単にコピーできるため、デストラクタはほとんど存在しません(スコープガードなどの例外がいくつかあります)。基本的に、C +タイプセーフティ+テンプレート+ラムダを実行しています。もちろん、例外はコンパイラスイッチを介して無効にされます。 STLに関しては、その良い部分(つまり、アルゴリズム、数値、type_traits、反復子、アトミック、...)はすべて使用可能です。例外をスローする部分は、ランタイムメモリ割り当て部分とセミOO部分とうまく一致するため、ストリーム、std :: array、std :: stringを除くコンテナをすべて一度に取り除くことができます。

どうしてですか?

  1. OOのように、例外は問題を他の場所に隠したり移動したりすることで幻想的な清潔さを提供し、プログラムの残りの部分の診断を難しくします。 「-fno-exceptions」なしでコンパイルする場合、すべてのクリーンで適切に動作する関数は、失敗する可能性があるという疑いに耐える必要があります。すべての操作を失敗可能にするよりも、コードベースの周辺で広範な健全性チェックを行う方がはるかに簡単です。
  2. 例外は基本的に、宛先が指定されていない長距離GOTOであるためです。 longjmp()は使用しませんが、例外は間違いなくずっと悪いです。
  3. エラーコードが優れているからです。 [[nodiscard]]を使用して、呼び出しコードを強制的にチェックできます。
  4. 例外階層は不要だからです。ほとんどの場合、エラーの原因を区別することはほとんど意味がありません。エラーが発生した場合は、エラーごとに異なるクリーンアップが必要であり、明示的に通知する方がはるかに優れているためです。
  5. 維持する複雑な不変式があるためです。これは、どのように腸の奥深くにあっても、国境を越えた保証が必要なコードがあることを意味します。これを行うには2つの方法があります:命令型手続きを可能な限り純粋にする(つまり、失敗しないことを確認する)か、不変のデータ構造を持っている(つまり、障害回復を可能にする)のいずれかです。不変のデータ構造がある場合、もちろん例外もありますが、合計型を使用するので、例外を使用することはできません。ただし、機能的なデータ構造は遅いため、他の選択肢は純粋な関数を使用し、C、C++、Rustなどの例外のない言語で実行することです。 Dがどれほどきれいに見えても、GCと例外が取り除かれていない限り、それは非オプションです。
  6. 明示的なコードパスのように、例外をテストしたことがありますか? 「決して起こらない」例外についてはどうですか?もちろん、あなたはそうしません、そして、あなたが実際にそれらの例外にぶつかるとき、あなたは台無しにされます。
  7. 私はC++でいくつかの「美しい」例外中立コードを見てきました。つまり、呼び出すコードが例外を使用するかどうかに関係なく、Edgeケースなしで最適に実行されます。それらは書くのが本当に難しく、すべての例外保証を維持したい場合は修正するのが難しいと思います。ただし、例外をスローまたはキャッチする「美しい」コードを見たことはありません。例外と直接やり取りする私が見たすべてのコードは、普遍的にいものです。例外中立コードの作成に費やされた労力は、例外をスローまたはキャッチする安っぽいコードから節約された労力の量を完全に小さくします。 「Beautiful」は実際の美しさではないため引用符で囲まれています。編集するには例外中立性を維持するための追加の負担が必要になるため、通常化石化されます。例外を意図的かつ包括的に誤用してこれらのEdgeケースをトリガーする単体テストがない場合、「美しい」例外中立コードでさえ肥料になります。
42
KevinZ

この場合、コンパイラーを介して例外を無効にします(gccの場合は-fno-exceptions)。

Gccの場合、_GLIBCXX_THROW_OR_ABORTと呼ばれるマクロを使用します。

#ifndef _GLIBCXX_THROW_OR_ABORT
# if __cpp_exceptions
#  define _GLIBCXX_THROW_OR_ABORT(_EXC) (throw (_EXC))
# else
#  define _GLIBCXX_THROW_OR_ABORT(_EXC) (__builtin_abort())
# endif
#endif

(最新のgccバージョンのlibstdc++-v3/include/bits/c++configにあります)。

次に、スローされた例外が中止されるという事実に対処する必要があります。それでもシグナルをキャッチしてスタックを印刷することができます(SOでこれを説明している)には良い答えがあります)が、この種のことは(少なくともリリースでは)避けるべきです。

何か例を挙げたい場合は、

try {
   Foo foo = mymap.at("foo");
   // ...
} catch (std::exception& e) {}

できるよ

auto it = mymap.find("foo");
if (it != mymap.end()) {
    Foo foo = it->second;
    // ...
}
20
Zouch

また、例外を使用しないことについて尋ねるとき、標準ライブラリに関するより一般的な質問があることを指摘したいと思います。Are yo例外」キャンプ?

標準ライブラリは重いです。たとえば、多くのGameDev企業のように、一部の「例外を使用しません」キャンプでは、STLに適した代替手段が使用されます-主にEASTLまたはTTLに基づいています。とにかく、これらのライブラリは例外を使用しません。これは、第8世代のコンソールが例外をあまりにも(またはまったく)処理しなかったためです。最先端のAAAプロダクションコードの場合、例外はとにかく重すぎるので、このような場合の勝ち組シナリオです。

言い換えれば、多くのプログラマーにとって、例外をオフにすることは、STLをまったく使用しないこととペアになります。

18
abl

私は例外を使用しています...しかし、私は強制されていません。

スローできる関数を呼び出さないようにしていますか? (それがどのようにスケーリングされるかはわかりませんので、これが当てはまる場合、どのようにこれを達成するのか非常に興味があります)

これはおそらく、少なくとも大規模には実行不可能です。多くの関数は投げることができますが、コードベースを完全に損なうことは避けてください。

標準ライブラリを投げても大丈夫ですか?「例外を使用しません」を「コードから例外をスローせず、他のコードから例外をキャッチしない」として扱いますか?

ライブラリコードが例外をスローし、コードがそれを処理しない場合、終了がデフォルトの動作です。

コンパイラスイッチを介して例外処理を完全に無効にしますか?もしそうなら、標準ライブラリの例外を投げる部分はどのように機能しますか?

これは可能です(以前は、一部のプロジェクトタイプで人気があったこともあります)。コンパイラはこれをサポートしている/サポートしている可能性がありますが、結果がどのようなものになる可能性があるのか​​(およびその条件下でどの言語機能がサポートされるのか)についてドキュメントを参照する必要があります。

一般に、例外がスローされる場合、プログラムは中止するか、終了する必要があります。一部のコーディング標準ではまだこれが必要です。JSFコーディング標準が思い浮かびます(IIRC)。

「例外を使用しない」ユーザー向けの一般的な戦略

ほとんどの関数には、一連の前提条件があり、呼び出しの前にをチェックできます。それらを確認してください。満たされていない場合は、電話をかけないでください。そのコードのエラー処理が何であれ、フォールバックします。前提条件が満たされていることを確認するためにチェックできない関数については、それほどではありませんが、プログラムは中止される可能性があります。

例外をスローするライブラリを回避する-標準ライブラリのコンテキストでこれを尋ねたので、これはありません法案に完全に適合しますが、オプションのままです。

その他の可能な戦略。これは些細に聞こえるかもしれませんが、それらを使用しない言語を選択してください。 Cはうまくできました...

...私の質問の要点(もしあれば、標準ライブラリとの相互作用)、私はあなたのコンストラクタについて聞いてみたいです。それらは失敗する可能性がありますか、または慣例により、失敗時にエラーコードを返すことができる(コンストラクターができない)専用のinit関数を持つ2ステップ構成を使用していますか?それとも、あなたの戦略は何ですか?

コンストラクターを使用する場合、通常、障害を示すために使用される2つのアプローチがあります。

  1. 内部エラーコードまたはenumを設定して、障害とその障害を示します。これは、オブジェクトの構築と適切なアクションが行われた後に調査できます。
  2. コンストラクターを使用しないでください(または、少なくとも、コンストラクターで失敗しないもののみを構築します-ある場合)。次に、何らかの種類のinit()メソッドを使用して、構築を実行(または完了)します。失敗した場合、メンバーメソッドはエラーを返すことができます。

init()テクニックの使用は、内部の「エラー」コードよりも連鎖可能であり、拡張性が高いため、一般的に好まれます。

繰り返しになりますが、これらは例外が存在しない環境(Cなど)に由来する手法です。 C++などの言語を例外なく使用すると、その使いやすさと標準ライブラリの幅広さの有用性が制限されます。

10
Niall

あなたが尋ねた質問に完全に答えようとするのではなく、エラーを処理するメカニズムとして例外を利用しないコードベースの例として、Googleを挙げます。

Google C++コードベースでは、失敗する可能性のあるすべての関数は、呼び出し先の結果を指定するstatusなどのメソッドを持つokオブジェクトを返します。
開発者がreturn statusオブジェクトを無視した場合、コンパイルに失敗するようにGCCを設定しました。

また、彼らが提供する小さなオープンソースコード(LevelDBライブラリなど)からは、とにかくSTLをあまり使用していないようであるため、例外処理はまれになります。 Titus WintersがCPPConでの講義で述べているように、彼らは「標準を尊重しますが、それを偶像化しないでください」。

8
David Haim

これは態度の質問だと思います。 「何かが失敗しても気にしない」という陣営にいる必要があります。これは通常、コードを生成します。そのためには、なぜ突然何かが機能しなくなったのかを見つけるために(顧客サイトで)デバッガーが必要です。また、この方法でソフトウェアの「エンジニアリング」を行う可能性のある人々は、非常に複雑なコードを使用しないでください。例えば。依存するすべてのn個のリソースが(これらのリソースにRAIIを使用して)正常に割り当てられた場合にのみ実行されるという事実に依存するコードを書くことはできません。したがって、このようなコーディングは、次のいずれかとなります。

  • エラー処理のための管理不能な量のコード
  • コードの実行を回避するための管理不能な量のコード。これは、一部のリソースの正常な割り当てに依存します
  • エラー処理がないため、サポートと開発者の時間が大幅に長くなります

ここで、最新のコードについて説明していることに注意してください。顧客提供のdllをオンデマンドで読み込み、子プロセスを使用しています。何かが失敗する可能性のある多くのインターフェースがあります。私はgrep/more/ls/findの一部の置き換えについては話していません。

0
user8434768