C コードでのエラー処理

私は両方のアプローチを使用しましたが、どちらもうまくいきました。どちらを使用する場合でも、常に次の原則を適用するようにしています:

可能性のある唯一のエラーがプログラマー エラーである場合は、エラー コードを返さず、関数内でアサートを使用してください。

入力を検証するアサーションは、関数が期待するものを明確に伝えますが、エラー チェックが多すぎると、プログラム ロジックがわかりにくくなる可能性があります。さまざまなエラー ケースすべてに対して何をすべきかを決定することは、設計を非常に複雑にする可能性があります。代わりに、プログラマーが null ポインターを渡さないように主張できるのに、functionX が null ポインターを処理する方法を理解する必要はありません。


戻り値の方法としてエラーが好きです。 API を設計していて、ライブラリをできるだけ簡単に利用したい場合は、次の追加について検討してください。

    <リ>

    考えられるすべてのエラー状態を 1 つの型定義された列挙型に格納し、それをライブラリで使用します。 int を返すだけでなく、さらに悪いことに、int やさまざまな列挙を return-code と混ぜて使用しないでください。

    <リ>

    エラーを人間が読める形式に変換する関数を提供します。シンプルにできます。 error-enum を入力し、const char* を出力するだけです。

    <リ>

    この考え方がマルチスレッドの使用を少し難しくしていることは承知していますが、アプリケーション プログラマがグローバルなエラー コールバックを設定できるとよいでしょう。そうすれば、バグハント セッション中にブレークポイントをコールバックに入れることができます。

それが役に立てば幸い。


一般的な C (および C++) エラー処理手法のそれぞれをいつ使用するかについての推奨事項を含む、CMU の CERT からの優れた一連のスライドがあります。最良のスライドの 1 つは、この意思決定ツリーです:

個人的には、このフローチャートについて 2 つの点を変更します。

まず、オブジェクトが戻り値を使用してエラーを示す必要がある場合があることを明確にします。関数がオブジェクトからデータを抽出するだけでオブジェクトを変更しない場合、オブジェクト自体の整合性は危険にさらされておらず、戻り値を使用してエラーを示す方が適切です。

第二に、常にではない C++ で例外を使用するのに適しています。例外は、エラー処理に費やされるソース コードの量を減らすことができ、ほとんどの場合、関数のシグネチャに影響を与えず、コールスタックに渡すことができるデータに非常に柔軟であるため、優れています。一方、例外はいくつかの理由で適切な選択ではない場合があります:

<オール> <リ>

C++ の例外には、非常に特殊なセマンティクスがあります。これらのセマンティクスが必要ない場合は、C++ 例外を選択するのは適切ではありません。例外は、スローされた直後に処理する必要があり、設計は、エラーがコールスタックを数レベル巻き戻す必要があるケースを優先します。

<リ>

例外をスローする C++ 関数は、例外をスローしないように後でラップすることはできません。少なくとも、とにかく例外の全コストを支払わなければなりません。エラー コードを返す関数は、C++ 例外をスローするようにラップできるため、より柔軟になります。 C++ の new スローしないバリアントを提供することで、これを正しく行います。

<リ>

C++ 例外は比較的コストがかかりますが、この欠点は、例外を賢明に使用するプログラムにとっては大げさです。プログラムは、パフォーマンスが懸念されるコードパスで例外をスローするべきではありません。プログラムがどれだけ速くエラーを報告して終了できるかは問題ではありません。

<リ>

C++ 例外が利用できない場合があります。それらは C++ 実装で文字通り利用できないか、コード ガイドラインで禁止されています。

元の質問はマルチスレッド コンテキストに関するものだったので、元の回答ではローカル エラー インジケーター手法 (SirDarius の回答で説明されていること) が過小評価されていたと思います。スレッドセーフであり、呼び出し元がエラーをすぐに処理することを強制せず、エラーを説明する任意のデータをバンドルできます。欠点は、オブジェクトによって保持される必要があり (または何らかの形で外部的に関連付けられていると思われます)、リターン コードよりも無視しやすいことです。