|
|
Adolfo Di Mare
|
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].
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:
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- 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;
Throw()
,
Signal()
o Re_Throw()
se regresa a donde
se llamó al último Exception()
.
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:
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 globalCONST 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;
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
.
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:
La diferencia al regresar aThrow(Division_by_Zero); Throw(200); Signal(Division_by_Zero); Signal(200);
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 1
, 2
,
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.
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:
En la implementación del destructor del ADT, 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 }
PROCEDURE UnRegister_ADT(VAR snode: TException);
Throw()
vs Signal()
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.
useexcp.pas
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
.
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.
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.
except.pas
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.
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.
[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.
|
[-] | 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
|
Adolfo Di Mare <adolfo@di-mare.com>
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: |
|
|