Corrección y robustez

Es extremadamente importante que el código de GNOME sea correcto y robusto. Esto significa que el código debería hacer lo que se espera que haga y debería manejar bien las condiciones de excepción. Aunque esto pueda parecer obvio, esta sección dará algunas ideas para asegurar la corrección de tu código de GNOME. Esto es muy importante, ya que los usuarios esperan y merecen un software confiable que se ejecute correctamente y que no se caiga.

Cómo asegurar la consistencia

Utiliza las macros de aserción de Glib para asegurarte que el estado de un programa es consistente. Estas macros ayudan a localizar errores muy rápidamente y se gastará mucho menos tiempo en el depurado si se emplean de forma generosa y consistente.

Inserta verificaciones de sanidad en el código en puntos importantes como es el inicio de funciones públicas, al final de código que realiza una búsqueda que siempre debe ser exitosa y en cualquier lugar donde el rango de valores calculados es importante.

Aserciones y precondiciones

Las aserciones y precondiciones ayudan a asegurar que el estado de un programa es consistente. Glib proporciona macros para colocar aserciones y precondiciones en el código. Deberías usarlas libremente; a cambio podrás localizar errores muy rápidamente y ocuparás menos tiempo rastreando errores con el depurador.

Existen macros de Glib para precondiciones, las cuales emiten un mensaje cuando una condición falla y retornan de la función desde donde fueron llamadas. Debieran ser usadas en el inicio de las funciones.

g_return_if_fail (condición)

Retorna desde la línea actual de la función si la condición es falsa.

g_return_val_if_fail (condición, valor)

Retorna el valor indicado desde la función actual si la condición es falsa.

También existen macros para aserciones. Estas emitirán un mensaje cuando una condición falle y llamará a la función abort(3) para terminar el programa. Debieran ser usadas para asegurarse la consistencia para códigos internos.

g_assert (condición)

Aborta el programa si la condición es falsa.

g_assert_not_reached ()

Aborta el programa si se llama a la macro.

Estas funciones debieran emplearse para imponer precondiciones en el código y verificar su corrección — piensa en ellas como verificaciones de sanidad en los programas. Debieras usarlas libremente como asistencia para atrapar los errores rápidamente; una vez que el programa se encuentre completamente depurado, puedes compilarlo con estas macros deshabilitadas y así evitar añadirle sobrecarga al momento de ejecutarlos.

Las macros g_return_*() debieran emplearse al inicio de las funciones públicas de las bibliotecas, para asegurarse de que los argumentos que se pasan a ellas sean correctos y tengan un rango válido. Si una función no retorna valor (por ejemplo, retorna void), debieras usar g_return_if_fail(). En caso contrario, debiera usar g_return_val_if_fail() para retornar un valor ‘seguro’. Cuando se invoca una función de biblioteca que usa estas macros con un argumento incorrecto , se producirá un mensaje de error y continuará la ejecución. El programa cliente podrá tener algún sobresalto, hacer nada o caerse, pero al menos sabrá dónde se le ha pasado un valor incorrecto a la función.

La macro g_assert() debiera usarse para asegurar la consistencia interna de una biblioteca o programa. En vez de retornar de la función y continuar la ejecución si la condición falla, g_assert() producirá en mensaje de error e inmediatamente abortará el programa. Esto es para evitar que el programa continúe ejecutándose en un estado inconsistente. Debieras usar esta macro para asegurarte de que el programa o biblioteca está usando valores internos sanos.

La macro g_assert_not_reached() se usa para marcar el lugar en el código que nunca debiera producirse. Por ejemplo, si tienes una cláusula switch y piensas que manejas todos los valores posibles en las etiquetas case, entonces debieras colocar g_assert_not_reached() en la etiqueta default para asegurarte de que el código nunca llegue allá (podría significar que ha perdido algún valor o que el programa se encuentra incorrecto).

Estas macros ayudan a encontrar errores más rápido a través de avisos que se producen tan pronto como el programa alcanza un estado incosistente. Úsalos frecuentemente y encontrarás muchos errores más fácilmente.

Tópicos relacionados con GTK+

Debe ser cuidadoso cuando escribas manejadores de eventos — asegurate de que los eventos son manipulados en las situaciones apropiadas. Es necesario asegurarse de que los manejadores de señales tengan los prototipos correctos. Esto es muy importante. Recuerda que no todos los prototipos de manejadores de señal se parecen a lo siguiente:

static void my_handler (GtkWidget *widget, gpointer data);

Por ejemplo, los manejadores de eventos tienen un parámetro extra de evento y retornan gint. Revisa los archivos de encabezado de GTK+ cuando necesites verificarlo.

Asegúrate que el programa trata de una forma apropiada todas las acciones generadas por el usuario. Recuerda que el usuario puede cerrar una ventana en cualquier momento a través del administrador de ventanas; tenlo presente y escribe el código necesario para manejar esta situación.

Si verificas los modificadores de teclas con una máscara de estado de eventos, por ejemplo, Control+F10 escribe lo siguiente:

  guint modifiers;

  modifiers = gtk_accelerator_get_default_mod_mask ();

  if (event->keysym == GDK_F10
      && (event->state & modifiers) == GDK_CONTROL_MASK) {
          do_something ();
  }

Esto es necesario; si en vez de lo anterior escribes:

  if (event->keysym == GDK_F10 && event->state == GDK_CONTROL_MASK)

entonces el programa no funcionará correctamente si el usuario tiene, por ejemplo, activada la tecla NumLockNumLock también es un modificador, y si está activado, entonces la máscara de estado del evento no será como se espera en el segundo ejemplo.

Vistas y mapas de color

Algunos usuarios utilizan tarjetas de vídeo avanzadas (por ejemplo, SGIs y Suns) que soportan múltiples vistas simúltaneamente. Una vista define la representación en memoria que usa un dispositivo de hardware para almacenar los contenidos de una imagen. Muchas tarjetas de vídeo de PC soportan sólo una vista a la vez, pero el hardware avanzado puede tener diferentes ventanas y pixmaps en diferentes vistas simultáneamente.

Es importante entender las vistas y mapas de colores si vas a escribir código que crea ventanas y pixmaps por tus propios medios, en vez de utilizar las funciones de alto nivel como GnomeCanvas y GnomePixmap. Para mayor información, lea el manual de programación de Xlib.

En general, sólo necesitas recordar que la vista y el mapa de colores de un área de dibujo debe coincidir con los de otra área de dibujo si deseas copiar un trozo desde la primera área de dibujo a la segunda. Si no son las mismas, podrías obtener el mensaje «BadMatch error» del servidor X y tu aplicación muy probablemente abortará su ejecución.

Si creas un contexto gráfico (GC) y lo compartes para pintar en varias áreas de dibujo, asegúrate que todas ellas tengan la misma vista y mapa de colores que fue definido para el GC. Lo mismo se aplica si quieres copiar un área desde un pixmap a una ventana; ambos deben tener la misma vista y mapa de colores.

Si no estás seguro de que tu código lo hace correctamente, pregunta educadamente, en una de las listas de correo de desarrollo de GNOME, si alguien puede realizar una prueba con una tarjeta de video que soporta esta característica. Dicha persona sabrá como arreglar el problema y te dirá al respecto.

Temas relacionados con Unix

Verifica los valores de retorno de todas las llamadas al sistema que el programa realice. Recuerda que muchas de las llamadas al sistema pueden ser interrumpidas (por ejemplo, la llamada retorna -1 y errno será definido a EINTR) y deben reiniciarse.

No asumas, por ejemplo, que la función write(2) escribirá el buffer completo de una vez; tienes que verificar el valor de retorno, el cual indica el número de bytes escritos e intenta nuevamente hasta que sea cero. Si el valor de retorno es -1, recuerda verificar el valor de errno y manejar el error apropiadamente.

Si la aplicación llama a la función fork(2) sin llamar a execve(2), recuerda que el proceso hijo no puede hacer llamadas X. Normalmente se puede diagnosticar este problema a través de un oscuro mensaje de error de Xlib.

Lee el libro «Advanced programming in the Unix environment», de Richard Stevens, para aprender acerca de todos estos teassertmas y asegúrate que tus programas usan la API de Unix de forma correcta. Si no deseas asegurarte del uso correcto de las llamadas Unix, pregunta en las listas de correo.