Cでは、次のようなエラーを処理することをお勧めします(私はそう思います)。
int status = tree_climb(tree, ...);
if (status != 0) {
global_logger.message(2, "Cannot climb a tree %s", tree->name);
return EPIPE;
}
または、代わりに
forest_errno = tree_climb(tree, ...);
if (forest_errno != 0) {
goto exit;
}
exit:
if (tree) {
tree_dispose(tree);
}
if (forest) {
forest_dispose(forest);
}
return forest_errno;
質問は:
これにプリプロセッサマクロを使用しない理由は何ですか?次のようなコードをたくさん目にしたことを思い出せません。
#define CHECK_OR_RETURN(contract, error_status, log_level, message_format, ...) \
if (!(contract)) { \
global_logger.message(log_level, message_format, ##__VA_ARGS__); \
return error_status; \
}
マクロを使用する理由は次のとおりです。
マクロを使用しない理由は次のとおりです。
なぜ人々はこれをいつもしませんか?
「代替」は、Cコードがリソースの段階的な取得を実装する可能性があることを示しています。
エラー処理を行わずに次の例を見てみましょう。ここでインデントを使用して、さまざまな手順を示します。
FILE *fp = fopen("data.txt", "rb"); // step 1: acquire file resource
Forest* forest = generate_forest(fp,...); // step 2: allocate tree resource
Tree *tree = get_tree(forest, "SPAIN"...);
Element *element = malloc(sizeof(element)); // step 3: allocate new element
element->value = MAGIC;
tree_add(tree, element); // suppose element is copied into the tree
free (element); // end step 3
...
release_forest (forest); // end step 2 (forest takes care of its trees)
...
fclose (fp); // end step 1
ここでは、各単一の命令によってエラーが発生する可能性があります。エラーが発生するステップによっては、一部または複数のリソースを解放する必要がある場合があります。
代替1
マクロを使用してさまざまなエラーをログに記録して処理すると、すべてのリソースが解放されるわけではないのに、リソースをリークするだけです。
代替2
いくつかのネストされた条件ステートメントを使用する:
FILE *fp = fopen("data.txt", "rb"); // step 1: acquire file resource
if (fp) { // go on for step 1
Forest* forest = generate_forest(fp,...); // step 2: allocate tree resource
if (forest) { // go on for step 2
Tree *tree = get_tree(forest, "SPAIN"...);
...
release_forest (forest); // end step 2 (forest takes
}
else {
errcode = logmyerror ("forest not properly loaded", ENOTFOUND);
}
fclose (fp); // end step 1
}
else {
errcode = logmyerror ("file not found ", EOOPSAGAIN);
}
return errcode; // final exit point
代替3
ignomous goto (ご覧のとおり、いくつかの利点があります)。ここで、さまざまなラベルが、関連する手順の出口点に対応しています。
FILE *fp = fopen("data.txt", "rb"); // step 1: acquire file resource
if (fp==NULL) {
errcode = logmyerror ("file not found ", EOOPSAGAIN);
goto out0;
}
Forest* forest = generate_forest(fp,...); // step 2: allocate tree resource
if (forest==NULL) {
errcode = logmyerror ("forest not properly loaded", ENOTFOUND);
goto out1;
}
Tree *tree = get_tree(forest, "SPAIN"...);
...
out2:
release_forest (forest); // end step 2 (forest takes care of its trees)
...
out1:
fclose (fp); // end step 1
out0:
return errcode;
結論
もちろん、そのようなマルチレベルのリソース割り当てがない場合は、マクロを完全に使用できます。しかし、現実にはこれでは不十分な状況がたくさんあると思います。
C++では、RAIIを使用する場合、これは必要ないことに注意してください。デストラクタは、戻るときに常にリソースをクリーンアップします。
あなたがリストした理由のために人々はしません。さらに、一般的な「適切なコーディングプラクティス」はありませんが、最良の場合には、いくつかの部門のルールがあります。また、一貫性のないコードがたくさんあります。つまり、パーツはマクロを使用し、パーツは使用しません。おそらく、どのプログラマーが手にしたかに依存します。
だから私はどのアドバイスを与えることができますか?これらの単純なエラー処理スニペットがたくさんある場合は、マクロを自由に使用してください。密集したコードは、特にそれらの反復的なスニペットでは、散在するよりも読む方が良いと思います。
もちろん、より複雑なエラー処理がある場合は、管理できないマクロになる可能性があります。
Cで例外がない場合は、「ロングジャンプ」で例外をエミュレートできます。このようにして、try/catchのように、例外を処理するための1つのポイントを持つことができ、このポイントをより高いスタックレベルにすることもできます。
以下は実装例ですが、基本(longjmp/setjmp)は最初からCの一部であり、追加のソフトウェアは必要ありません。 http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html