私はこのQ&Aに何度も遭遇し、より包括的な回答を提供したいと考えました。これについて考える最良の方法は、呼び出し元にエラーを返す方法と、何を返すかです。
どうやって
関数から情報を返す方法は3つあります。
- 戻り値
- 引数外
- 帯域外、非ローカルgoto(setjmp / longjmp)、ファイルまたはグローバルスコープ変数、ファイルシステムなどが含まれます。
戻り値
戻り値は単一のオブジェクトのみですが、任意の複合体にすることができます。エラーを返す関数の例を次に示します。
enum error hold_my_beer();
戻り値の利点の1つは、呼び出しを連鎖させて、煩わしくないエラー処理を実現できることです。
!hold_my_beer() &&
!hold_my_cigarette() &&
!hold_my_pants() ||
abort();
これは読みやすさだけでなく、そのような関数ポインターの配列を均一な方法で処理することもできます。
引数外
引数を介して複数のオブジェクトを介して複数を返すことができますが、ベストプラクティスでは、引数の総数を少なくすることをお勧めします(たとえば、<= 4)。
void look_ma(enum error *e, char *what_broke);
enum error e;
look_ma(e);
if(e == FURNITURE) {
reorder(what_broke);
} else if(e == SELF) {
tell_doctor(what_broke);
}
帯域外
setjmp()を使用して、場所とint値の処理方法を定義し、longjmp()を介してその場所に制御を移します。Cでのsetjmpおよびlongjmpの実用的な使用法を参照してください。
何
- インジケータ
- コード
- オブジェクト
- 折り返し電話
インジケータ
エラーインジケーターは、問題があることを通知するだけで、その問題の性質については何も通知しません。
struct foo *f = foo_init();
if(!f) {
/// handle the absence of foo
}
これは、関数がエラー状態を通知する最も強力な方法ではありませんが、呼び出し元が段階的にエラーに応答できない場合に最適です。
コード
エラーコードは、問題の性質について発信者に伝え、適切な応答(上記から)を許可する場合があります。戻り値、またはエラー引数の上のlook_ma()の例のようになります。
オブジェクト
エラーオブジェクトを使用すると、呼び出し元に任意の複雑な問題について通知できます。たとえば、エラーコードと適切な人間が読めるメッセージです。また、呼び出し側に複数の問題が発生したこと、またはコレクションの処理時にアイテムごとにエラーが発生したことを通知することもできます。
struct collection friends;
enum error *e = malloc(c.size * sizeof(enum error));
...
ask_for_favor(friends, reason);
for(int i = 0; i < c.size; i++) {
if(reason[i] == NOT_FOUND) find(friends[i]);
}
もちろん、エラー配列を事前に割り当てる代わりに、必要に応じて動的に(再)割り当てることもできます。
折り返し電話
コールバックはエラーを処理するための最も強力な方法です。何かがうまくいかなかったときにどのような動作を見たいかを関数に伝えることができるからです。各関数にコールバック引数を追加できます。または、次のような構造体のインスタンスごとにのみカスタマイズが必要な場合:
struct foo {
...
void (error_handler)(char *);
};
void default_error_handler(char *message) {
assert(f);
printf("%s", message);
}
void foo_set_error_handler(struct foo *f, void (*eh)(char *)) {
assert(f);
f->error_handler = eh;
}
struct foo *foo_init() {
struct foo *f = malloc(sizeof(struct foo));
foo_set_error_handler(f, default_error_handler);
return f;
}
struct foo *f = foo_init();
foo_something();
コールバックの興味深い利点の1つは、ハッピーパスにオーバーヘッドがないエラーがない場合、コールバックを複数回、またはまったく呼び出せないことです。
ただし、制御の反転があります。呼び出しコードは、コールバックが呼び出されたかどうかを認識しません。そのため、インジケーターを使用することも意味があります。