10

例外をサポートしていない言語および/またはライブラリでは、多くの/ほとんどすべての関数が、操作の成功または失敗を示す値を返します。最もよく知られている例は、おそらく や 、またはいくつかの libc 関数などの UN*X システム コールopen()ですchdir()

とにかく、私が C コードを書くと、たいてい次のようになります。

int retval;
...
retval = my_function(arg1, arg2);
if (retval != SUCCESS_VALUE) { do_something(); }

retval = my_other_function(arg1, arg2);
if (retval != SUCCESS_VALUE) { do_something_else(); }

今、私が望むのは、retvalをどこにも保存せず、例外でエラーをスローすることですが、それはできません。次善の策は何ですか?この問題に真の解決策がないことはわかっていますが、それでも何とかしたいと思っています

いくつかのアイデア:

  • 'sと一緒に暮らすようにしてくださいassert()(ただし、それは単に死ぬわけにはいかない製品コード用ではありません)。
  • 関数呼び出しをマクロまたは戻り値チェック関数でラップします (例: ensure_success(my_function(args)or ) ensure_success(my_other_function(args),my_error_handler,error_handler_args)

この件に関して私が好むかもしれない他の慣行はありますか?

編集:

  • はい、C コードを書いています。私は C で書くことを完全に避けるべきだというあなたの意見を尊重しますが、それは実際には建設的ではありません。これは言語戦争の質問ではありません。
  • 何をするのが最善かについて意見を求めているわけではありません。追加の可能性が欲しいだけです。(私は好きなものを選びますが、他の人は他のものを選ぶかもしれません。)
4

7 に答える 7

3

ある種の科学的好奇心を持ってこの問題に取り組んでみてください。エラー処理に対する C のアプローチにより、プログラマーがエラー状態をより認識し、エラーとエラーの処理場所/方法に注意を払うようになったと主張する人がたくさんいます。これは、瞑想のような意識の練習(少し退屈な場合)と考えてください:)

それと戦わないでください。C の精神でこれを可能な限り解決すると、物事の見方が少し広がります。

C エラー処理のマントラに関するこの記事をチェックしてください: http://tratt.net/laurie/tech_articles/articles/how_can_c_programs_be_so_reliable

一般的な質問に対する一般的な答えの 1 つは、関数をできるだけ小さくして、エラー時に関数から直接戻ることができるようにすることです。このアプローチは、すべての言語で有効です。残りは、コードの構造化の演習です。

于 2013-06-27T12:25:56.197 に答える
3

あなたは、問題に対する多くの解決策において、多くのことがうまくいかない可能性があるという事実に直面しています。

この「エラー」状況を処理することは、問題の解決策の一部です。

したがって、私の答えは、何も(またはほとんど)失敗しない問題を解決することに固執するか、エラー状態の処理が解決策の一部であることを受け入れる負担を負うことです。

したがって、(また) 失敗を処理するために必要なコードを設計/作成することは、成功への不可欠な方法です。


実用的なアドバイスとして: エラー処理をコード全体の一部として見ると、エラー処理については、コードの他のすべての行と同じ規則が適用されます。

  • わかりやすい
  • 効率的になるのに十分なほど厳密
  • 拡張できるほど柔軟
  • 保存の失敗 (このコンテキストで言及するのは冗長です)
  • 体系的/一貫性がある (パターン、命名、レイアウトに関して)
  • 文書化された

使用可能な言語構造:

  • breaks はローカル コンテキストから外れています
  • ( goto)*
  • ( longjmp& 例外を「シミュレート」する友人)*
  • 明確に定義された (再び体系的/一貫性のある) 関数宣言と戻りコード

*構造化された方法で規律とともに使用すること。

于 2013-06-27T11:59:37.247 に答える
2

多くの場合と同様に、C でのエラー処理には、他の (高レベル) 言語よりも注意が必要です。

個人的には、それが必ずしも悪いことだとは思いません。なぜなら、エラー状態や関連する制御フローについて実際に考える必要があり、適切な設計を考え出す必要があるからです (そうしないと、結果はおそらく維持できない混乱になる)。

大まかに言って、エラー状態は致命的なものと致命的でないものに分けることができます。

致命的ではないものの中に、回復可能なものがあります。つまり、再試行するか、フォールバック メカニズムを使用するだけです。それらは、例外をサポートする言語であっても、通常はインラインで処理します。

次に、回復できないものがあります。errno代わりに、失敗をログに記録したい場合がありますが、通常は、たとえばリターン コードまたは何らかの型変数を介して呼び出し元に通知するだけです。おそらく、関数内でいくつかのクリーンアップを行う必要があるgotoかもしれません。コードをよりきれいに構造化するのに役立つ場合があります。

致命的なものの中には、プログラムを終了させるものがあります。つまり、exit()ゼロ以外のステータスでエラー メッセージを出力するだけです。

次に、コアをダンプするだけの例外的なケースがあります (例: 経由abort())。アサーションの失敗はそれらのサブセットですが、通常の実行中に状況が不可能であると想定される場合にのみ使用して、コードがビルドassert()で完全に失敗するようにしてください。NDEBUG

致命的な例外の 3 番目のクラスは、プログラム全体を終了させるのではなく、(おそらく深くネストされた) 呼び出しチェーンだけを終了させるものです。ここで使用できますlongjmp()が、割り当てられたメモリやファイル記述子などのリソースを適切にクリーンアップするように注意する必要があります。つまり、一部のプールでこれらのリソースを追跡する必要があります。

少しの可変引数マクロ マジックにより、少なくともこれらのケースのいくつかに対して適切な構文を考え出すことが可能になります。

_Bool do_stuff(int i);
do_stuff(i) || panic("failed to do stuff with %i", i);
于 2013-06-27T12:28:58.437 に答える
0

これに対する解決策が C にあれば、例外は発明されませんでした。次の 2 つの選択肢しかありません。

  • 例外をサポートする言語に移行します。
  • C を使用し、C を作成することを余儀なくされた要因が何であれ、すべてが修正不可能な問題であることを嘆いてください。

ただし、Visual C を使用している場合は、SEH を C の例外として使用できます。きれいではありませんが、機能します。

于 2013-06-27T12:21:41.070 に答える
0

glib API の多くは、GError一種の (もちろん明示的、これは C です) 例外コンテナーとして機能する抽象化を持っています。あるいはerrno、抽象化の方が適切な説明かもしれません。

これはポインターへのポインターとして使用され、呼び出しが成功したかどうかをすばやく確認し (エラーを保持するために新しい GError が作成されます) 、同時に詳細なエラー情報を処理できます。

于 2013-06-27T12:16:07.940 に答える
0

&&めったにありませんが、幸せな状況では、 andの短絡の性質を利用できます||

if (my_function1(arg1, arg2) == SUCCESS_VALUE 
 && my_function2(arg3) == SUCCESS_VALUE)
{
  /* Proceed */
}
else
{
  /* Cry and die. */
}

関数 1 と 2 のエラーを区別する必要がある場合、これはうまく機能しない可能性があります。

審美的には、超美しいわけではありませんが、これは結局のところエラー処理です:)

于 2013-06-27T11:54:35.700 に答える