Sì, due costrutti odiati combinati. È così brutto come sembra o può essere visto come un buon modo per controllare l'utilizzo di goto e anche fornire una ragionevole strategia di pulizia?Macro di controllo del flusso con 'goto'
Al lavoro abbiamo discusso se consentire o meno il goto nel nostro standard di codifica. In generale nessuno voleva consentire l'uso gratuito di goto, ma alcuni erano positivi nell'usarlo per i salti di pulizia. Come in questo codice:
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:
}
Il beneficio abovious di tale utilizzo è che non c'è bisogno di finire con questo codice:
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;
}
}
Soprattutto in funzioni di costruzione-come con molte allocazioni questo può a volte cresce molto male, non meno importante quando qualcuno deve inserire qualcosa nel mezzo.
Quindi, per poter utilizzare goto, ma ancora chiaramente isolarlo dall'essere utilizzato liberamente, è stata creata una serie di macro di controllo del flusso per la gestione dell'attività. Sembra qualcosa di simile (semplificato):
#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]:
possiamo usare questa come segue:
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;
Sembra bello, e viene fornito con molti vantaggi, ma ci sono degli svantaggi che dovremmo pensare a prima di lanciare questo fuori nello sviluppo? Dopo tutto controlla molto il flusso e goto: ish. Entrambi sono scoraggiati. Quali sono gli argomenti per scoraggiarli in questo caso?
Grazie.
Come Martin Fido sottolinea nella sua risposta, nel frammento di codice finale i puntatori p1, p2 e p3 sono tutti fuori dalla sezione di gestione degli errori, e nel primo frammento di codice p2 conterrà i dati di spazzatura (se si compila a tutti - non sono sicuro delle regole C). –
@j: Sono sicuro che questo codice non passerà nemmeno attraverso il compilatore. Grazie per il suggerimento, ma devi aver perso l'intero punto della domanda. – sharkin
@ R.A: cosa ti fa pensare che mi sia mancato il punto? La non-compilabilità era un problema secondario che pensavo valesse la pena di sottolineare in un commento. –