はい、2つの嫌いな構造が組み合わされています. それとも、goto の使用を制御し、適切なクリーンアップ戦略を提供するための良い方法と見なすことができますか?
職場では、コーディング標準で goto を許可するかどうかについて話し合いました。一般に、誰も goto を自由に使用できるようにしたくはありませんでしたが、クリーンアップ ジャンプに使用することに前向きな人もいました。このコードのように:
void func()
{
char* p1 = malloc(16);
if( !p1 )
goto cleanup;
char* p2 = malloc(16);
if( !p2 )
goto cleanup;
goto norm_cleanup;
err_cleanup:
if( p1 )
free(p1);
if( p2 )
free(p2);
norm_cleanup:
}
このような使用の上記の利点は、次のコードで終わる必要がないことです。
void func()
{
char* p1 = malloc(16);
if( !p1 ){
return;
}
char* p2 = malloc(16);
if( !p2 ){
free(p1);
return;
}
char* p3 = malloc(16);
if( !p3 ){
free(p1);
free(p2);
return;
}
}
特に、多くの割り当てがあるコンストラクターのような関数では、特に誰かが途中で何かを挿入しなければならない場合に、これは非常に悪くなることがあります。
そのため、goto を使用できるようにしつつ、自由に使用されないように明確に分離するために、一連のフロー制御マクロがタスクを処理するために作成されました。次のようになります (簡略化)。
#define FAIL_SECTION_BEGIN int exit_code[GUID] = 0;
#define FAIL_SECTION_DO_EXIT_IF( cond, exitcode ) if(cond){exit_code[GUID] = exitcode; goto exit_label[GUID];}
#define FAIL_SECTION_ERROR_EXIT(code) exit_label[GUID]: if(exit_code[GUID]) int code = exit_code[GUID];else goto end_label[GUID]
#define FAIL_SECTION_END end_label[GUID]:
これは次のように使用できます。
int func()
{
char* p1 = NULL;
char* p2 = NULL;
char* p3 = NULL;
FAIL_SECTION_BEGIN
{
p1 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p1, -1 );
p2 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p2, -1 );
p3 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p3, -1 );
}
FAIL_SECTION_ERROR_EXIT( code )
{
if( p3 )
free(p3);
if( p2 )
free(p2);
if( p1 )
free(p1);
return code;
}
FAIL_SECTION_END
return 0;
見栄えがよく、多くの利点がありますが、これを開発に展開する前に考慮すべき欠点はありますか? 結局のところ、非常にフロー制御と goto:ish です。どちらも落胆しています。この場合、彼らを思いとどまらせるための議論は何ですか?
ありがとう。