3.9. Consejos para depuración

En un entorno ideal, habrá un depurador excelente disponible que hará que el comportamiento de su programa sea transparente y podrá descubrir cualquier error rápidamente. Sin embargo, muchos depuradores tienen puntos débiles, y eso puede requerir tenga que añadir trozos de código a su programa que le ayuden a entender que está pasando. Además, puede que para la plataforma para la que esté desarrollando (por ejemplo en sistemas empotrados, con lo que yo tuve que tratar durante mis años de formación) no haya ningún depurador disponible, y quizá tenga una realimentación muy limitada (por ejemplo, un display de LEDs de una línea). En esos casos debe ser creativo a la hora de descubrir y representar información acerca de la ejecución de su programa. Esta sección sugiere algunas técnicas para conseguirlo.

3.9.1. Banderas para depuración

Si coloca el código de depuración mezclado con un programa, tendrá problemas. Empezará a tener demasiada información, que hará que los errores sean difíciles de aislar. Cuando cree que ha encontrado el error empieza a quitar el código de depuración, sólo para darse cuenta que necesita ponerlo de nuevo. Puede resolver estos problemas con dos tipos de banderas: banderas de depuración del preprocesador y banderas de depuración en ejecución.

Banderas de depuración para el preprocesador

Usando el preprocesador para definir (con #define) una o más banderas de depuración (preferiblemente en un fichero de cabecera), puede probar una bandera usando una sentencia #ifdef e incluir condicionalmente código de depuración. Cuando crea que la depuración ha terminado, simplemente utilice #undef la bandera y el código quedará eliminado automáticamente (y reducirá el tamaño y sobrecarga del fichero ejecutable).

Es mejor decidir los nombres de las banderas de depuración antes de empezar a contruir el proyecto para que los nombres sean consistentes. Las banderas del preprocesador tradicionalmente se distinguen de las variables porque se escriben todo en mayúsculas. Un nombre habitual es simplemente DEBUG (pero tenga cuidado de no usar NDEBUG, que está reservado en C). La secuencia de sentencias podrías ser:

#define DEBUG // Probably in a header file
//...
#ifdef DEBUG // Check to see if flag is defined
/* debugging code here */
#endif // DEBUG

La mayoría de las implementaciones de C y C++ también le permitirán definir y eliminar banderas (con #define y #undef) desde línea de comandos, y de ese modo puede recompilar código e insertar información de depuración con un único comando (preferiblemente con un makefile, una herramienta que será descrita en breve). Compruebe la documentación de su entorno si necesita más detalles.

Banderas para depuración en tiempo de ejecución

En algunas situaciones es más conveniente activar y desactivar las banderas de depuración durante la ejecución del programa, especialmente cuando el programa se ejecuta usando la línea de comandos. Con programas grandes resulta pesado recompilar sólo para insertar código de depuración.

Para activar y desactivar código de depuración dinámicamente cree banderas booleanas.

//: C03:DynamicDebugFlags.cpp
#include <iostream>
#include <string>
using namespace std;
// Debug flags aren't necessarily global:
bool debug = false;

int main(int argc, char* argv[]) {
  for(int i = 0; i < argc; i++)
    if(string(argv[i]) == "--debug=on")
      debug = true;
  bool go = true;
  while(go) {
    if(debug) {
      // Debugging code here
      cout << "Debugger is now on!" << endl;
    } else {
      cout << "Debugger is now off." << endl;
    }  
    cout << "Turn debugger [on/off/quit]: ";
    string reply;
    cin >> reply;
    if(reply == "on") debug = true; // Turn it on
    if(reply == "off") debug = false; // Off
    if(reply == "quit") break; // Out of 'while'
  }
} ///:~

Listado 3.62. C03/DynamicDebugFlags.cpp


Este programa sigue permitiéndole activar y desactivar la bandera de depuración hasta que escriba quit para indicarle que quiere salir. Fíjese que es necesario escribir palabras completas, no solo letras (puede abreviarlo a letras si lo desea). Opcionalmente, también se puede usar un argumento en línea de comandos para comenzar la depuración - este argumento puede aparecer en cualquier parte de la línea de comando, ya que el código de activación en main() busca en todos los argumentos. La comprobación es bastante simple como se ve en la expresión:

string(argv[i])

Esto toma la cadena argv[i] y crea un string, el cual se puede comparar fácilmente con lo que haya a la derecha de ==. El programa anterior busca la cadena completa --debug=on. También puede buscar --debug= y entonces ver que hay después, para proporcionar más opciones. El Volumen 2 (disponible en www.BruceEckel.com) contiene un capítulo dedicado a la clase string Estándar de C++.

Aunque una bandera de depuración es uno de los relativamente pocos casos en los que tiene mucho sentido usar una variable global, no hay nada que diga que debe ser así. Fíjese en que la variable está escrita en minúsculas para recordar al lector que no es una bandera del preprocesador.