sábado, marzo 12, 2016

Buenas Maneras (II): Errores y excepciones

En C++, en JAVA y en C# (y supongo que en la mayoría de los lenguajes de alto nivel), existe una herramienta muy potente para la gestión de errores dentro de un programa: las Excepciones.

A decir verdad en C++ no pasan más que por ser un retorno de un int por una vía distinta a la habitual, sin embargo sigue siendo un camino a tener en cuenta. Hay que tener en cuenta que las excepciones no son necesariamente errores en una función, sino situaciones excepcionales y distintas al flujo normal del programa (la propia definición de la palabra lo dice).

He observado que algunos programadores utilizan las excepciones para devolver valores o ejecutar un código dentro del flujo normal del programa, y esto a mi modo de ver es un error muy grande.

Para hacerlo más claro voy a explicar un ejemplo:
Un programa que comunica el PC con un microcontrolador vía serie. Al enviar el PC una orden, el micro responde que la operación no se ha podido realizar de forma correcta. Algunos programadores utilizan una excepción para informar al usuario, lo cual es un error. En este caso deberíamos tener en cuenta los posibles casos que el micro pueda informar y tratarlos adecuadamente.
Debemos dejar las excepciones para resolver problemas que nos surjan fuera del flujo normal, e intentar dejar las respuestas de las diferentes partes del programa al margen de las excepciones.

Otro ejemplo un poco menos claro:
En el mismo programa de antes se debe tener en cuenta que el micro pueda estar desconectado u ocupado realizando alguna otra tarea. Esto generalmente provocará un TIMEOUT en la llamada al puerto serie. Muchos programadores utilizan una excepción para informar al usuario que el puerto no responde (en la propia excepción). Si bien el TIMEOUT es una excepción en sí misma, no se debe utilizar para informar al usuario, sino que, lo que se debería hacer es recoger la excepción y tratarla en el flujo normal del programa (reintentos de enviar el comando, informar al usuario, etc.).

En definitiva para una función cualquiera la cabecera debería seguir la siguente pauta:

tRespuesta Funcion(tParametros) throw tExcepcion

Donde tRespuesta definirá las respuestas normales de la función y tExcepcion definirá los errores y excepciones que no hayan podido ser tratados dentro de la función o que deban ser informados a otras funciones.

En C el tema cambia, ya que no existen las excepciones de forma nativa, así que u optamos por utilizar librerías externas que implementan macros para realizar esta función, o directamente nos montamos nosotros el sistema.

En mi caso he decidido hacer esto último de una forma muy sencilla: todas mis funciones devuelven un entero:
int Funcion(tParametros)

de tal forma que si el entero es un número negativo la función está devolviendo un código de error. Si la función devuelve 0 (OK) es un funcionamiento normal y dejo los números positivos para la devolución de otros resultados (por ejemplo números de bytes leidos de un puerto, número de estado de una máquina de estados, etc.)

De esta forma siempre puedo hacer el siguiente código:

#include "error.h"
return = foo(parameters);
if(return < OK) { // Tratamiento de los errores }

He incluso tratar cada uno de los posibles errores por separado con un switch-case-default.

Evidentemente reconocer los posibles problemas dentro de la función (punteros no válidos, indices fuera de rango, etc.) corre a cargo mío, pero es un sistema que me funciona bastante bien y muy simple y rápido de implementar.

Sólo hay que tener en cuenta que los valores a devolver por la función siempre tienen que ser enteros positivos y que si se necesitaran otro tipo de valores se deberá pasar un parámetro por referencia para guardarlos allí.

Un apunte, algunos microcontroladores tienen implementado una serie de traps, es decir funciones a las que se llaman cuando hacemos una operación prohibida (como una división por cero), estas traps ya hacen de excepciones (aunque en realidad son errores graves) por lo que se debería no intentar que saltasen nunca ya que hacerlo debería significar hacer un reset del programa.


S2

Ranganok Schahzaman

No hay comentarios: