[C con Clase] Logueo de errores...

Gilberto Cuba Ricardo gilbert en ucp.ho.rimed.cu
Lun Ago 16 17:35:00 CEST 2010


Hola Steven,

Steven Davidson escribió:

> Hola Gilberto,

>> Existe algún modelo en específico o acercamiento a él, de como poder
>> insertar el tratamiento de las excepciones en el modelo de una
>> aplicación cualquiera. La idea es que no sabría como diseñar lo
>> suficientemente bien este sistema en una aplicación, de forma tal que
>> sea lo más genérico posible. Ahora leo a Stroustrup B. [3rd Ed.] "The
>> C++ Programing Languaje", que espero me de un poco de vista.

Además de esto he seguido revisando y he encontrado muy buenos
comentarios y reflexiones generales sobre todo este mundo que es
bastante extenso. Comparto estos tres enlaces que fueron bastantes claros
y suficientes en el entendimiento:

http://stackoverflow.com/questions/1335561/c-exception-class-design
http://www.boost.org/community/error_handling.html
http://en.wikipedia.org/wiki/Exception_handling

> Esto depende del control que quieras tener. En general, vas a tener que
> fijarte en cada posible excepción y aplicar la resolución que quieras.
> Esto es lo que te ofrece el sistema de excepciones. Puedes resolver el
> problema inmediato y continuar con el programa, por lo que no es 
> necesario el comportamiento de una terminación abrupta del programa.

He estado pensando en alguna situación que es difícil predecir
para poderla manipular con las excepciones. Ha esto me refiero cuando
la aplicación se vuelve inestable y se cierra, incluso teniendo un
buen tratamiento con las excepciones, y no estamos ahí para poder
tener referencia del problema y poderlo resolver con alguna variante
de cambio en el código, digamos.

Según todo lo que he visto hasta ahora, un tratamiento de excepción
'general' en el bloque principal de la aplicación, en ocasiones, pudiera
no detener el cierre de la aplicación sin darle un tratamiento correcto
o sin reportar las causas del cierre. Digamos que en:

...
int main(...)
{
  try // try general de la aplicación
    {
      ... // otros try y funcionamiento de la aplicación
    }
  catch(...)
    {
      // loguear el estado de la aplicación
    }

  return 0;
}

pudiera darse algún caso en el que el error se salte ese catch y no
tengamos la información que necesitamos. ¿Estoy equivocado o puede
darse ese caso? Si me equivoco en mi suposición, entonces no tiene
sentido la siguiente idea.

> Ahora bien, si ya tienes pensado el mismo comportamiento de abortar el
> programa para cualquier excepción, entonces quizá no te interese el 
> sistema de excepciones. En su lugar, puedes usar las funciones 
> estándares para registrar una función propia para que se invoque al 
> abortarse el programa. Esto se basa en recibir la señal 'SIGABRT'. Por
> ejemplo,

> void antes_de_abortar( int n )
> {
>    ofstream ofsBitacora( "error.log", ios::app );
>    ofsBitacora << "Mensaje" << endl;
> }

> int main()
> {
>    signal( SIGABRT, antes_de_abortar );
>    ...
> }

> Si prefieres mayor control para detallar el comportamiento de cada 
> excepción, entonces tendrás que usar el sistema de excepciones o 
> implementar uno parecido. Obviamente, te aconsejo que uses el sistema de
> excepciones que trae C++ porque ya está hecho.

Esto me pareció genial también, y me motivó aún más, sin embargo, veo
que es demasiado grande para mi talla. Porque me surgieron varias
ideas enseguida y dudas respecto a su funcionamiento a pesar de haber
leído algo.

Como por ejemplo: en implementar los dos sistemas, el de las excepciones
y el de los signals. Las excepciones para poder realizar un tratamiento
pormenorizado de los errores, y las señales para capturar una salida
inesperada de mayor envergadura, digamos con la señal SIGABRT. Pero
entonces pensé que si siempre que se disparaba otro como SIGSEGV, se
dispararía SIGABRT, si fuera el más general, o habría que enlazarlos a
todos ellos (lo cual sería bastante trabajoso) para que dispararan a
SIGABRT. Ya me dirá.

> Las definiciones estándares de '__LINE__' y '__FILE__' son símbolos del
> precompilador. Por lo tanto, no tienen nada que ver con el depurador. 
> Por ejemplo,

> int main()
> {
>    int n = __LINE__;

>    return 0;
> }

> El precompilador reescribirá el código fuente para que el compilador vea
> esto:

> int main()
> {
>    int n = 3;

>    return 0;
> }

> No tiene más misterio, la verdad.

Interesante esto, es verdad, no tiene más misterio :)

> En cuanto a __FUNCTION__, esta constante simbólica no es estándar, pero
> sí aparece en el compilador de GNU.

Es muy cierto, como tampoco ayuda mucho cuando la llamada se produce
desde un método de una clase, porque solamente devuelve el nombre del
método como función y no el clásico que conocemos: class_name::method.

> Ten presente, que si quieres usar estas constantes, tendrás que hacer 
> obtener esta información justo en el momento en que se produce el error.
> Esto significa que no obtendrás la información correcta si usas el 
> sistema de excepciones. Acabarías haciendo esto, por ejemplo,

> #define bitacora(b,m)\
> b << "Error en la línea #" << __LINE__ \
> << " de la función '" << __FUNCTION__ \
> << "' en el fichero \"" << __FILE__ << "\": " << m
> ...
> int main()
> {
>    ofstream ofsBitacora( "error.log", ios::app );

>    if( bErrorDesbordamiento )
>      bitacora( ofsBitacora, "Desbordamiento" ) << endl;
>    ...
> }

Sí, ya había tenido esto en cuenta y estaba haciendo algo bonito hasta
que vi el diseño que tiene Ogre en su librería, algo parecido a lo que
estaba haciendo pero más profesional y adecuado a estos tiempos.
Utiliza un Factory para modelar cada uno de los tipos de clases de
excepciones que heredan de una principal, para luego utilizando el
#define, hacer la llamada a la clase factory que contiene todas las
demás clases con los tipos de excepciones. Recomiendo verla para el
que desea tener una idea de la implementación de un sistema de
excepciones.

> Existe otra forma para poder usar estas constantes simbólicas dentro del
> sistema de excepciones, pero tendrás que contar las líneas. Por ejemplo,

> struct MiExcepcion
> {
>    string sFuncion;
>    string sFichero;

>    MiExcepcion( const string &func, const string &fich )
>    : sFuncion(func), sFichero(fich)  {}
> };

> class MiClase
> {
> private:
>    int *pLista;
>    int nCant;

> public:
>    MiClase() : pLista(0), nCant(0);

>    int dato( int n ) const
>    {
>      if( n >= nCant )
>        throw MiExcepcion( "int MiClase::dato(int)", __FILE__ );
>      return pLista[n];
>    }
> };

> int main()
> {
>    MiClase obj;

>    try
>    {
>      int num = obj.dato(1000);
>    }
>    catch( MiExcepcion &ref )
>    {
>      ofstream ofsBitacora( "error.log", ios::app );
>      ofsBitacora << "Error en la línea #" << __LINE__-5
>                  << " de la función '" << ref.sFuncion
>                  << "' en el fichero \"" << ref.sFichero << "\": "
>                  << "Desbordamiento" << endl;
>    }
>    catch( ... )
>    {
>      ofstream ofsBitacora( "error.log", ios::app );
>      ofsBitacora << "Error: mal de ojo" << endl;
>    }

>    return 0;
> }

> Como puedes ver, usamos __LINE__, pero restamos 5 para indicar 5 líneas
> anteriores; justo donde se encuentra la invocación de 'dato()'.

No me parece que esta sea la adecuada.

> Supongo que la otra forma de hacer esto es guardando el código fuente 
> entero como una series de cadenas de caracteres, como si perteneciese a
> una base de datos. De esta forma, implementas los usos de __LINE__, 
> __FUNCTION__, y __FILE__ dentro de tu programa. Nuevamente, tienes mayor
> control, pero estás "reinventando la rueda", como se suele decir.

> En general, no es necesario conocer la línea, función, y fichero fuente
> (o de cabecera) donde se produce el error. Si no necesitas conocer esta
> información, entonces creo que con un mensaje detallado del error o de
> la excepción nos ahorramos "problemas" y así podemos recurrir a la 
> bitácora y al sistema de excepciones de C++.

Coincido con usted, sin embargo, si llevamos un buen tiempo sin
trabajar en una aplicación, creo que demoraríamos un poco en llegar al
lugar del error teniendo solamente un texto explicativo; en cambio
conociendo la línea y el archivo creo que andamos más rápido.

> Espero que todo esto te ayude.

Bastante y nuevamente gracias.

> Steven

-- 
Salu2,
 Gilbert





Más información sobre la lista de distribución Cconclase