[UCR]  
[/\]
Universidad de Costa Rica
Escuela de Ciencias de la
Computación e Informática
[<=] [home] [<>] [\/] [=>]

Manejo de excepciones en Turbo Pascal


Revisión 4.0
Adolfo Di Mare



Resumen [<>] [\/] [/\]

Se presenta un paquete que implementa las construcciones requeridas para manejar excepciones en el ámbito del Turbo Pascal, en las versiones v4.0 y posteriores 5.0, 5.5 y 6.0. El paquete sigue el modelo de terminación para manejo de excepciones, y es relativamente fácil de usar. A software package for exception handling in Turbo Pascal version v4.0 and later is discussed. This package follows the exception termination model, and it is relatively easy to use.

      Una excepción es un evento inesperado, generalmente con proporciones de calamidad. El buen programador desea que su programa sea robusto, de forma que ante estos eventos reaccione adecuadamente; si no es posible corregir los problemas que se producen después de una falla, es por lo menos deseable que el programa tenga una degradación suave, evitando de esta manera un estruendoso fin de ejecución. En lo posible se trata siempre de sobreponerse de la mejor manera a la catástrofe.

      Existen muchos tipos de excepciones. Por ejemplo, cuando en tiempo de ejecución se usa un índice de vector que está fuera de rango se produce una excepción. También sucede cuando se hace una división por cero, o cuando el resultado de una expresión es demasiado pequeño (desbordamiento y bajo rebase). Los principales tipos de excepción son los siguientes:

      Un ejemplo práctico del uso de excepciones es el siguiente: supóngase que se está escribiendo una hoja de cálculo, que tiene un largo y complicado proceso para recalcular el valor de todas las celdas. Se desea permitirle al usuario interrumpir en cualquier momento el proceso, lo que puede implementarse de dos formas.

      La primera implementación, y más complicada, consiste en agregar un montón de parámetros de retorno a cada rutina usada para recalcular la hoja. En el momento en que se detecta la interrupción, se comenzaría una cadena de retornos de procedimientos, hasta llegar al nivel superior. En cada procedimiento debe incluirse código para retornar adecuadamente después de que se produce la interrupción, mientras realiza su trabajo.

      La otra forma es usar excepciones. Inicialmente, se establece un contexto de excepción en el nivel principal. Luego cualquier rutina (hasta las recursivas) pueden retornar abruptamente a este nivel generando una excepción. De esta manera el programa que no estará plagado de códigos de retorno para manejar esta situación.

      El ejemplo anterior muestra que si no se usan excepciones, el programador que desea implementar un programa muy robusto debe incluir en cada procedimiento muchos parámetros que le permitan retornar códigos de error. Además, cada procedimiento debe también incluir bastante código para tomar las acciones apropiadas, dependiendo de cada posible problema. De esta forma quedan mezcladas en el mismo módulo la lógica que resuelve un problema con la que maneja las situaciones insólitas que surgen cuando ocurren fallas. El resultado de todo esto es programas que son tan difíciles de leer y como de mantener.

      Una manera de resolver este problema es usar un manejador de excepciones. Cada lenguaje de computación da soporte al manejo de excepciones de forma y grado diferente. En el lenguaje PL/I se implementa mediante la cláusula ON; CLU tiene un mecanismo a este efecto (que es, por cierto, muy completo) y en C se usan los famosos procedimientos de biblioteca setjump() y longjump(). Lisp no podía ser la excepción, y cuenta con CATCH y THROW. Para el caso de Turbo Pascal existen varias bibliotecas que implementan este mismo concepto, usando alguno de los nombres anteriores. En ADA se usan los verbos "exception" y "raise"; la implementación que aquí se describe es muy parecida al mecanismo de ADA, aumentado con el verbo Signal() propuesto en [Lee-83].

Uso del manejador except.pas [<>] [\/] [/\]

      La implementación del manejador de excepciones para Turbo Pascal está en la unidad except.pas, la que cuenta con los siguientes procedimientos:

- FUNCTION  Exception_Code    : WORD;
- FUNCTION  Catch             : WORD;
- FUNCTION  Exception         : BOOLEAN;
- PROCEDURE Throw       (code : WORD);
- PROCEDURE Signal      (code : WORD);
- PROCEDURE Re_Throw;
- PROCEDURE Leave_Exception_Context;
- PROCEDURE Release_Nested_Contexts;
      Una buena parte de la implementación de estas operaciones está escrita en lenguaje de máquina, pues para manejar las excepciones es necesario modificar la pila de ejecución del programa. Su funcionamiento es realmente interesante, pues aunque estos procedimientos son llamados como si fueran procedimientos, al ejecutar un Throw(), Signal() o Re_Throw() se regresa a donde se llamó al último Exception().

IF NOT Exception THEN BEGIN     { <=== }
  Proceso_Normal({...});
  Leave_Exception_Context;
END
ELSE BEGIN
  { Manejadores de excepciones }
  { ==> Exception = TRUE       }
  CASE Catch OF
    1: Interrupcion_Operador;
    2: Datos_Invalidos({...});
    3: Error_Fatal;
    6: Throw(7);
    8: Re_Throw;
  ELSE
    Re_Throw;
  END; { CASE }
END; { Exception }
Figura 1

      Para atender excepciones, el programador debe establecer un Contexto de Excepción, que es un bloque de código que tiene asociados varios manejadores de excepciones. En el caso de Turbo Pascal, para establecer el contexto de excepción se usa el procedimientos Exception(), en la forma ilustrada en la Figura 1.

      En la Figura 1 se muestra cómo el código que normalmente se ejecuta para resolver un problema dado, que se denota por medio del procedimiento Proceso_Normal(), está físicamente separado de los manejadores de excepciones, los que se encuentran después de la instrucción "CASE Catch OF".

      Cuando el procedimiento Exception() se ejecuta el resultado es que guarda, en un variable global definida dentro de la unidad except.pas, su propia dirección de retorno. Además, Exception() siempre retorna FALSE como su valor. Como se ha guardado la dirección de retorno de Exception(), las demás operaciones que implementan el manejo de excepciones pueden luego simular un retorno de la función Exception() pero con el valor TRUE, lo que hace que se ejecute el código envuelto en le ELSE del IF que envuelve al contexto de excepción [Col-91].

      Esto quiere decir que cuando Exception() se ejecuta siempre retorna el valor FALSE, mientras que cuando se intercepta una excepción la forma de procesarla es simular un retorno de la función Exception(), pero con el valor de retorno TRUE. Este elegante truco es muy conocido en ambientes C, en los que las funciones setjump() y longjump() soportan este tipo de comportamiento [Vid-92].

      Lo importante del manejo de excepciones es que en general no se puede saber de antemano cuando se producirá una. El programador puede generar una en un módulo anidado profundamente, o puede ser que un dato recién leido de un archivo tenga un formato incorrecto que provoque una falla en el programa. Independientemente de cómo se genera una excepción, en el ambiente de programación debe definirse como interceptarlas. Por eso el manejo de excepciones depende mucho de la plataforma en que corra el programa. En el caso de los sistemas Unix, el soporte para manejo de excepciones se da por medio de las funciones setjump() y longjump(), aunque los lenguajes de programación para una plataforma a veces incluyen un soporte más extenso, como es el caso de ADA y C++.

      Lo más usual es identificar cada una de las posibles excepciones por medio de un Código de Excepción. La siguiente es pequeña lista de algunas excepciones "famosas" de Turbo Pascal:

CONST
  No_Exception                =   0; { ZERO }

  { DOS Errors: [1..99] }
  File_Not_Found              =   2;
  File_Access_Denied          =   5; {...}

  { I/O Errors: [100..149] }
  Disk_Read_Error             = 100;
  Disk_Write_Error            = 101;
  Disk_Full                   = Disk_Write_Error;
  File_Not_Open               = 103;

  { Critical Errors: [150..199] }
  Device_Write_Fault          = 160;
  Device_Read_Fault           = 161;

  { Fatal Errors: [200..255] }
  Division_by_Zero            = 200;
  Range_Check_Error           = 201;
  Floating_Point_Overflow     = 205;
  Floating_Point_Underflow    = 206;
  Invalid_Floating_Point_Opr  = 207;
      En el caso del Turbo Pascal, lo que sucede es que cuando un procedimiento de biblioteca se encuentra con una falla, o cuando se recibe un código que amerita la cancelación del programa, entonces el ambiente Turbo Pascal invoca a la función apuntada por la variable global System.ExitSave. Como este comportamiento está claramente documentado, para implementar la unidad except.pas bastó usar a ExitSave para interceptar todas las excepciones que se producen en tiempo de ejecución. El código de excepción siempre se encuentra en la variable global System.ExitCode.

La programación con Excepciones [<>] [\/] [/\]

      Una vez que el programador cuenta con la posibilidad de manejar excepciones también necesita poder generarlas en sus programas. Para esto debe utilizar a las rutinas Signal(n), Throw(n) y Re_Throw(). Estas operaciones lo que hacen es simular que en tiempo de ejecución se ha producido la excepción número "n". Por ejemplo, para simular que se ha dado un error por división por cero, el programador puede ejecutar una de estas operaciones:
Throw(Division_by_Zero);      Throw(200);
Signal(Division_by_Zero);     Signal(200);
      La diferencia al regresar a Exception() desde Throw(), Re_Throw() o Signal() es que en este caso el valor retornado por Exception() es TRUE. De esta manera el programador puede depurar el código de manejo de excepciones, o usarlas para cualquier otro propósito en sus programas.

      Cuando se ejecuta alguna de las funciones Throw(), Signal() o Re_Throw(), lo que sucede es que estas operaciones simulan un retorno a la última invocación de la función Exception(), pero el valor retornado es TRUE. Esto implica que dentro de la unidad except.pas debe manetenerse una pila de invocaciones a Exception(). Es muy importante mencionar que para evitar retornar a un contexto de excepción que ya no existe, la última instrucción en todo contexto de excepción debe ser una invocación a la operción Leave_Exception_Context(), la que se encarga de resincronizar la pila interna que contiene except.pas con la pila de ejecución del programa.

      Desgraciadamete, en esta implementación si el programador olvida invocar a Leave_Exception_Context() al final de cada bloque de excepción el resultado es un error que es muy difícil de encontrar, pues el paquete de excepciones tratará de ejecutar procedimientos que ya han terminado, por lo que su pila de ejecución contendrá basura. Encontrar un error de estos es realmente muy difícil, y desgraciadamente no es posible dejar de obligar al programador a invocar a Leave_Exception_Context(); la única salida de este problema es que el compilador provea el manejo de excepciones, de forma que al compilar incluya que haga el trabajo de Leave_Exception_Context().

      En la Figura 1 se muestra cómo se usan los el manejadores de excepciones. La primera vez que se invoque a Exception() el valor retornado será FALSE, con lo que el computador continuará la ejecución en el bloque denotador por Proceso_Normal({...});. Cuando el programador desea invocar al manejador de excepciones, posiblemente porque detecta una situación anómala, entonces debe ejecutar el siguiente código:
      Throw(3); { vuelve a Exception; Catch = 3 }

      También podría ocurrir que un error de tiempo de ejecución produzca una excepción de código número 3 (que significa "Path_Not_Found" en Turbo Pascal), con lo que el manejador de excepciones sería invocado. El resultado será que se transferirá el control de ejecución a la última (más reciente) invocación activa del procedimiento Exception(), y el valor que se retornará será TRUE. Esto hará que el control se transfiera a la parte ELSE del IF, y luego el valor retornado por Catch() será 3.

      El argumento de Throw(3) es el código de error, o número de excepción, que sirve para identificar a cada una de las excepciones para las que el programador ha implementado un manejador de excepciones. En la Figura 1 se incluye código para procesar las excepciones 12, 3, 6 y 8. Si el código enviado por Throw() no es uno de éstos, entonces la cláusula ELSE del CASE se ejecutará. La utilidad de la pila interna de except.pas es precisamente recordar cuáles son todos los contextos de excepción a los que se ha entrado.

      El Re_Throw() actúa como un Throw(), pero envía el control al contexto asociado con el Exception() inmediatamente anterior (si lo enviara al mismo contexto entonces el programa se enciclaría). Re_Throw() simplemente ejecuta un Throw() con el último código de excepción, por lo que debe usarse únicamente dentro del código de manejo de excepciones. Si se ejecuta un Re_Throw() cuando no se ha producido una excepción, el resultado es, como mínimo, desastroso, pues el paquete de excepciones no sabrá cual es el código de excepción que debe retorno.

      Los nodos más viejos que están en la pila de except.pas corresponden a las primeras invocaciones de Exception(). De esta manera los procedimientos de nivel superior puedan atender excepciones que los de nivel inferior no manejan. La cadena de transferencias de control terminaría si se llegara al primer contexto de excepción, pues simplemente se cancelaría el programa para luego regresas el control al sistemas operativo. En este caso, el código de excepción indicará la razón de cancelación del programa.

Excepciones y ADTs [<>] [\/] [/\]

      Un Tipo Abstracto de Datos [ADT] es un módulo que encapsula el acceso a una estructura de datos particular. Los ADTs más conocidos son el Arreglo, la Lista y el Arbol. Además de la modularidad de programación que los ADTs ayudan a alcanzar, también son muy importantes porque con ellos se introdujeron los concepto de constructores y destructores, que son las operaciones del ADT encargadas de inicializar y destruir los objetos que usa un programa.

      Un manejador de excepciones no está completo si no tiene soporte para ADTs. Para esto, es necesario que el manejador de excepciones sea capaz de destruir los objetos que están en un contexto de excepción que debe ser abandonado abruptamente cuando se produce una excepción.

      Como el compilador de Pascal no tiene soporte para excepciones, entonces para implementar el soporte para ADTs es necesario que el programador incluya en sus ADTs un campo que luego le permita al manejador de excepciones invocar a los destructores de los ADTs. Para esto el programador debe incluir dentro de su ADT un campo de tipo TException, que sirve para crear una lista doblemente enlazada que une a todos los ADTs que deben ser destruidos. Este campo tiene un puntero que apunta al objeto a destruir y otro que apunta al destructor del objeto. De esta manera, cuando se produce una excepción, el manejador de excepciones puede invocar al destructor del ADT.

      Para enlazar un ADT a la lista de excepciones el programador debe invocar a la operación:

PROCEDURE Register_ADT(
  VAR ADT;                { Instancia del ADT }
  VAR snode: TException;  { campo dentro del ADT }
      done : POINTER);    { Puntero al destructor }
      En la implementación del destructor del ADT, el programador debe invocar a la operación:
      PROCEDURE UnRegister_ADT(VAR snode: TException);
que se encarga de desligar al ADT de la lista de excepciones.

Throw() vs Signal() [<>] [\/] [/\]

      La diferencia entre Throw() y Signal() es que Signal() no vuelve a un contexto que esté dentro del mismo procedimiento en que es invocado, sino que siempre retorna al contexto más cercano que esté en algún procedimiento que haya invocado, directa o indirectamente, al procedimiento en donde se invoca a Signal(). Signal() ignora los contextos de excepción que están a su mismo nivel, y siempre retorna a los de algún procedimiento llamador.

      Por el contrario, Throw() siempre retorna al contexto de excepción más inmediato, aunque ese contexto se encuentre en el mismo procedimiento en que se invoca a Throw().

      Las operaciones Throw(), Signal() y Re_Throw() tienen el mismo objetivo: transferir el control al llamado de Exception(), pero devolviendo el valor TRUE. El Throw() transfiere control al contexto de excepción más próximo, que puede estar en el mismo procedimiento en el que la invocación Throw() aparece. Signal() actúa diferente, pues inmediatamente causa la terminación del procedimiento en que aparece, y transfiere el control al siguiente contexto de manejo de excepciones. Es perfectamente válido invocar a Throw(), Signal() o Re_Throw() dentro del mismo manejador de excepciones.

      La sutil diferencia entre Throw() y Signal() surge para darle apoyo a una técnica de construcción de programas en la que los módulos se organizan en capas de diferente profundidad. Como estas capas corresponden a la invocación de procedimientos, la diferencia entre Throw() y Signal() puede expresarse de la siguiente manera: Signal() siempre transfiere el control a la capa superior de procesamiento, mientras que Throw() no necesariamente lo hace. En aquellos casos en que el programador no usa contextos de excepción anidados, por lo que en cada momento cualquier procedimiento tiene sólo un contexto para manejo de excepciones activo, no hay diferencia entre Throw() y Signal().

      Esta implementación de Signal() difiere de la propuesta en [Lee-83], en la que invocar a Signal() siempre es equivalente a invocar a Throw() seguido por Re_Throw(). Signal() ignora todos los contextos de manejo de excepciones que están dentro del mismo procedimiento, y bifurca a algún procedimiento llamador.

Programa de ejemplo useexcp.pas [<>] [\/] [/\]

      Para mostrar las cualidades (y defectos) del uso de excepciones hay que hecharle una ojeada al programa useexcp.pas, que realiza un trabajo bastante reducido, aunque ejercita toda la funcionalidad de except.pas. Este programa insistentemente invoca varios procedimientos para mostrar el resultado de usar las excepciones. La forma de ver qué ocurre en el programa es utilizar el depurador simbólico del ambiente de programación para ejecutarlo paso por paso, instrucción por instrucción (con las tecla F7 y F8). Es necesario establecer un punto de corte (breakpoint) para el manejador de excepciones antes de entrar al contexto de excepción, pues cuando se produce una excepción para interceptarla hay que modificar la pila de ejecución del programa, y si de antemano el operador no ha establecido el punto de corte entonces el programa continuará ejecutándose hasta terminar. En cualquier momento es posible ver cuáles son los procedimientos activos examinando la pila de ejecución del programa con la tecla Ctrl-F3.

VAR
  clean : ARRAY[0..5] OF BYTE;
{...}
IF NOT Exception THEN BEGIN
  FOR i := 0 TO 6 DO BEGIN
    clean[i] := 0;      { Range_Check_Error ==> => =>}
  END;                                            {||}
                                                  {||}
  Leave_Exception_Context;                        {\/}
END                                               {  }
ELSE BEGIN                                        {||}
  { Exception handler }                           {\/}
  CASE Catch OF                { <= <= <= <= <= <= <=}
    Range_Check_Error : BEGIN
      WriteLn('clean[',i:1,'] is out of bounce');
      i := 0;
    END;
  ELSE
    EXIT;
  END { CASE }
END; { Exception }
Figura 2

      Todo el trabajo se realiza dentro del procedimiento WORK(). Al principio se define el vector clean[] de 6 componentes con rango [0..5]. La Figura 2 contiene el primer ejemplo de uso de except.pas. Cuando en el contexto de excepción se trata de accesar el campo clean[6], que no existe pues el rango máximo es 5, se produce la excepción de rango y el control pasa al contexto de excepción que envuelve al ciclo FOR en que se produce la falla del programa. Para facilitarle al lector ubicar los puntos de salto, cada punto de salto está marcado con flechas que indican adónde será transferido el control una vez que se produzca la excepción (en este caso el lector debe poner un punto de corte en la instrucción CASE Catch OF, que es adónde comienza el manejador de excepciones).

      Luego se muestra que es factible detectar excepciones de sobre rebase (overflow) cuando dentro de un contexto de excepción se ejecuta la instrucción:
     a := Exp(100000);
En este mismo ejemplo se muestra que la invocación a Re_Thorw() hace que el control pase al contexto de excepción inmediatamente superior.

      Más adelante al invocar al procedimiento Cicle() se muestra que es posible retornar de un procedimiento recursivo usando Signal() o Throw(). Como el procedimiento Cicle() no establece su propio contexto de excepción, el efecto de usar Signal() o Throw() en este caso es el mismo, aunque más adelante, en el procedimiento Throw_vs_Signal(), se muestra la diferencia. En todo momento es válido anidar contextos de excepción, ya sea directamente en el programa, o indirectamente al invocar una rutina que tiene su propio contexto de excepción.

      Algunas veces este paquete de excepciones puede detectar que el programador olvidó liberar un contexto de excepción invocando a Leave_Exception_Context(). Un ejemplo se muestra en la rutina Exception_without_Leave_Exception().

      Otro ejemplo interesante del uso de excepciones es la invocación al procedimiento Deep_Throat(0), que lo único que hace es llamarse recursivamente y de cuando en cuando imprimir un puntico. Eventualmente la pila de ejecución se agota, y se produce una excepción por rebase de la pila del programa. Este ejemplo muestra una de las fortalezas de except.pas, pues pocos programas pueden soportar que se les rebase la pila de ejecución.

************
clean[6] is out of bounce
Overflow found  a := 0.0
==> call Cicle(7)==> call Cicle(6)==> Cicle(6)::Throw(6)
==> call Cicle(9)==> call Cicle(8)==> Cicle(8)::Signal(8)
==> call Cicle(2)==> call Cicle(1)==> call Cicle(0)==> 
Cicle(0)::Throw(0)

 0 1 2 3 Level #3
Throw(25) received ==> Signal(NINE)
a := 246913578.0         i := 0
I will create some exception contexts
==> I will NOT release them!!!  ++++++++++++++++++-----  (40)
As except.pas is dumb, this gets executed
====> I cleaned up the mess!!! [but I should fix the program]
Couldn't fix the exception stack
Exception stack cleared!
Ctrl-Break cannot be detected within the IDE
(0)... Program_Stack_Overflow
clean[6] is out of bounce
ExitProcedure [System.ExitCode = 0]
Figura 3

      A fin de cuentas, el resultado de ejecutar useexcp.pas se muestra en la Figura 3. Algunos de los mensajes fueron producidos por la rutina ExitProcedure() que se ejecuta cuando el programa termina; los otros sirven para ver cómo se desarrolla la ejecución cada vez que se produce una excepción. Al ejecutar a useexcp.pas también se puede observar cómo funciona except.pas, entrando con F7 a ver qué hace cada uno de las rutinas que exporta esa unidad.

Restricciones de except.pas

      Ya se mencionó que esta implementación de excepciones requiere que el programador "recuerde" siempre cerrar cada contexto de excepción invocando a Leave_Exception_Context(). Esto es muy incómodo, pero necesario porque no siempre es posible saber cuál contexto de excepción está todavía activo. Si except.pas no fuera un módulo externo al lenguaje Turbo Pascal, entonces el compilador podría insertar la invocación a Leave_Exception_Context() automáticamente; pero no parece que este tipo de soporte se vaya a incluir para el lenguaje Turbo Pascal.

      El otro problema que debe enfrentar el programador cuando usa el paquete de excepciones es que en algunos casos pueden quedar variables asignadas en memoria dinámica que no pueden destruirse porque para manejar excepción se ha debido abandonar abruptamente un procedimiento. Por ejemplo, si el procedimiento Proc() llama a Explota(), y Explota() crea algunas variables en memoria dinámica, como los punteros a esas variables son valores locales a Explota(), cuando el manejador de excepciones transfiere el control de ejecución a Proc() a causa de una excepción, los punteros a las variables creadas en memoria dinámica por Explota() se pierden porque ya no existen las variables locales de Explota(). El resultado es que queda un poco de memoria dinámica asignada que no puede ser recuperada. Una solución paracial para este problema es que el programador use ADTs, y nuevamente recuerde registrarlos al contexto de excepción al implementar los constructores y destructores, para que el manejador de excepciones se encarga de invocar a sus destructores si han sido debidamente registrados en su contexto de excepción.

      Otra solución al problema de la memoria dinámica es que el programador se preocupe de desasignar la memoria dinámica utilizado en antes de invocar a Throw() o Signal(). Nuevamente, la responsabilidad de restaurar el estado correcto del programa recae sobre el programador, quien para hacerlo estaría obligado a incluir código en el ELSE del IF en que se invoca a Exception(), que es donde se establece el contexto de excepción: este código se encargaría de desasignar la memoria dinámica que haya sido asignada en el contexto de excepción, para luego ejecutar un Re_Throw() que pase la excepción sea al contexto de excepción inmediatamente superior. En [Ich-79] se discute cómo lograr esto.

      Como el código del manejador excepciones tiene acceso a las variables del programa, pues el manejador aparece en el ELSE del IF que implementa el contexto de excepción, el programdor puede encargarse de destruir manualmente las variables de memoria dinámica que haya usado. A veces da pereza destruir esas variables, principalmente si no es posible utilizar el código que las destruye cuando no ha ocurrido una excepción.

      Cuando se produce una excepción el manejador de excepciones la intercepta, y retorna al contexto de excepción adecuado indicando un código de error. Sin embargo, en muchas aplicaciones este código de error no da suficiente información como para resolver adecuadamente el problema que produjo la excepción. Por eso algunos autores exigen que en lugar de un "código", el manejador de excepciones pueda producir un "mensaje" de excepción. Este es el enfoque que se ha usado en C++ [Str-88b]. A todas luces es mejor retornar mensajes que códigos, pero para eso se necesita asistencia del compilador. La solución que aquí se presenta es parcial, pero tiene la ventaja de que es suficiente para muchas como aplicaciones, y es adecuada para que los estudiantes aprendan a escribir programas usando contextos de excepción.

      El problema de como mezclar bien un paquete de excepciones con un eficiente manejador de memoria dinámica es muy difícil de resolver. Por ejemplo, el manejo de excepciones que se incluyó en el lenguaje C++ debió esperar casi dos años antes de ser aceptado, pues el uso excepciones complica la administración de la memoria dinámica.

      Para implementar except.pas ha sido necesario manipular los registros de estado del programa en tiempo de ejecución. El código ha sido probado con las versiones más importantes del compilador Turbo Pascal (v4.0, v5.0, v5.5, v6.0, v7.0) pero podría darse el caso de que cambiara la forma de invocar procedimientos en una versión posterior: entonces debería cambiarse esta implementación. Sin embargo, lo más posible es que no haya que hacer esto cambios para soportar futuras versiones de Turbo Pascal.

      Este paquete sólo da soporte a ambientes MS-DOS monousuario. Si se desea escribir programas que puedan crear tareas concurrentes, será necesario modificar significativamente la implementación de esta biblioteca, pues en estos momentos además de que ha sido implementada usando lenguaje de máquina x86, la pila para anotar cuáles son los contextos de excepción que está dentro de la unidad except.pas es una variable global. Esto impide compartirla entre varias tareas que nacen del mismo programa.

Conclusiones [<>] [\/] [/\]

      Esta implementación obliga al programador a ser cuidadoso al usar excepciones, pues por un pequeño descuido pueden quedar variables asignadas en memoria dinámica "guindando". Pese al cúmulo de restricciones que presenta, esta paquete para el manejo de excepciones en Pascal es muy útil por lo menos en un ambiente académico, pues los estudiantes pueden usarlo para aprender a usar esta importante herramienta de programación. Como Turbo Pascal corre en máquinas muy pequeñas, este paquete puede ser usado prácticamente en cualquier ambiente.

Agradecimientos [<>] [\/] [/\]

      Cuatro personas han trabajado para lograr implementar except.pas. David Chaves fue quien programó inicialmente la mayor parte del código ensamblador de la unidad Except. Luego William Martínez tuvo la paciencia de buscarle errores a la primera implementación. Por último, Gustavo Alonso Sanabria quien tuvo la pacienca de depurar el código. Además, William y Gustavo le hicieron algunas modificaciones al paquete original definido por Adolfo, lo que ha resultado en un sistema más fácil de usar.

Reconocimientos [<>] [\/] [/\]

      Esta investigación se realizó dentro del proyecto de investigación 329-89-019 "Estudios en la tecnología de programación por objetos y C++", inscrito ante la Vicerrectoría de Investigación de la Universidad de Costa Rica. La Escuela de Ciencias de la Computación e Informática también ha aportado fondos para este trabajo.


Bibliografía [<>] [\/] [/\]

[DiM-88] Di Mare, Adolfo: Convenciones de Programación para Pascal, Reporte Técnico ECCI-01-88, Proyecto 326-86-053, Escuela de Ciencias de la Computación e Informática, Universidad de Costa Rica, 1988.
[BI-88] Borland International: Turbo Pascal Reference Manual version 5.5, Borland International, California (U.S.A.), 1988.
[Col-91] Colvin, Gegory: Exception Handling in ANSI C, C/C++ Users Journal, Vol.9 No.8, pp [77-78,83-88], Agosto 1991.
[Ich-79] Ichbiah, J.D et al: Rationale for the Design of the ADA Programming Language, SigPlan Notices, Vol.14 No.6, Junio 1979.
[Ker-86] Kernighan, Brian: El lenguaje de programación C, Prentice Hall; 1986.
[Lee-83] Lee, P. A.: Exception Handling in C Programs, Software Practice and Experience, Vol.13, pp 389-405.
[LG-86] Liskov, Barbara & Guttag, John: Abstraction and Specification in Program Development, McGraw-Hill, 1986.
[Str-86] Stroustrup, Bjarne: The C++ Programming Language, Addison-Wesley, 1986.
[Str-88a] Stroustrup, Bjarne: What is Object-Oriented Programming, IEEE Transactions on Software Engineering, Mayo 1988.
[Str-88b] Stroustrup, Bjarne: C++ Exception Handling, IEEE Transactions on Software Engineering, Mayo 1988.
[Vid-92] Vidal, Carlos: Exception Handling, C/C++ Users Journal, Vol.10 No.9, pp [19-27], Septiembre 1992.


Indice [<>] [\/] [/\]

[-] Resumen
[-] Uso del manejador except.pas
[-] La programación con Excepciones
[-] Excepciones y ADTs
[-] Throw() vs Signal()
[-] Programa de ejemplo useexcp.pas
[-] Restricciones de except.pas
[-] Conclusiones
[-] Agradecimientos
[-] Reconocimientos

Bibliografía
Indice
Acerca del autor
Acerca de este documento
[/\] Principio [<>] Indice [\/] Final


Acerca del autor [<>] [\/] [/\]

Adolfo Di Mare: Investigador costarricense en la Escuela de Ciencias de la Computación e Informática [ECCI] de la Universidad de Costa Rica [UCR], en donde ostenta el rango de Profesor Catedrático. Trabaja en las tecnologías de Programación e Internet. Es Maestro Tutor del Stvdivm Generale de la Universidad Autónoma de Centro América [UACA], en donde ostenta el rango de Catedrático y funge como Consiliario Académico. Obtuvo la Licenciatura en la Universidad de Costa Rica y la Maestría en Ciencias en la Universidad de California, Los Angeles [UCLA].

[mailto] Adolfo Di Mare <adolfo@di-mare.com>


Acerca de este documento [<>] [\/] [/\]

Referencia: Di Mare, Adolfo: Manejo de excepciones en Turbo Pascal, Reporte Técnico ECCI-94-10 (Revisión 4), Proyecto 326-89-019, http:// www.di-mare.com /adolfo/p/ except.htm, Escuela de Ciencias de la Computación e Informática (ECCI), Universidad de Costa Rica (UCR), 1994.
Internet: http://www.di-mare.com/adolfo/p/except.htm
http://www.di-mare.com/adolfo/p/src/except.zip
Autor: Adolfo Di Mare <adolfo@di-mare.com>
Contacto: Apdo 4249-1000, San José Costa Rica
Tel: (506) 207-4020       Fax: (506) 234-8846
Revisión: ECCI-UCR, Marzo 1999
Visitantes:


Copyright © 1997 Adolfo Di Mare
Derechos de autor reservados © 1997 Adolfo Di Mare <adolfo@di-mare.com>
[home] [<>] [/\]