Sí, dos constructos odiados combinados. ¿Es tan malo como suena o puede verse como una buena forma de controlar el uso de goto y también proporciona una estrategia de limpieza razonable?Macros de control de flujo con 'goto'
En el trabajo, tuvimos una discusión sobre si permitimos ir a nuestro estándar de codificación. En general, nadie quería permitir el uso gratuito de goto, pero algunos eran positivos sobre su uso para saltos de limpieza. Al igual que en este código:
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:
}
El beneficio abovious de tal uso es que usted no tiene que terminar con este código:
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;
}
}
Especialmente en funciones constructoras como con muchas asignaciones esto puede a veces crecen muy mal, no menos cuando alguien tiene que insertar algo en el medio.
Por lo tanto, para poder utilizar goto, pero aún así aislarlo claramente de ser utilizado libremente, se creó un conjunto de macros de control de flujo para manejar la tarea. Se ve algo como esto (simplificado):
#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]:
podemos utilizar esto como sigue:
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;
Se ve bien, y viene con muchos beneficios, pero, ¿existen inconvenientes que deben estar pensando antes de implementar esto en el desarrollo? Después de todo, es muy controlador de flujo y goto: ish. Ambos están desalentados. ¿Cuáles son los argumentos para desalentarlos en este caso?
Gracias.
Como Martin Fido señala en su respuesta, en el último fragmento de código, los punteros p1, p2 y p3 están fuera de la sección de manejo de errores, y en el primer fragmento de código p2 contendrá datos de basura (si compila en absoluto, no estoy seguro acerca de las reglas de C). –
@j: estoy seguro de que este código ni siquiera pasará por el compilador. Gracias por la sugerencia, pero debe haber perdido todo el sentido de la pregunta. – sharkin
@ R.A: ¿Qué te hace pensar que me perdí el punto? La falta de complicidad fue un tema secundario confuso que pensé que valía la pena señalar en un comentario. –