例外処理

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

計算する、コンピュータ・プログラミング例外処理が発生に応答する処理である例外特別な処理を必要とする異常なまたは例外条件- -中に実行プログラム。一般に、例外は通常の実行フローを中断し、事前に登録された例外ハンドラーを実行します。これがどのように行われるかの詳細は、それがハードウェアまたはソフトウェアの例外であるかどうか、およびソフトウェアの例外がどのように実装されているかによって異なります。例外処理が提供されている場合は、専用のプログラミング言語によって容易になります。構築物、のようなハードウェア機構割り込み、またはオペレーティング・システム(OS)プロセス間通信のような(IPC)設備信号。一部の例外、特にハードウェアの例外は、実行が中断された場所から再開できるほど適切に処理される場合があります。

ソフトウェアで例外処理に対する代替的なアプローチは、エラーチェックである[1]偶発用後明示的なチェックを通常のプログラムの流れを維持する特殊な使用報告戻り値、補助グローバル変数などのC 'エラー番号を、またはポイント・ステータス・フラグをフローティング。例外的なケースを先制的にフィルタリングする入力検証もアプローチです。

ハードウェアで

ハードウェア例外メカニズムはCPUによって処理されます。これは、たとえばエラー検出をサポートし、プログラムフローをエラー処理サービスルーチンにリダイレクトすることを目的としています。例外が保存される前の状態(スタックなど)。[2]

ハードウェア例外処理/トラップ:IEEE754浮動小数点

IEEE 754 浮動小数点ハードウェア標準での例外処理は、一般に例外条件を指し、「特定のオペランドでの操作がすべての妥当なアプリケーションに適した結果をもたらさない場合に発生するイベント。その操作は1つ以上の例外を通知する場合があります。デフォルトを呼び出すか、明示的に要求された場合は、言語定義の代替処理を呼び出します。」

デフォルトでは、IEEE 754例外は再開可能であり、さまざまな例外を事前定義された値に置き換えることで処理されます。たとえば、ゼロ除算例外を無限大にし、後で例外が発生したかどうかを確認するためのステータスフラグ提供します一般的なC99プログラミング言語参照してください)。IEEE 754例外の処理例)。ステータスフラグの使用によって可能になる例外処理スタイルには、次のものが含まれます。最初に、高速で直接的な実装を使用して式を計算します。ステータスフラグをテストして失敗したかどうかを確認します。次に、必要に応じて、より低速で数値的に堅牢な実装を呼び出します。[3]

IEEE 754標準では、「トラッピング」という用語を使用して、例外条件でのユーザー提供の例外処理ルーチンの呼び出しを指します。これは、標準のオプション機能です。この規格では、可除特異点を簡潔に処理するために、値のデフォルト以外の事前置換とそれに続く再開の実装など、このためのいくつかの使用シナリオを推奨しています[3] [4] [5]

デフォルト値の事前置換後の再開のデフォルトのIEEE754例外処理動作は、数値例外に対するプログラム制御のフローの変更に固有のリスクを回避します。たとえば、1996年にAriane 5(Flight 501)の初飛行は、算術エラー(この場合は64ビット浮動小数点)で計算を中止するというAdaプログラミング言語の例外処理ポリシーが原因で壊滅的な爆発に終わりました。16ビット整数への変換オーバーフロー[4]Ariane Flight 501の場合、プログラマーは、搭載コンピューターの計算上の制約に関する懸念から、7つの重要な変数のうち4つだけをオーバーフローから保護し、仮定が正しいAriane4のコード再利用したため、3つの保護されていない変数[6] William Kahanよると、ソフトウェアを中止させる64ビットから16ビットへの変換のオーバーフローが発生したため、デフォルトの置換のIEEE 754例外処理ポリシーが使用されていれば、501便の損失は回避されたはずです。 Ariane5では完全に不要であることが判明したコードの一部。[4]クラッシュに関する公式レポート(ジャック=ルイ・リオンが率いる調査委員会が実施)は、「アリアン5の開発における根本的なテーマは、ランダムな故障軽減への偏見です。慣性航法システムのサプライヤー(SRI)は、与えられた仕様に従っているだけであり、例外が検出された場合はプロセッサを停止することが規定されていました。発生した例外は、ランダムな障害ではなく、設計エラーによるものでした。例外は検出されましたが、ソフトウェアに障害があることが示されるまでソフトウェアは正しいと見なされるべきであるという見解がとられていたため、不適切に処理されました。 [...]失敗は体系的なソフトウェア設計エラーが原因でしたが、このタイプの問題を軽減するメカニズムを導入することができます。たとえば、SRI内のコンピューターは、必要な態度の最良の見積もりを提供し続けることができたはずです。情報。ミッションクリティカルな機器の処理中にプロセッサを停止させるために、ソフトウェアの例外を許可する必要がある、または要求する必要があるという懸念の理由があります。実際、同じソフトウェアが両方のSRIユニットで実行されるため、適切なソフトウェア機能が失われることは危険です。アリアン501の場合、これにより、2つのまだ健全な重要な機器ユニットのスイッチがオフになりました。」[7]

処理の観点からは、ハードウェア割り込みは再開可能な例外に似ていますが、通常、ユーザープログラムの制御フローとは関係ありません

オペレーティングシステムが提供する例外処理機能

Unixライクな オペレーティングシステムはIPCを介してプログラムの例外を処理するための機能を提供します。通常、プロセスの実行によって引き起こされた割り込みは、オペレーティングシステムの割り込みサービスルーチンによって処理され、オペレーティングシステムは、オペレーティングシステムに、呼び出される信号ハンドラを登録するように要求した可能性のある信号をそのプロセスに送信する場合があります。信号が発生したとき、またはオペレーティングシステムにデフォルトのアクション(プログラムの終了など)を実行させます。典型的な例は、SIGSEGVSIGBUSSIGILL、およびSIGFPEです。

OS / 360や後継オペレーティングシステムなどの他のオペレーティングシステムでは、IPCの代わりに、またはIPCに加えて異なるアプローチを使用する場合があります。

ソフトウェアで

ソフトウェアの例外処理とソフトウェアツールによって提供されるサポートは、ハードウェアでの例外処理によって理解されるものとは多少異なりますが、同様の概念が含まれています。例外処理のための言語メカニズムをプログラミングでは、用語の例外は、典型的には、例外条件に関する情報を記憶するデータ構造を示すために特定の意味で使用されています。制御を転送したり、例外を発生させたりするメカニズムの1つは、スローと呼ばれます。例外がスローされると言われています実行は「キャッチ」に転送されます。

ルーチンの作成者の観点から、例外を発生させることは、ルーチンが正常に実行できなかったことを通知するための便利な方法です。たとえば、入力引数が無効な場合(たとえば、値が関数のドメインにある場合)。または、依存しているリソースが利用できない場合(ファイルの欠落、ハードディスクエラー、メモリ不足エラーなど)、またはルーチンが特別な処理を必要とする通常の状態(注意、ファイルの終わりなど)を検出した場合。 。例外のないシステムでは、ルーチンはいくつかの特別なエラーコードを返す必要があります。ただし、これは、ルーチンのユーザーが通常の戻り値と誤った戻り値を区別するために追加のコードを記述する必要がある半述語の問題によって複雑になることがあります。

プログラミング言語は、例外とは何かという概念が大きく異なります。現代の言語は大きく2つのグループに分けることができます:[8]

  • 例外がフロー制御構造として使用されるように設計されている言語:Ada、Modula-3、ML、OCaml、PL / I、Python、およびRubyはこのカテゴリーに分類されます。
  • 例外が異常で予測不可能な誤った状況を処理するためにのみ使用される言語:C ++、[9] Java、[10] C#、Common Lisp、Eiffel、およびModula-2。

Kiniryはまた、「言語設計は例外の使用に部分的にのみ影響し、その結果、システム実行中の部分的および全体的な障害を処理する方法に影響を与えます。他の主要な影響は、通常はコアライブラリでの使用例と技術的なコード例です。書籍、雑誌記事、オンラインディスカッションフォーラム、および組織のコード標準。」[8]

例外処理戦略を検討する場合、現在のアプリケーションは多くの設計上の課題に直面しています。特に最新のエンタープライズレベルのアプリケーションでは、例外はプロセスの境界とマシンの境界を越えなければならないことがよくあります。確実な例外処理戦略の設計の一部は、プロセスのソフトウェア部分で経済的に処理できないレベルまでプロセスが失敗したことを認識することです。[11]

歴史

1960年代と1970年代Lisp開発されたソフトウェア例外処理。これはLISP1.5(1962)で発生し、プログラムを終了したりデバッガーに入ったりする代わりに、エラーの場合に返されるキーワードによって例外がキャッチされました[12]エラー調達がで導入されたのMacLispを経て1960年代後半にキーワード。[12]これは、急速にエラー上げるが、非ローカル制御フローのためだけでなく使用し、従って、2つの新しいキーワードによって増大し、そして(1972のMacLisp年6月)、予約及びERRSETNILERRCATCHTHROWERRSETERRエラー処理用。現在一般的に「最終的に」と呼ばれているクリーンアップ動作は、1970年代半ばから後半にかけてNIL(New Implementation of LISP)に導入されましたUNWIND-PROTECT[13]これは、それまでに採用されたCommon Lispの。これと同時代のdynamic-windSchemeは、クロージャの例外を処理していました。構造化例外処理に関する最初の論文は、Goodenough(1975a)Goodenough(1975b)でした。[14]例外処理は、1980年代以降、多くのプログラミング言語で広く採用されました。

PL / Iは動的スコープの例外を使用しましたが、最近の言語では字句スコープの例外を使用しています。PL / I例外処理には、エラーではないイベント(注意、ファイルの終わり、リストされた変数の変更など)が含まれていました。最近のいくつかの言語はエラー以外の例外をサポートしていますが、それらの使用は一般的ではありません。[要出典]

元々、ソフトウェア例外処理には、ほとんどのハードウェア例外のような再開可能な例外(再開セマンティクス)と再開不可能な例外(終了セマンティクス)の両方が含まれていました。ただし、再開セマンティクスは1970年代と1980年代には実際には効果がないと見なされ(以下に引用するC ++標準化の説明を参照)[15]、Common Lisp、Dylan、PL / Iなどのプログラミング言語によって提供されていますが、現在は一般的に使用されていません。

終了セマンティクス

現代の言語の例外処理メカニズムは、通常は再開可能であるハードウェア例外とは対照的に、通常は再開不可能です(「終了セマンティクス」)。これは、どちらかの決定を支持する理論的および設計上の議論があるため、両方を使用した経験に基づいています。これらは、1989年から1991年のC ++標準化の議論の中で広く議論され、その結果、終了セマンティクスの決定的な決定がなされました。[15] C ++メカニズムのそのような設計の理論的根拠について、Stroustrupは次のよう述べています。

[A] 1991年11月のパロアルト[C ++標準化]会議で、個人的な経験とJim Mitchell(Sunから、以前はXerox PARCから)のデータの両方に裏打ちされた終了セマンティクスの議論の見事な要約を聞きましたジムは、20年間にわたって半ダースの言語で例外処理を使用し、XeroxのCedar / Mesaシステムの主要な設計者および実装者の1人として再開セマンティクスの初期の支持者でした彼のメッセージは

「再開よりも終了が優先されます。これは意見の問題ではなく、長年の経験の問題です。再開は誘惑的ですが、有効ではありません。」

彼は、いくつかのオペレーティングシステムの経験でこの声明を支持しました。主な例はCedar / Mesaでした。これは、再開が好きで使用した人々によって書かれましたが、10年間使用した後、50万回線システムに残った再開の使用は1つだけでした。これは、コンテキスト調査でした。このようなコンテキスト照会では再開は実際には必要なかったため、再開を削除し、システムのその部分で大幅な速度の向上が見られました。再開が使用されたすべてのケースで、10年以上にわたって問題が発生し、より適切な設計がそれに取って代わりました。基本的に、再開を使用するたびに、別々のレベルの抽象化をばらばらに保つことができませんでした。[14]

批評

例外処理の安全性に関する対照的な見解は、1980年にTony Hoareによって与えられAdaプログラミング言語は「...多数の機能と表記規則があり、それらの多くは不要であり、例外処理のように、危険です。[...]現在の状態のこの言語を信頼性が重要なアプリケーションで使用することを許可しないでください[...]。プログラミング言語エラーの結果として次のロケットが迷うことは探索的ではない可能性があります金星への無害な旅の宇宙ロケット:それは私たち自身の都市の1つで爆発している核弾頭かもしれません。」[16]

例外処理は、特に例外の複数のソースがある場合、ソフトウェアで正しく処理されないことがよくあります。500万行のJavaコードのデータフロー分析で、1300を超える例外処理の欠陥が見つかりました。[17] 他の人による複数の先行研究(1999–2004)と彼ら自身の結果を引用して、WeimerとNeculaは、例外を伴う重大な問題は、「プログラマーが推論するのが難しい隠れた制御フローパスを作成する」ことであると書いた。[17] :8:27 

Goは当初、例外処理を明示的に省略してリリースされ、開発者は制御フローを難読化したと主張しました[18]その後、例外のようなpanic/recoverメカニズムが言語に追加されました。これは、Goの作成者が、プロセス全体を停止する必要がある回復不能なエラーにのみ使用することを推奨しています。[19] [20] [21] [22]

構造化されていないフローとしての例外は、リソースリークミューテックスによってロックされたセクションをエスケープしたり、ファイルを一時的に開いたままにしているセクションなど)や一貫性のない状態のリスクを高めます。例外が存在する場合のリソース管理はさまざまな手法があります。最も一般的なのは、disposeパターンfinally、制御がコードのセクションを終了するときにリソースを自動的に解放する何らかの形式のアンワインド保護(句など)を組み合わせたものです。

プログラミング言語での例外サポート

多くのコンピューター言語には、例外と例外処理のサポートが組み込まれています。これには、ActionScriptAdaBlitzMaxC ++C#ClojureCOBOLDECMAScriptEiffelJavaMLNext Generation ShellObject PascalDelphiFree Pascalなど)、PowerBuilderObjective-Cが含まれますOCamlPHP(バージョン5以降)、PL / IPL / SQLPrologPythonREALbasicRubyScalaSeed7SmalltalkTclVisual Prolog、およびほとんどの.NET言語。これらの言語では通常、例外処理を再開することはできません。例外がスローされると、プログラムは例外ハンドラーが見つかるまで関数呼び出しスタック検索します

一部の言語では、この検索が進むにつれてスタック巻き戻す必要があります。関数の場合である、fは、ハンドラを含む H例外のためにEを、関数呼び出しG順番に関数呼び出し、時間、および例外Eがで発生する時間、次いで、関数H及びGが終了してもよく、HF処理するEを

この巻き戻しなしの例外処理言語はCommon Lispのそのとコンディションシステム、PL / IとSmalltalkの。すべて例外ハンドラーを呼び出し、スタックを巻き戻しません。ただし、PL / Iでは、「ONユニット」(例外ハンドラー)がONユニットからGOTOを実行すると、スタックが巻き戻されます。例外ハンドラーには、計算を再開する、再開する、または巻き戻すオプションがあります。これにより、プログラムはエラーが発生した場所とまったく同じ場所で計算を続行したり(たとえば、以前に欠落していたファイルが利用可能になった場合)、例外処理メカニズムに加えて通知、ロギング、クエリ、および流動変数を実装できます(実行済み) Smalltalkで)。スタックレス実装Mythryl プログラミング言語は、スタックを巻き戻すことなく、一定時間の例外処理をサポートします。

構文上のわずかな違いを除いて、使用されている例外処理スタイルは2つだけです。最も一般的なスタイルでは、例外は、例外オブジェクト(JavaやObject Pascalなど)または特別な拡張可能な列挙型の値(AdaやSMLthrowなどraise)を使用した特別なステートメント(またはによって開始されます。(マーカー句と例外ハンドラの開始のためのスコープtryなどや言語のブロックスターターbegin第ハンドラ句(開始および終了)catchexceptrescue)。いくつかのハンドラー句を続けることができ、それぞれが処理する例外タイプと例外オブジェクトに使用する名前を指定できます。

一部の言語elseでは、ハンドラーのスコープの終わりに達する前に例外が発生しなかった場合に使用される句(も許可されています。

より一般的なのは、例外が発生したかどうかに関係なく実行される関連句(finallyまたはensure)で、通常は例外処理ブロックの本体内で取得されたリソースを解放します。それが奨励以来、注目すべきは、C ++は、この構造を提供していないリソースの取得された初期使用してリソースを解放します(RAII)技術デストラクタを

全体として、例外処理コードは次のようになります(Javaのような擬似コード)。

 { 
    line  =  consoleを試してくださいreadLine ();

    もし ライン長さ() ==  0  {
        スロー 新しい EmptyLineExceptionを"コンソールから読み込んだ行が空でした!" ); 
    }

    コンソールprintLine "Hello%s!"   line );
    コンソールprintLine "プログラムは正常に実行されました。" ); 
} 
catch  EmptyLineException  e  {
    コンソールprintLine "こんにちは!" ); 
} 
catch  Exception  e  { 
    console プリントライン"エラー:"  +  E メッセージ()); 
}
最後に {
    コンソールprintLine "プログラムは現在終了しています。" ); 
}

マイナーなバリエーションとして、一部の言語は、例外のクラスを内部的に処理する単一のハンドラー句を使用します。

WestleyWeimerGeorgeNeculaよる2008年の論文によると、Javatry...finallyブロックの構文は、ソフトウェアの欠陥の原因となっています。メソッドが3〜5個のリソースの取得と解放を処理する必要がある場合、プログラマーは、これが正しい解決策であっても、読みやすさの懸念から、明らかに十分なブロックをネストすることを望んでいません。複数のリソースを処理する場合でも単一のtry...finallyブロックを使用することは可能ですが、これには番兵値を正しく使用する必要があります。これは、このタイプの問題のもう1つの一般的なバグの原因です。[17] :8:6-8:7 の意味についてtry... catch...finally一般に、WeimerとNeculaは、「try-catch-finallyは概念的に単純ですが、言語仕様[Gosling etal。1996]で最も複雑な実行記述があり、その中にネストされた「if」の4つのレベルが必要です。公式の英語の説明。要するに、プログラマーが見落としがちなコーナーケース多数含まれています。」[17] :8:13–8:14 

が、Cは「例外処理」をエラーチェックの様々な手段をサポートしていますが、一般的にサポートするために考えられていない、標準ライブラリ関数が例外セマンティクスを実装するために使用することができます。 setjmplongjmp

Perlには、構造化例外処理のオプションのサポートがあります。

例外処理に対するPythonのサポートは広く普及しており、一貫性があります。tryandexceptキーワードを使用せずに堅牢なPythonプログラムを作成することは困難です。[要出典]

UI階層での例外処理

ReactVueなどの最近のフロントエンドWebフレームワークでは、エラーがコードの実行時にコールスタックに伝播するのと同様の方法で、エラーがUIコンポーネント階層に伝播するエラー処理メカニズムが導入されています。[23] [24]ここで、エラー境界メカニズムは、典型的なトライキャッチメカニズムの類似物として機能します。したがって、コンポーネントは、その子コンポーネントからのエラーがキャッチされて処理され、親コンポーネントに伝播されないようにすることができます。

たとえば、Vueでは、コンポーネントは実装することでエラーをキャッチします errorCaptured

Vue component 'parent'  { 
    template  '<div> <slot> </ slot> </ div>' 
    errorCaptured  err  vm  info  =>  alert 'エラーが発生しました' ); 
})
Vue コンポーネント'子'  {
    テンプレート '<div> {{cause_error()}} </ div>' 
})

マークアップでこのように使用する場合:

<> 
    <> </> 
</>

子コンポーネントによって生成されたエラーは、親コンポーネントによってキャッチおよび処理されます。[25]

例外処理の実装

プログラミング言語での例外処理の実装には、通常、コードジェネレーターとコンパイラーに付属するランタイムシステムの両方からのかなりの量のサポートが含まれます。 (元のC ++コンパイラであるCfrontの有効期間を終了したのは、C ++への例外処理の追加でした[26])2つのスキームが最も一般的です。最初の動的登録は、例外処理の観点からプログラムの状態に関する構造を継続的に更新するコードを生成します。[27] 通常、これによりスタックフレームレイアウトに新しい要素が追加されますそのフレームに関連付けられた関数またはメソッドに使用できるハンドラーを認識しています。例外がスローされた場合、レイアウト内のポインターがランタイムを適切なハンドラーコードに転送します。このアプローチはスペースの点でコンパクトですが、フレームの開始と終了に実行オーバーヘッドが追加されます。これは、多くのAda実装で一般的に使用されていました。たとえば、他の多くの言語機能で複雑な生成とランタイムのサポートがすでに必要でした。動的登録は、定義がかなり簡単であるため、正当性の証明に適しています。[28]

2番目のスキーム、および多くの実稼働品質のC ++コンパイラーに実装されているスキームは、テーブル駆動型のアプローチです。これにより、コンパイル時リンク時に静的テーブルが作成され、例外処理に関してプログラムカウンターの範囲がプログラムの状態に関連付けられます。[29] 次に、例外がスローされた場合、ランタイムシステムはテーブル内の現在の命令位置を検索し、実行中のハンドラーと実行する必要があるものを判別します。このアプローチにより、例外がスローされない場合のエグゼクティブのオーバーヘッドが最小限に抑えられます。これはある程度のスペースを犠牲にして発生しますが、このスペースは、例外が実際にスローされるまでロードまたは再配置されない読み取り専用の専用データセクションに割り当てることができます。[30] この2番目のアプローチは、スレッドセーフを達成するという点でも優れています[要出典]

他の定義および実装スキームも提案されています。[31]メタプログラミングをサポートする言語の場合、オーバーヘッドをまったく含まないアプローチ(すでに存在するリフレクションのサポートを超えて)が進んでいます。[32]

契約による設計に基づく例外処理

例外の別の見方は、契約による設計の原則に基づいており、特にEiffel言語によってサポートされていますアイデアは、「正常」および「異常」な動作を正確に定義することにより、例外処理のより厳密な基礎を提供することです。具体的には、このアプローチは2つの概念に基づいています。

  • 失敗:オペレーションが契約を履行できないこと。たとえば、加算によって算術オーバーフローが発生する場合があります(数学的な合計の適切な近似値を計算するという契約を満たしていません)。または、ルーチンがその事後条件を満たさない場合があります。
  • 例外:ルーチンの実行中に発生する異常なイベント(そのルーチンは例外の受信者」です)。このような異常なイベントは、ルーチンによって呼び出された操作失敗起因します。

オブジェクト指向ソフトウェア構築でBertrandMeyerによって導入された「安全な例外処理の原則」は、例外が発生したときにルーチンが反応できる意味のある方法は2つだけであると考えています。

  • 失敗、または「組織化されたパニック」:ルーチンは、不変条件(これは「組織化された」部分)を再確立することによってオブジェクトの状態を修正し、次に失敗(パニック)して、呼び出し元で例外をトリガーします(異常なイベントは無視されません)。
  • 再試行:ルーチンは、通常、次の試行が成功する可能性が高くなるようにいくつかの値を変更した後、アルゴリズムを再試行します。

特に、単に例外を無視することは許可されていません。ブロックを再試行して正常に完了するか、例外を呼び出し元に伝播する必要があります。

これは、Eiffel構文で表現された例です。send_fast通常はルーチンがメッセージを送信するためのより良い方法であると想定していますが、失敗して例外がトリガーされる場合があります。その場合、アルゴリズムは次にを使用しますがsend_slow、失敗する頻度は低くなります。場合はsend_slow失敗し、ルーチンsend全体としては例外を取得するには、発信者を引き起こして、失敗するはずです。

センド M  MESSAGEは です
  -可能な場合はそうでない場合は、低速リンクを介して、高速リンクを通じてメートル送信します。
地元
  tried_fast  tried_slowは BOOLEANは
ない
  場合 tried_fastは 、その後
     tried_slow  := 
     send_slow  M 
  
     tried_fast  := 
     send_fast  メートル
  エンド
救助
  場合 ではない tried_slow その後、
     再試行
  エンド
エンドを

ブールローカル変数は、開始時にFalseに初期化されます。場合は send_fast失敗し、体(do句)が実行させる、再び実行されますsend_slow。この実行がsend_slow失敗した場合、rescue句は最後までno retryelsefinalに句なしif)で実行され、ルーチンの実行全体が失敗します。

このアプローチには、「正常」と「異常」のケースを明確に定義するメリットがあります。例外を引き起こす異常なケースは、ルーチンが契約を履行できないケースです。これは、役割の明確な配分を定義します。do条項(通常の本体)は、ルーチンの契約の達成または達成の試みを担当します。このrescue句は、コンテキストを再確立し、プロセスを再開することを担当します。これが成功する可能性がある場合は、実際の計算を実行することはできません。

Eiffelの例外にはかなり明確な哲学がありますが、Kiniry(2006)は、「言語定義の一部である例外はINTEGER値で表され、開発者定義の例外はSTRING値で表されるため、その実装を批判しています。は基本的な値であり、オブジェクトではありません。ヘルパールーチンで表現されるものを超える固有のセマンティクスはありません。これは、実際には表現のオーバーロードのために絶対に確実なものではありません(たとえば、同じ値の2つの整数を区別することはできません)。」[8]

キャッチされない例外

例外がスローされてキャッチされない場合(操作上、該当するハンドラーが指定されていない場合に例外がスローされます)、キャッチされなかった例外はランタイムによって処理されます。これを行うルーチンは、キャッチされなかった例外ハンドラ[33] [34]最も一般的なデフォルトの動作は、プログラムを終了し、コンソールにエラーメッセージを出力することです。これには通常、例外の文字列表現やスタックトレースなどのデバッグ情報が含まれます[33] [35] [36]これは、例外がランタイムに到達する前にキャッチするトップレベル(アプリケーションレベル)のハンドラー(たとえば、イベントループ内)を使用することで回避できることがよくあります。[33] [37]

キャッチされない例外が生じ得るにもかかわらず、ことに留意されたい番組(プログラム例外が捕捉されていない場合に、特にバック部分的に完了したトランザクションをロールしないことにより、正しくない、またはリソースを解放しなくてもよい)異常終了し、処理が正常に終了する(と仮定ランタイム(プログラムの実行を制御している)はプロセスの正常なシャットダウンを保証できるため、ランタイムは正しく機能します。

マルチスレッドプログラムでは、スレッドでキャッチされない例外が発生すると、プロセス全体ではなく、そのスレッドだけが終了する場合があります(スレッドレベルのハンドラーでキャッチされない例外は、トップレベルのハンドラーによってキャッチされます)。これはサーバーにとって特に重要です。たとえば、サーバー全体に影響を与えることなくサーブレット(独自のスレッドで実行)を終了できます。

このデフォルトのキャッチされない例外ハンドラーは、グローバルに、またはスレッドごとにオーバーライドできます。たとえば、キャッチされない例外の代替ロギングまたはエンドユーザーレポートを提供したり、キャッチされなかった例外が原因で終了したスレッドを再起動したりできます。たとえば、Javaでは、これは;を介して単一のスレッドに対して実行され、;を介しThread.setUncaughtExceptionHandlerてグローバルに実行されThread.setDefaultUncaughtExceptionHandlerます。Pythonでは、これはを変更することによって行われsys.excepthookます。

例外の静的チェック

チェックされた例外

Javaの設計者は、特別な例外のセットである[38]チェック例外[39]考案しましたメソッドが発生する可能性のあるチェックされた例外は、メソッドのシグネチャの一部ですたとえば、メソッドがをスローする可能性がある場合IOExceptionは、メソッドシグネチャでこの事実を明示的に宣言する必要があります。そうしないと、コンパイル時エラーが発生します。

ただし、Kiniry(2006)は、Javaのライブラリ(2006年の場合と同様)は、エラー報告へのアプローチに一貫性がないことが多いと述べています。関連するクラスの定数フィールド。」[8]

チェックされた例外は、OCamlプログラミング言語に存在する例外チェッカーに関連しています[40] OCamlの外部ツールは、非表示(つまり、構文アノテーションを必要としない)とオプション(つまり、例外をチェックせずにプログラムをコンパイルして実行することができますが、本番コードには推奨されません)の両方です。

CLUプログラミング言語は近いJavaは後で紹介したものとのインタフェースと機能を持っていました。関数は、その型にリストされている例外のみを発生させることができますが、呼び出された関数からのリーク例外はfailure、コンパイル時エラーを引き起こすのではなく、自動的に唯一の実行時例外に変換されます。その後、Modula-3にも同様の機能がありました。[41]これらの機能には、チェックされた例外の概念の中心となるコンパイル時チェックは含まれていません。また、(2006年現在)Java以外の主要なプログラミング言語には組み込まれていません。[42]

C ++プログラミング言語の初期のバージョンには、例外仕様と呼ばれる、チェックされた例外のためのオプションのメカニズムが含まれていました。デフォルトでは、どの関数も例外をスローできますが、これは、関数がスローする可能性のある例外を指定する関数シグネチャに追加されたによって制限される可能性があります。コンパイル時に例外仕様が適用されませんでした。違反すると、グローバル関数が呼び出されました。[43]throwstd::unexpected 空の例外指定を指定できます。これは、関数が例外をスローしないことを示しています。これは、例外処理が言語に追加されたときにデフォルトになりませんでした。これは、既存のコードの変更が多すぎ、他の言語で記述されたコードとの対話が妨げられ、プログラマーがローカルで非常に多くのハンドラーを記述したくなるためです。レベル。[43]ただし、空の例外仕様を明示的に使用すると、C ++コンパイラーは、関数で例外処理が行われる可能性がある場合に除外される重要なコードおよびスタックレイアウトの最適化を実行できます。[30] 一部のアナリストは、C ++での例外仕様の適切な使用を実現するのは難しいと考えていました。[44]この例外仕様の使用はC ++ 03に含まれ、2012 C ++言語標準(C ++ 11[45]非推奨になりC ++ 17の言語から削除されました。例外をスローしない関数をキーワードで指定できるようになりましたnoexcept

Javaとは対照的に、C#のような言語では、例外タイプの宣言は必要ありません。 HanspeterMössenböckによると、呼び出される(チェックされた)例外と呼び出されない(チェックされていない)例外を区別しないと、記述されたプログラムはより便利になりますが、キャッチされない例外はスタックトレース[46]しかし、Kiniry(2006)は、JavaのJDK(バージョン1.4.1)が多数の未チェックの例外をスローすることを指摘しています。 。 Kiniryはまた、「Javaプログラマーなら誰でも知っているように、try catch一般的なJavaアプリケーションのコードは、例外をチェックしていない他の言語での明示的な仮パラメータと戻り値のチェックに必要な同等のコードよりも大きい場合があります。実際、塹壕内のJavaプログラマーの間の一般的なコンセンサスは、チェックされた例外を処理することは、ドキュメントを書くこととほぼ同じくらい不快な作業であるということです。したがって、多くのプログラマーは、チェックされた例外を「再送信」したと報告しています。これにより、チェックされているが無視されている例外が多数発生します。」[8] Kiniryは、C#の開発者は、この種のユーザーエクスペリエンスの影響を受けているようであり、次の引用が(Eric Gunnerson経由で)彼らに起因していると述べています。

「小さなプログラムの調査は、例外仕様を要求することで開発者の生産性とコード品質の両方を向上させることができるという結論につながりますが、大規模なソフトウェアプロジェクトの経験は、生産性の低下とコード品質の向上がほとんどまたはまったくないという別の結果を示唆しています。」[8]

Anders Hejlsbergよると、設計グループでは、C#の言語機能として例外をチェックしないというかなり幅広い合意がありました。ヘルスバーグはインタビューで次のように説明した

「throws句は、少なくともJavaで実装されている方法では、必ずしも例外を処理する必要はありませんが、処理しない場合は、どの例外が通過する可能性があるかを正確に確認する必要があります。宣言された例外をキャッチするか、独自のthrows句に入れる必要があります。この要件を回避するために、人々はばかげたことをします。たとえば、すべてのメソッドを「例外をスロー」で装飾します。それは機能を完全に打ち負かし、プログラマーにもっとゴツゴツしたガンクを書かせただけです。それは誰の助けにもなりません。」[47]

使用状況に関する見解

チェックされた例外は、コンパイルに、特定のアプリケーション実行時発生する未処理の例外の発生率を減らすことができます。チェックされていない例外(JavaオブジェクトRuntimeExceptionやなどError)は未処理のままです。

ただし、チェックされた例外はthrows、実装の詳細を明らかにしてカプセル化を減らす広範な宣言を必要とするか、適切なハンドラーから正当な例外を隠す可能性のある、あまり考慮されていないブロックのコーディングを奨励する可能性があります。[要出典]時間の経過とともに 成長するコードベース検討してください。インターフェイスは、例外XおよびYをスローするように宣言される場合があります。新しいバージョンのコードでは、例外Zをスローする場合、新しいコードは以前の使用と互換性がなくなります。さらに、アダプターパターン付きtry/catch、コードの1つの本体がインターフェイスを宣言し、それが別のコードの本体によって実装されるため、コードをプラグインして最初のコードで呼び出すことができます。アダプタコードには、問題を説明するための豊富な例外セットがありますが、強制されます。インターフェイスで宣言された例外タイプを使用します。

スローされる可能性のあるすべての例外のスーパークラス宣言するか、呼び出されたメソッドの抽象化レベルに適した例外タイプを定義および宣言し[48]、下位レベルの例外をこれらにマッピングすることにより、宣言された例外の数を減らすことができます。タイプ。根本的な原因を保持するために、例外チェーンを使用しラップすることが望ましい。さらに、上記の変更されたインターフェイスの例では、呼び出し元のコードも変更する必要がある可能性があります。これは、ある意味で、メソッドがスローする可能性のある例外が、とにかくメソッドの暗黙的なインターフェイスの一部であるためです。

宣言を使用する、通常はJavaでのチェックを満たすのに十分です。これにはある程度の用途があるかもしれませんが、Oracleが推奨していないチェック例外メカニズムを本質的に回避します。[49]throws Exceptioncatch (Exception e)

チェックされていない例外タイプは、スコープの最も外側のレベルを除いて、通常は処理されるべきではありません。これらは多くの場合、リカバリを許可しないシナリオを表します。sはプログラミングの欠陥を反映することが多く[50]sは一般にリカバリ不可能なJVM障害を表します。チェック例外をサポートする言語でも、チェック例外の使用が適切でない場合があります。[51]RuntimeExceptionError

例外の動的チェック

例外処理ルーチンのポイントは、コードがエラー状態を処理できるようにすることです。例外処理ルーチンが十分に堅牢であることを確立するには、ソフトウェアのフォールトインジェクションミューテーションテストファジングとも呼ばれる)を介して作成できるような、さまざまな無効または予期しない入力をコードに提示する必要があります。テスト)。例外処理ルーチンを作成するのに最も難しいタイプのソフトウェアの1つは、プロトコルソフトウェアです。これは、関連する仕様に準拠していない入力を受け取るために、堅牢なプロトコル実装を準備する必要があるためです。

ソフトウェア開発ライフサイクルプロセス全体で意味のある回帰分析を確実に実行できるようにするには、例外処理テストを高度に自動化し、テストケースを科学的で反復可能な方法で生成する必要があります。このようなテストを実行する市販のシステムがいくつか存在します。

Java.NETなどのランタイムエンジン環境には、ランタイムエンジンに接続するツールがあり、対象の例外が発生するたびに、例外がスローされたときにメモリに存在していたデバッグ情報を記録します(コールスタックヒープ)。値)。これらのツールは、自動例外処理またはエラーインターセプトツールと呼ばれ、例外の「ルート原因」情報を提供します。

例外シンクロニシティ

チェックされた例外の概念にいくらか関連しているのは、例外の同期性です。同期例外は特定のプログラムステートメントで発生します非同期例外は事実上どこでも発生する可能性があります。[52] [53]したがって、コンパイラーは非同期例外処理を要求できません。また、プログラミングも困難です。自然に非同期のイベントの例には、Ctrl-C押してプログラムを中断したり、別の実行スレッドから「停止」や「一時停止」など信号を受信したりすることが含まれます。

プログラミング言語は通常、非同期性を制限することでこれに対処します。たとえば、Javaは、あるスレッドが別のスレッドを停止できるようにするために使用されたThreadDeath例外の使用を廃止しました。[54]代わりに、プログラムの適切な場所でのみ、または同期的に発生する半非同期の例外が発生する可能性があります。

条件システム

Common Lispのディランスモールトークが有する条件システム[55] 参照Common Lispのコンディションシステムの処理システム前述の例外を含みます)。これらの言語または環境では、条件の出現(Kent Pitmanによる「エラーの一般化」)は関数呼び出しを意味し、例外ハンドラーの後半でのみ、スタックを巻き戻す決定を下すことができます。

条件は、例外の一般化です。条件が発生すると、適切な条件ハンドラーが検索され、スタック順に選択されて、条件を処理します。エラーを表さない状態は、安全に完全に処理されない可能性があります。それらの唯一の目的は、ユーザーに向けてヒントや警告を広めることかもしれません。[56]

継続可能な例外

これは、例外処理のいわゆる再開モデル関連しています。このモデルでは、一部の例外は継続可能であると言われます。ハンドラーで修正アクションを実行した後、例外を通知した式に戻ることができます。条件システムは次のように一般化されます。重大でない条件(別名継続可能な例外のハンドラー内で、シグナリング式と条件ハンドラーの間にある事前定義された再始動ポイント(別名再始動にジャンプすることができます。再起動は、一部の字句環境で閉じられる関数であり、プログラマーは、条件ハンドラーを完全に終了する前、またはスタックを部分的に巻き戻す前に、この環境を修復できます。

例は、PL / IのENDPAGE条件です。ONユニットは、次のページのページトレーラー行とヘッダー行を書き込み、その後、中断されたコードの実行を再開するためにフォールスルーする場合があります。

ポリシーとは別のメカニズムを再起動し

さらに、条件処理により、メカニズムがポリシーから分離されます。再起動は、エラーから回復するためのさまざまな可能なメカニズムを提供しますが、特定の状況で適切なメカニズムを選択しないでください。これは、条件ハンドラーの領域であり、(上位レベルのコードに配置されているため)より広いビューにアクセスできます。

例:単一のsyslogファイルエントリを解析することを目的としたライブラリ関数があるとします。エントリの形式が正しくない場合、この関数は何をすべきですか?同じライブラリをさまざまな目的のプログラムに展開できるため、正しい答えは1つではありません。インタラクティブなログファイルブラウザでは、エントリを解析せずに返してユーザーに表示できるようにするのが適切な場合がありますが、自動ログ要約プログラムでは、エントリにnull値を指定するのが適切な場合があります。読み取り不可能なフィールドですが、不正な形式のエントリが多すぎると、エラーで中止されます。

つまり、この質問は、汎用ライブラリ関数には知られていない、プログラムのより広い目標に関してのみ答えることができます。それでも、エラーメッセージを表示して終了することが正しい答えになることはめったにありません。だからではなく、単にエラーで終了すると、関数があり再起動確立続ける-のためにするインスタンス、ログエントリをスキップし、欠損値をユーザに尋ねるために、読めないフィールドのデフォルトはnull値を提供するためにさまざまな方法を提供し、またはをスタックを巻き戻し、エラーメッセージを表示して処理を中止します。提供される再起動は、エラーからの回復に使用できるメカニズムを構成します。条件ハンドラーによる再起動の選択により、ポリシーが提供されます。

も参照してください

参考文献

  1. ^ 「「システムスレッド例外が処理されない」エラーを修正するためのガイド」whatt.org2016年9月25日にオリジナルからアーカイブされまし2019年1月8日取得
  2. ^ 「ハードウェア例外の検出」TEXASINSTRUMENTS。2011-11-24。アーカイブされたオリジナルの2013年11月10日に2012年10月5日取得
  3. ^ a b Xiaoye Li ; ジェームズ・デンメル(1994)。「例外処理による高速数値アルゴリズム、コンピュータでのIEEEトランザクション、43(8)」:983–992。 引用ジャーナルには|journal=ヘルプが必要です
  4. ^ a b c W.Kahan(2005年7月5日)。「∞/∞の前置換のデモンストレーション」(PDF)2012年3月10日のオリジナルからアーカイブ(PDF)
  5. ^ ジョンハウザー(1996)。「数値プログラムでの浮動小数点例外の処理、プログラミング言語およびシステムでのACMトランザクション18(2)」:139–174。 引用ジャーナルには|journal=ヘルプが必要です
  6. ^ 「Arianeのレッスン」www.irisa.fr2016年6月4日にオリジナルからアーカイブされました2018年5月5日取得
  7. ^ 「ARIANE5の失敗-完全なレポート」2014年4月26日にオリジナルからアーカイブされました2014年7月16日取得
  8. ^ a b c d e f Kiniry、JR(2006)。「JavaとEiffelの例外:例外の設計とアプリケーションにおける2つの極端な点」。例外処理技術の高度なトピックコンピュータサイエンスの講義ノート。4119pp。288–300。土井10.1007 / 11818502_16ISBN 978-3-540-37443-5
  9. ^ 「Stroustrup:C ++のスタイルとテクニックに関するFAQ」www.stroustrup.com2018年2月2日にオリジナルからアーカイブされました2018年5月5日取得
  10. ^ ブロッホ、ジョシュア(2008)。「項目57:例外的な状況でのみ例外を使用する」効果的なJava(第2版)アディソン-ウェスリー。NS。 241ISBN  978-0-321-35668-0
  11. ^ すべての例外が処理される、Jim Wilcox、「すべての例外が処理される」2015年3月18日にオリジナルからアーカイブされました取得した2014年12月8日を
  12. ^ a b Gabriel&Steele 2008、p。3.3。
  13. ^ ホワイト1979、p。194。
  14. ^ a b Stroustrup 1994、p。392。
  15. ^ a b Stroustrup 1994、16.6例外処理:再開と終了、390〜393ページ。
  16. ^ CARホア。「皇帝の古着」。1980年チューリング賞講演
  17. ^ a b c d ワイマー、W; ネキュラ、GC(2008)。「例外的な状況とプログラムの信頼性」(PDF)プログラミング言語とシステムに関するACMトランザクション30(2)。2015年9月23日のオリジナルからアーカイブ(PDF)
  18. ^ 「よくある質問」2017年5月3日にオリジナルからアーカイブされました2017年4月27日取得try-catch-finallyイディオムのように、例外を制御構造に結合すると、複雑なコードが生成されると考えられます。また、プログラマーは、ファイルを開かないなどの通常のエラーを例外としてラベル付けするように促される傾向があります。
  19. ^ ウェイバックマシンアーカイブされた2013-10-24のパニックと回復 、ウィキに行く
  20. ^ 「毎週のスナップショット履歴」golang.org2017年4月3日にオリジナルからアーカイブされました。
  21. ^ 「例外のようなメカニズムの提案」golang-nuts2010年3月25日取得した3月25日に2010
  22. ^ 「効果的な囲碁」golang.org2015年1月6日にオリジナルからアーカイブされました。
  23. ^ 「エラー境界」反応します。20181210日取得
  24. ^ 「Vue.jsAPI」Vue.js 20181210日取得
  25. ^ 「Vue.jsでのエラー処理」CatchJS 20181210日取得
  26. ^ Scott Meyers最も重要なC ++ソフトウェア... Wayback Machine20114月28日にアーカイブされたもの、2006年
  27. ^ D. Cameron、P。Faust、D。Lenkov、M。Mehta、「C ++例外処理の移植可能な実装」、 C ++会議の議事録(1992年8月) USENIX
  28. ^ Graham Hutton、Joel Wright、「ウェイバックマシン正しく アーカイブされた例外のコンパイル2014-09-11」。プログラム構築の数学に関する第7回国際会議の議事録、2004年。
  29. ^ Lajoie、Josée(1994年3月から4月)。「例外処理–ランタイムメカニズムのサポート」。C ++レポート6(3)。
  30. ^ a b Schilling、Jonathan L.(1998年8月)。「C ++例外処理の最適化」。SIGPLANの通知33(8):40–47。土井10.1145 /286385.286390S2CID 1522664 
  31. ^ 『アーカイブコピー』から。アーカイブされたオリジナルの2012年1月1日に。のRetrieved 2012-02-27CS1 maint: archived copy as title (link)"、インテルコーポレーション
  32. ^ M. Hof、H.Mössenböck、P。Pirkelbauer、「ウェイバックマシンアーカイブされた2016-03-03のメタプログラミングを使用したゼロオーバーヘッド例外処理 」、 Proceedings SOFSEM'97、1997年11月、コンピュータサイエンスの講義ノート1338、pp。 423-431。
  33. ^ a b c Mac開発者ライブラリ、「ウェイバックマシンで20163月4日にアーカイブされたキャッチされない例外
  34. ^ MSDN AppDomain.UnhandledExceptionイベント ウェイバックマシンで20163月4日にアーカイブされました
  35. ^ Pythonチュートリアル、「 8。 ウェイバックマシン2015年9月1日にアーカイブされたエラーと例外
  36. ^ 「Javaプラクティス->キャッチされない例外ハンドラーを提供する」www.javapractices.com2016年9月9日にオリジナルからアーカイブされました2018年5月5日取得
  37. ^ PyMOTW(Python Module Of The Week)、「ウェイバックマシンアーカイブされた2015-09-15の例外処理
  38. ^ 「GoogleAnswers:チェックされた例外の起源」2011年8月6日にオリジナルからアーカイブされました2011年12月15日取得
  39. ^ Java言語仕様、第11.2章。http://java.sun.com/docs/books/jls/third_edition/html/exceptions.html#11.2 アーカイブ2006年12月8日で、ウェイバックマシン
  40. ^ 「OcamlExc-ObjectiveCaml用のキャッチされていない例外アナライザー」Caml.inria.fr。2011年8月6日にオリジナルからアーカイブされました2011年12月15日取得
  41. ^ 「Modula-3-プロシージャタイプ」.cs.columbia.edu。1995-03-08。2008年5月9日にオリジナルからアーカイブされました2011年12月15日取得
  42. ^ 「ブルースエッケルのMindView、Inc:Javaにはチェック例外が必要ですか?」Mindview.net。2002年4月5日にオリジナルからアーカイブされまし2011年12月15日取得
  43. ^ B ビャーネ・ストロヴストルップC ++言語プログラミング第三版、アディソンウェズリー、1997年ISBN 0-201-88954-4をpp。375-380。 
  44. ^ Reeves、JW(1996年7月)。「例外仕様に関する10のガイドライン」。C ++レポート8(7)。
  45. ^ サッター、ハーブ(2010年3月3日)。「トリップレポート:2010年3月のISO C ++標準会議」2010年3月23日にオリジナルからアーカイブされました取得した3月24日に2010
  46. ^ Mössenböck、Hanspeter(2002-03-25)。「高度なC#:可変数のパラメーター」(PDF)http://ssw.jku.at/Teaching/Lectures/CSharp/Tutorial/:InstitutfürSystemsoftware、JohannesKeplerUniversitätLinz、FachbereichInformatikNS。32. 2011年9月20日のオリジナルからアーカイブ(PDF)取り出さ2011-08-05に
  47. ^ ビルベナーズ; ブルースエッケル(2003年8月18日)。「チェックされた例外の問題:アンダース・ヘルスバーグとの会話、パートII」NS。 22015年2月18日にオリジナルからアーカイブされました。
  48. ^ Bloch 2001:178 Bloch、Joshua(2001)。効果的なJavaプログラミング言語ガイドアディソン-ウェスリープロフェッショナル。ISBN 978-0-201-31005-4
  49. ^ 「例外の利点(Java™チュートリアル:エッセンシャルクラス:例外)」Download.oracle.com。2011年10月26日にオリジナルからアーカイブされました2011年12月15日取得
  50. ^ Bloch 2001:172
  51. ^ 「チェックされていない例外–論争(Java™チュートリアル:エッセンシャルクラス:例外)」Download.oracle.com。2011年11月17日にオリジナルからアーカイブされました2011年12月15日取得
  52. ^ 「Haskellの非同期例外-Marlow、Jones、Moran(ResearchIndex)」Citeseer.ist.psu.edu。2011年2月23日にオリジナルからアーカイブされました2011年12月15日取得
  53. ^ Pythonの安全な非同期例外。「アーカイブされたコピー」2006年8月30日にオリジナルからアーカイブされました取り出さ2006-12-07にCS1 maint: archived copy as title (link)
  54. ^ 「Javaスレッドプリミティブの非推奨」Java.sun.com。2009年4月26日にオリジナルからアーカイブされました2011年12月15日取得
  55. ^ どのような条件(例外)が実際にあるか(2008-03-24)。「どのような条件(例外)が実際にあるのか」Danweinreb.org。2013年2月1日にオリジナルからアーカイブされまし2014年9月18日取得
  56. ^ 「条件システムの概念」Franz.com。2009年7月21日。2007年6月28日にオリジナルからアーカイブされまし2011年12月15日取得

外部リンク