30 de julio de 2010

Manejo de errores en C

Es común el llamado sucesivo de funciones, para completar algún proceso, en algunos casos estas funciones son dependientes entre sí, i.e. no se puede avanzar si no se cumple un paso previo, (por ejemplo no se puede leer un archivo sin antes abrirlo), en cambio otras ocaciones un llamado no depende en absoluto del anterior, (por ejemplo cargar un driver de video y uno de sonido, ahí el orden no importa, ni siquiera el llamado secuencial tiene importancia y podrían llamarse en "paralelo" desde diferentes threads). Entonces dependiendo del caso, si alguna función reporta un error, el mismo podría ignorarse y continuar, o se podría reintentar el llamado, abortar el proceso o realizar alguna tarea correctiva y continuar, etc...

En todos los casos es importante chequear si las funciones devuelven OK o ERROR para direccionar el flujo correctamente.


Supongamos que:

               ==0 significa OK
                !=0 significa ERROR

Entonces el código podría ser:

if(functionA()!=0)
      {error management}
   if(functionB()!=0)
      {error management}
   if(functionC()!=0)
      {error management}
   if(functionD()!=0)
      {error management}
   if(functionE()!=0)
      {error management}
...

Esto tiene a simple vista algunas contras..
  • ¿Qué hacer si ante un error quiero saltear la funcion y seguir con las otras?
  • ¿Qué tal si quiero "reintentar" con la que falló?
  • ¿Y si además quiero que el código se vea limpio y lindo?
Otra opción podría ser:

do{
   if(functionA()!=0)  {error=1;break;}
   if(functionB()!=0)  {error=2;break;}
   if(functionC()!=0)  {error=3;break;}
   if(functionD()!=0)  {error=4;break;}
   if(functionE()!=0)  {error=5;break;}
break;
}while(1)
{error management común para todas, con un switch por ejemplo}


Pero el código no mejora mucho su belleza, además, y sigue sin permitir  "reintentar" fácilmente los llamados..

¿Qué tal si hubiera algo como esto?

TEST
   functionA();
   functionB(); <-Repeat_If_Fail(4 times);
   functionC();
   functionD(); <-Ignore_If_Fail(); 
   functionE();
END_TEST

Es buen momento para presentar a Delorean.h , un macro que permite hacer eso de forma compacta:

TEST
   functionA() _
   functionB() _R(4)
   functionC() _
   functionD() _I
   functionE() _
END_TEST 

Un pequeño macro .h intenta aportar legibilidad a este tipo de código, si bien su campo de aplicacion es reducido, en mi opinión la legibilidad del código es fundamental, muchas de las demás características deseables de un código, son consecuencias de esta primera.