いくつかの“良い” Cエラー処理にlongjmp / setjmpを使用する方法は?
-
03-07-2019 - |
質問
1つのプロジェクトにCを使用する必要があり、エラー処理に longjmp / setjmp
を使用することを考えています。リターンコードよりも1か所でエラーを処理する方がはるかに簡単だと思います。これを行う方法についてのリードがあれば感謝します。
このようなエラーが発生した場合、リソースのクリーンアップが正しく行われることを特に懸念しています。
また、それらを使用するマルチスレッドプログラムで発生するエラーをどのように処理しますか?
さらに良いことに、エラー/例外処理のためにすでに存在するCライブラリがありますか?
解決
この例/チュートリアルをご覧ください:
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch .html
他のヒント
リソースのクリーンアップが心配な場合は、longjmp()とsetjmp()が良いアイデアかどうか真剣に考える必要があります。
実際に正確にクリーンアップできるようにリソース割り当てシステムを設計する場合は問題ありませんが、実際にはコードが使用する標準ライブラリが割り当てる場合、その設計は難しい傾向があり、通常は不完全です解放する必要のあるリソース。特別な注意が必要であり、完全に信頼できるものではないため、setjmp()/ longjmp()呼び出しの複数の使用に耐える必要がある長期実行システムには適していません(リーク、拡張、最終的には問題)。
Symbianは、 longjmp()
の観点から Leave
メカニズムを実装しました。これは、必要なすべてのことを順を追って説明します。
Symbianにはグローバルな「クリーンアップスタック」があり、ジャンプが発生した場合にクリーンアップするものをプッシュおよびポップします。これは、C ++例外がスローされたときにC ++コンパイラが行う自動スタックアンワインドの手動による代替です。
Symbianには、飛び出す「トラップハーネス」がありました。これらはネストできます。
(Symbianは最近C ++例外に関して再実装しましたが、インターフェースは変更されていません)。
すべてをまとめると、適切なC ++例外はコーディングエラーが発生しにくく、独自のCの同等物を展開するよりもはるかに高速であると思います。
(現代のC ++コンパイラは、スローされない場合、「ゼロオーバーヘッド」例外に非常に優れています。たとえば、 longjmp()
は、ジャンプした場合でも、すべてのレジスタの状態などを保存する必要があります後で採用されないため、基本的に例外ほど速くなることはありません。)
例外とRAIIのみを採用するより良いCとしてC ++を使用することは、例外のエミュレーションに longjmp()
を使用するのに適した良いルートです。
setjmp()/ longjmp()
に one を使用したことがわかっただけで、エラー処理とは関係ありませんでした。
それを使用する必要は本当にありません。それはいつでも簡単にフォローできるものにリファクタリングできるからです。 setjmp()/ longjmp()
の使用は、簡単に悪用される可能性があるという点で goto
と非常に似ています。コードを読みにくくすることは、一般的に悪い考えです。私はそれらが本質的に悪いと言っているわけではないことに注意してください、それらは代替よりも簡単に悪いコードにつながる可能性があるというだけです。
FWIW、彼らが非常に貴重だったのは、私が業界の初期に行ったプロジェクト(MS-DOS 6の時間枠)でした。 Turbo Cを使用して、 yield()
関数でこれらの関数を使用してタスクを切り替える協調マルチスレッドライブラリを作成しました。
あの頃から触っていない(または必要がなかった)のは確かだ。
例外は、はるかに優れた一般的なメカニズムですが、Cの暗黒時代には、コマンドシェルを含むプロセッサエミュレータを作成しました。割り込み処理のためにsetjmp / longjmpを設定するために使用されるシェル(つまり、プロセッサが実行中でユーザーがbreak / ctrl-cを押すと、コードはSIGINTとlongjmpsをシェルにトラップします)。
setjmp
/ longjmp
を適度に使用して、コールバック内からエスケープしました。他のさまざまなライブラリレベルを経由する必要はありません。
その場合(正しく思い出せば)は、yaccで生成されたパーサー内のコードが(非構文的な)問題を検出し、解析を放棄したが、かなり便利なエラーレポートを呼び出し側に返すことでした。すべてのyacc生成コードの反対側。別の例は、Expatパーサーから呼び出されるコールバック内です。いずれの場合も、これを行う他の方法がありましたが、この方法で単純に救済するよりも面倒で曖昧に見えました。
しかし、他の回答が指摘したように、クリーンアップには注意する必要があり、 longjmp
コードが動的に保護された領域のスコープ内でのみ呼び出し可能であることを確認することに非常に思慮深い setjmp
によって。
マルチスレッドプログラミングのコンテキストで実行していますか?それは不可能ではないと確信していますが、Oooh:アスピリンの家族パックを今すぐ取り出してください。 setjmp
/ longjmp
のペアをできるだけ近づけておくのが賢明でしょう。一致する setjmp
/ longjmp
のペアが同じスレッド内にある限り、大丈夫だと思いますが、...そこに注意してください。