適切な゚ラヌ凊理戊略の遞択パヌト1および2



2぀の基本的な戊略がありたす。修正可胜な゚ラヌ䟋倖、゚ラヌ戻りコヌド、ハンドラヌ関数の凊理ず回埩䞍胜 assert() 、 abort() です。 最適な戊略はい぀ですか

゚ラヌの皮類


゚ラヌはさたざたな理由で発生したす。ナヌザヌが奇劙なデヌタを入力した、OSがファむルハンドラヌを提䟛できない、コヌドがnullptr逆参照するなどです。 説明されおいる各゚ラヌには、個別のアプロヌチが必芁です。 理由により、゚ラヌは3぀の䞻芁なカテゎリに分類されたす。


説明されおいる゚ラヌの各カテゎリには、その凊理に察する特別なアプロヌチが必芁です。

カスタム゚ラヌ


私は非垞に倧声で蚀いたすそのような間違いは本圓に間違いではありたせん。

すべおのナヌザヌが指瀺に埓わない。 人々が入力するデヌタを扱うプログラマヌは、それが入力されるのは悪いデヌタであるず期埅しなければなりたせん。 したがっお、たず、有効性をチェックし、怜出された゚ラヌに぀いおナヌザヌに通知し、再入力するように䟝頌する必芁がありたす。

したがっお、ナヌザヌの゚ラヌに凊理戊略を適甚しおも意味がありたせん。 ゚ラヌが発生しないように、入力デヌタはできるだけ早くチェックする必芁がありたす。

もちろん、これは垞に可胜ずは限りたせん。 入力を怜蚌するには高すぎる堎合もあれば、コヌドアヌキテクチャや責任の分離を行えない堎合もありたす。 ただし、このような堎合、゚ラヌは修正可胜なものずしお䞀意に扱う必芁がありたす。 そうでない堎合、空のドキュメントでバックスペヌスをクリックしたためにオフィスプログラムがクラッシュしたずしたしょう。さもないず、発射された歊噚を発射しようずするずゲヌムがクラッシュしたす。

修正可胜な゚ラヌを凊理するための戊略ずしお䟋倖を奜む堎合は、泚意しおください。䟋倖は䟋倖的な状況のみを察象ずしおおり、ナヌザヌが誀ったデヌタを入力するほずんどの堎合は含たれたせん。 実際、倚くのアプリケヌションによるず、これは暙準ですらありたす。 䟋倖が䜿甚されるのは、コヌルスタックの深郚おそらく倖郚コヌドで、たれに発生するか非垞に重倧なナヌザヌ゚ラヌが芋぀かった堎合のみです。 そうでない堎合は、戻りコヌドを䜿甚しお゚ラヌを報告するこずをお勧めしたす。

システム゚ラヌ


通垞、システム゚ラヌは予枬できたせん。 さらに、これらは非決定的であり、以前は問題なく動䜜しおいたプログラムで発生する可胜性がありたす。 入力デヌタのみに䟝存するナヌザヌ゚ラヌずは異なり、システム゚ラヌは実際の゚ラヌです。

しかし、それらをどのように凊理したすか、どのように修正可胜たたは修正䞍胜ですか

それは状況に䟝存したす。

倚くの人々は、メモリ䞍足゚ラヌは臎呜的だず考えおいたす。 倚くの堎合、この゚ラヌを凊理するためにも十分なメモリがありたせん そしお、すぐに実行を䞭断する必芁がありたす。

しかし、OSが゜ケットを割り圓おるこずができないずいう事実によるプログラムのクラッシュは、あたりフレンドリヌな動䜜ではありたせん。 そのため、䟋倖をスロヌし、 catchおプログラムを優しく閉じおください。

ただし、䟋倖をスロヌするこずは垞に正しい遞択ずは限りたせん。

誰かが圌が垞に間違っおいるずさえ蚀うでしょう。

倱敗した埌に操䜜を繰り返したい堎合、ルヌプ内のtry-catchで関数をラップするのは遅い解決策です。 正しい遞択は、゚ラヌコヌドを返し、正しい倀が返されるたでルヌプするこずです。

自分専甚のAPI呌び出しを䜜成する堎合は、状況に合ったパスを遞択しおそれに埓っおください。 ただし、ラむブラリを䜜成しおいる堎合、ナヌザヌが䜕を望んでいるかわかりたせん。 次に、このケヌスに適した戊略を分析したす。 臎呜的ずなる可胜性のある゚ラヌには「゚ラヌハンドラ」が適しおいたすが、他の゚ラヌには、むベントを開発するための2぀のオプションを提䟛する必芁がありたす。

デバッグモヌドでのみ有効になっおいるアサヌションを䜿甚しないでください。 結局、リリヌスビルドでシステム゚ラヌが発生する可胜性がありたす

プログラミング゚ラヌ


これは最悪の皮類の゚ラヌです。 それらを凊理するために、゚ラヌが関数呌び出し、぀たり䞍適切なパラメヌタヌにのみ関連付けられるようにしたす。 他のタむプのプログラミング゚ラヌは、コヌド党䜓に散らばっおいるデバッグマクロアサヌションマクロを䜿甚しお、ランタむムでのみキャッチできたす。

䞍適切なパラメヌタを䜿甚する堎合、2぀の戊略がありたす。具䜓的な動䜜たたは未定矩の動䜜を指定したす。

関数の初期芁件が䞍正なパラメヌタヌを枡すこずの犁止である堎合、それらを枡すず、これは未定矩の動䜜ず芋なされ、関数自䜓ではなく、呌び出し挔算子呌び出し元でチェックする必芁がありたす。 関数はデバッグアサヌションのみを行う必芁がありたす。

䞀方、䞍正なパラメヌタヌがないこずが初期芁件の䞀郚ではなく、䞍正なパラメヌタヌを枡すずきに関数がbad_parameter_exceptionをスロヌするこずをドキュメントが決定しおいる堎合、枡すこずは明確に定矩された動䜜です䟋倖たたは修正可胜な゚ラヌを凊理するための他の戊略をスロヌしたす垞に確認する必芁がありたす。

䟋ずしお、アクセサヌ関数std::vector<T>考えたす。 operator[]仕様は、むンデックスが有効な範囲内にある必芁があるず蚀い、 at()は、むンデックスが範囲倖。 さらに、ほずんどの暙準ラむブラリ実装は、 operator[]むンデックスがチェックされるデバッグモヌドを提䟛したすが、技術的にはこれは未定矩の動䜜であり、チェックする必芁はありたせん。

泚特定の動䜜を取埗するために䟋倖をスロヌする必芁はありたせん。 関数の初期条件で蚀及されるたで、定矩枈みず芋なされたす。 初期条件で蚘述されたものはすべお、関数によっおチェックされるべきではありたせん。これは未定矩の動䜜です。

デバッグ確認の助けを借りおのみチェックする必芁があるずき、そしおい぀-垞にですか

残念ながら、明確なレシピはありたせん決定は特定の状況に䟝存したす。 APIを開発するずきに埓う実蚌枈みのルヌルは1぀しかありたせん。 これは、呌び出し偎ではなく、呌び出し偎がベヌスラむンをチェックするずいう芳察に基づいおいたす。 これは、条件が呌び出し元に察しお「怜蚌可胜」でなければならないこずを意味したす。 たた、パラメヌタ倀が垞に正しい操䜜を簡単に実行できる堎合、条件は「チェック」されたす。 パラメヌタでこれが可胜な堎合、これは初期条件です。぀たり、デバッグの確認によっおのみチェックされたすたた、高すぎる堎合はたったくチェックされたせん。

しかし、最終決定は他の倚くの芁因に䟝存するため、䞀般的なアドバむスをするこずは非垞に困難です。 デフォルトでは、未定矩の動䜜に枛らし、確認のみを䜿甚しようずしたす。 暙準ラむブラリがoperator[]およびat()行うように、䞡方のオプションを提䟛するこずが掚奚される堎合がありたす。

堎合によっおは、これは間違いかもしれたせん。

std::exception階局に぀いお


修正可胜な゚ラヌを凊理するための戊略ずしお䟋倖を遞択した堎合は、新しいクラスを䜜成し、暙準ラむブラリの䟋倖クラスの1぀から継承するこずをお勧めしたす。

これらの4぀のクラスのうち1぀だけを継承するこずをお勧めしたす。


暙準ラむブラリは、論理゚ラヌ぀たり、プログラマヌずランタむム゚ラヌを分離するこずに泚意しおください。 ランタむム゚ラヌは、システム゚ラヌよりも広い定矩です。 「プログラムの実行䞭にのみ怜出される゚ラヌ」に぀いお説明したす。 この蚀葉遣いはあたり有益ではありたせん。 個人的には、プログラムの゚ラヌだけでなく、ナヌザヌの過倱によっおも発生する可胜性のある䞍良パラメヌタヌに䜿甚したす。 ただし、これは呌び出しスタックの奥深くでのみ定矩できたす。 たずえば、 暙準圢匏のコメントの圢匏が䞍適切な堎合、 std::runtime_error起因するstd::runtime_error䟋倖が発生しstd::runtime_error 。 その埌、適切なレベルでキャッチされ、ログに蚘録されたす。 しかし、 std::logic_error以倖ではこのクラスを䜿甚したせん。

たずめるず


゚ラヌを凊理するには2぀の方法がありたす。


謝蟞は、デバッグモヌドでのみ、臎呜的な゚ラヌ凊理戊略の特別な皮類です。

゚ラヌには3぀の䞻な原因があり、それぞれに特別なアプロヌチが必芁です。


C ++での柔軟な゚ラヌ凊理技術


時には䜕かが機胜しないこずがありたす。 ナヌザヌが無効な圢匏でデヌタを入力するず、ファむルが怜出されず、ネットワヌク接続が倱敗し、システムのメモリが䞍足したす。 これらはすべお゚ラヌであり、凊理する必芁がありたす。

これは、高レベル関数で比范的簡単に実行できたす。 䜕かがうたくいかなかった理由を正確に知っおおり、それに応じお察凊できたす。 しかし、䜎レベル関数の堎合、すべおがそれほど単玔ではありたせん。 圌らは䜕が悪いのかを知らず、倱敗の事実のみを知っおおり、それを圌らに電話した人に報告しなければなりたせん。

C ++には、゚ラヌ戻りコヌドず䟋倖ずいう2぀の䞻なアプロヌチがありたす。 今日、䟋倖の䜿甚が広たっおいたす。 しかし、さたざたな理由で、䜿甚できない/䜿甚できないず思う/䜿甚したくないず考える人もいたす。

私は味方したせん。 代わりに、䞡方のアプロヌチの支持者を満足させるテクニックを説明したす。 特に、この手法はラむブラリ開発者に圹立ちたす。

問題


私はfoonathan / memoryプロゞェクトに取り組んでいたす。 この゜リュヌションはさたざたなアロケヌタヌクラスを提䟛するので、䟋ずしお割り圓お関数の構造を芋おみたしょう。

簡単にするために、 malloc()たす。 割り圓おられたメモリぞのポむンタを返したす。 メモリ割り圓おが倱敗するず、 nullptr 、぀たりNULL 、぀たり誀った倀がNULLたす。

この゜リュヌションには欠点がありたす malloc() すべおの呌び出しをチェックする必芁がありたす。 これを忘れた堎合は、存圚しないメモリを割り圓おおください。 さらに、本質的に゚ラヌコヌドは掚移的です。゚ラヌコヌドを返すこずができる関数を呌び出し、それを無芖したり凊理したりできない堎合は、゚ラヌコヌドも返す必芁がありたす。

これにより、通垞のコヌド分岐ず誀ったコヌド分岐が亀互に発生する状況になりたす。 その堎合、䟋倖がより適切な゜リュヌションになりたす。 それらのおかげで、必芁なずきにだけ゚ラヌを凊理できたす。そうでない堎合は、呌び出し元に゚ラヌを返すのは非垞に静かです。

これは欠点ずみなすこずができたす。

しかし、そのような状況では、䟋倖には非垞に倧きな利点もありたす。メモリ割り圓お関数は有効なメモリを返すか、たったく䜕も返したせん。 これはオヌルオアナッシング関数であり、戻り倀は垞に有効です。 これは、Scott Mayerの原則によるず、「 むンタヌフェむスを誀っお䜿いにくくし、正しく䜿いやすいようにする 」ずいう有甚な結果です。

䞊蚘を考えるず、゚ラヌ凊理メカニズムずしお䟋倖を䜿甚する必芁があるず䞻匵できたす。 この意芋は、私を含むほずんどのC ++開発者によっお共有されおいたす。 しかし、私が行っおいるプロゞェクトは、メモリ割り圓おツヌルを提䟛するラむブラリであり、リアルタむムアプリケヌション向けに蚭蚈されおいたす。 そのようなアプリケヌションのほずんどの開発者特にigrodelovにずっお、䟋倖の䜿甚は䟋倖です。

パン探偵。

この開発者グルヌプを尊重するには、私のラむブラリヌが䟋倖なく行う方が良いです。 しかし、私ず他の倚くの人はそれらの優雅さず゚ラヌ凊理の容易さのためにそれらを奜むので、他の開発者のために、私のラむブラリは䟋倖を䜿うほうが良いです。

それではどうしたすか

理想的な゜リュヌション䟋倖を自由に有効および無効にする機胜。 ただし、䟋倖の性質を考えるず、゚ラヌコヌドず亀換するこずはできたせん。内郚゚ラヌチェックコヌドがないためです。すべおの内郚コヌドは、䟋倖が透過的であるずいう前提に基づいおいたす。 たた、゚ラヌコヌドが内郚で䜿甚され、䟋倖に倉換されたずしおも、䟋倖の利点のほずんどが奪われおしたいたす。

幞いなこずに、メモリ䞍足゚ラヌが発生したずきに䜕をするかを刀断できたす。ほずんどの堎合、このむベントを蚘録しおプログラムを䞭断したす。 そのような状況では、䟋倖は単に、プログラムを蚘録しお䞭断する別のコヌドに制埡を枡す方法です。 しかし、制埡を移すための叀くお効率的な方法がありたす関数ポむンタヌ、぀たりハンドラヌ関数です。

䟋倖を有効にしおいる堎合は、それらをスロヌしたす。 それ以倖の堎合は、ハンドラヌ関数を呌び出しおからプログラムを終了したす。 これにより、圹に立たない関数が機胜しなくなり、プログラムを正垞に実行し続けるこずができたす。 䞭断しない堎合、関数の必須の事埌条件に違反したす。垞に有効なポむンタヌを返したす。 実際、この条件が満たされるず、別のコヌドの䜜業を構築できたす。実際、これは正垞な動䜜です。

私はこのアプロヌチを䟋倖凊理ず呌び、メモリを操䜜するずきにそれを守りたす。

解決策1䟋倖ハンドラヌ


最も䞀般的な動䜜が「ログ蚘録ず䞭止」である堎合の条件で゚ラヌを凊理する必芁がある堎合は、䟋倖ハンドラヌを䜿甚できたす。 これは、䟋倖オブゞェクトをスロヌする代わりに呌び出されるハンドラヌ関数です。 既存のコヌドであっおも、実装は非垞に簡単です。 これを行うには、䟋倖クラスに凊理コントロヌルを配眮し、マクロでthrow匏をラップしたす。

最初に、クラスを远加し、ハンドラ関数を構成し、堎合によっおはリク゚ストする関数を远加したす。 暙準ラむブラリがstd::new_handler凊理するのず同じ方法でこれを行うこずをお勧めしstd::new_handler 。

 class my_fatal_error { public: //  ,      ,   , //       using handler = void(*)( ... ); //  - handler set_handler(handler h); //    handler get_handler(); ... //   }; 

これは䟋倖クラスのスコヌプ内にあるため、特別な方法で名前を付ける必芁はありたせん。 たあ、それは私たちにずっお簡単です。

䟋倖が有効な堎合、条件付きコンパむルを䜿甚しおハンドラヌを削陀できたす。 必芁に応じお、必芁な機胜を提䟛する通垞の混合クラスmixinクラスも䜜成したす。

䟋倖コンストラクタヌぱレガントです。珟圚のハンドラヌ関数を呌び出し、パラメヌタヌから必芁な匕数を枡したす。 そしお、その埌のthrowマクロず結合したす。

 If```cpp #if EXCEPTIONS #define THROW(Ex) throw (Ex) #else #define THROW(Ex) (Ex), std::abort() #endif 

 >   throw   [foonathan/compatiblity](https://github.com/foonathan/compatibility).     : ```cpp THROW(my_fatal_error(...)) 

䟋倖サポヌトを有効にしおいる堎合、䟋倖オブゞェクトが䜜成されおスロヌされたす。すべおが通垞どおりです。 しかし、サポヌトがオフになっおいる堎合、ずにかく䟋倖オブゞェクトが䜜成されたす-これは重芁です-その埌のみstd::abort()呌び出されたす。 たた、コンストラクタヌはハンドラヌ関数を呌び出すため、必芁に応じお機胜したす。゚ラヌログのチュヌニングポむントを取埗したす。 コンストラクタヌの埌のstd::abort()の呌び出しのおかげで、ナヌザヌは事埌条件を砎るこずができたせん。

メモリを操䜜するずき、䟋倖が有効になっおいるずき、䟋倖がスロヌされたずきに呌び出されるハンドラもありたす。

したがっお、この手法を䜿甚するず、䟋倖をオフにしおも、ある皋床のカスタマむズを匕き続き䜿甚できたす。 , , , . , , .

?


. ?

— . , , . , .
: . , . , , , — .

, , .

. :

 void* try_malloc(..., int &error_code) noexcept; void* malloc(...); 

nullptr error_code . nullptr , . , :

 void* malloc(...) { auto error_code = 0; auto res = try_malloc(..., error_code); if (!res) throw malloc_error(error_code); return res; } 

, , . . , , (overload) .

, . , , . , .

2:


, . , .

, . — nullptr , — , .

, errno - GetLastError() !

, std::optional - .

(exception overload) — — , . , .

std::system_error


++ 11.

(non-portable) std::error_code , . , std::error_condition . . , std::error_code . : std::system_error . std::error_code .

, -. — — , .

, . .

std::expected


, , , . , — .

!

№ 4109 : std::expected . , . :

 std::expected<void*, std::error_code> try_malloc(...); 

std::expected -null , — std::error_code . . std::expected .

おわりに


, . : , — .

— . , , callback, . , . , . .

— , , . , , . : .

Source: https://habr.com/ru/post/J322416/


All Articles