Articulo de referencia

Manejo de excepciones (programación)

En programación informática , existen varios mecanismos de lenguaje de programación para el manejo de excepciones . El término excepción se usa normalmente para denotar una estr...

En programación informática , existen varios mecanismos de lenguaje de programación para el manejo de excepciones . El término excepción se usa normalmente para denotar una estructura de datos que almacena información sobre una condición excepcional. Un mecanismo para transferir el control, o generar una excepción, se conoce como throw ; se dice que la excepción se ha lanzado . La ejecución se transfiere a un catch .

Uso

Los lenguajes de programación difieren sustancialmente en su noción de lo que es una excepción. Las excepciones pueden usarse para representar y manejar situaciones anormales, impredecibles y erróneas, pero también como estructuras de control de flujo para manejar situaciones normales. Por ejemplo, los iteradores de Python lanzan excepciones StopIteration para indicar que no hay más elementos producidos por el iterador. [ 1 ] Existe desacuerdo dentro de muchos lenguajes sobre lo que constituye un uso idiomático de las excepciones. Por ejemplo, Joshua Bloch afirma que las excepciones de Java solo deben usarse para situaciones excepcionales, [ 2 ] pero Kiniry observa que java.io.FileNotFoundExceptionla clase de Java no es en absoluto un evento excepcional. [ 3 ] De manera similar, Bjarne Stroustrup, autor de C++, afirma que las excepciones de C++ solo deben usarse para el manejo de errores, ya que para eso fueron diseñadas, [ 4 ] pero Kiniry observa que muchos lenguajes modernos como Ada, C++, Modula-3, ML y OCaml, Python y Ruby usan excepciones para el control de flujo. Algunos lenguajes como Eiffel, C#, Common Lisp y Modula-2 han hecho un esfuerzo concertado para restringir el uso de excepciones, aunque esto se hace a nivel social más que técnico. [ 3 ]

Historia

Los primeros compiladores de IBM Fortran tenían instrucciones para probar condiciones excepcionales. Estas incluían las instrucciones IF ACCUMULATOR OVERFLOW, IF QUOTIENT OVERFLOW, y IF DIVIDE CHECK. En aras de la independencia de la máquina, no se incluyeron en FORTRAN IV ni en el estándar Fortran 66. Sin embargo, desde Fortran 2003 es posible probar problemas numéricos mediante llamadas a funciones en el IEEE_EXCEPTIONSmódulo.

El manejo de excepciones de software continuó desarrollándose en las décadas de 1960 y 1970. LISP 1.5 (1958-1961) [ 5 ] permitió que las excepciones fueran generadas por la ERRORpseudofunción, de manera similar a los errores generados por el intérprete o compilador. Las excepciones eran capturadas por la ERRORSETpalabra clave, que devolvía NILen caso de un error, en lugar de terminar el programa o entrar al depurador . [ 6 ] PL/I introdujo su propia forma de manejo de excepciones alrededor de 1964, permitiendo que las interrupciones fueran manejadas con unidades ON. [ 7 ] MacLisp observó que ERRSETy ERRse usaban no solo para generar errores, sino también para el flujo de control no local, y por lo tanto agregó dos nuevas palabras clave, CATCHy THROW(junio de 1972). [ 8 ] El comportamiento de limpieza ahora generalmente llamado "finally" fue introducido en NIL (New Implementation of LISP) a mediados o finales de la década de 1970 como UNWIND-PROTECT. [ 9 ] Esto fue luego adoptado por Common Lisp . Contemporáneo a esto se encontraba dynamic-windScheme, que manejaba excepciones en cierres. Los primeros trabajos sobre el manejo estructurado de excepciones fueron Goodenough (1975a) y Goodenough (1975b) . [ 10 ] El manejo de excepciones fue posteriormente adoptado ampliamente por muchos lenguajes de programación a partir de la década de 1980.

Sintaxis

Muchos lenguajes de programación tienen soporte sintáctico incorporado para excepciones y manejo de excepciones. Esto incluye Ada , BlitzMax , C++ , C# , Clojure , COBOL , D , ECMAScript (por ejemplo, ActionScript , JavaScript ), Eiffel , Java , ML , Object Pascal (por ejemplo, Delphi , Free Pascal ), PowerBuilder , Objective-C , OCaml , Perl , [ 11 ] PHP (a partir de la versión 5), PL/I , PL/SQL , Prolog , Python , REALbasic , Ruby , Scala , Smalltalk , Tcl , Visual Prolog y la mayoría de los lenguajes .NET .

Excluyendo pequeñas diferencias sintácticas, solo se utilizan unos pocos estilos de manejo de excepciones. En el estilo más popular, una excepción se inicia mediante una instrucción especial ( throwo raise) con un objeto de excepción (por ejemplo, con Java u Object Pascal) o un valor de un tipo enumerado extensible especial (por ejemplo, con Ada o SML). El ámbito para los manejadores de excepciones comienza con una cláusula marcadora ( tryo el iniciador de bloque del lenguaje como begin) y termina al comienzo de la primera cláusula manejadora ( catch, except, rescue). Pueden seguir varias cláusulas manejadoras, y cada una puede especificar qué tipos de excepción maneja y qué nombre usa para el objeto de excepción. Como variación menor, algunos lenguajes usan una sola cláusula manejadora, que maneja internamente la clase de la excepción.

También es común una cláusula relacionada ( finallyo ensure) que se ejecuta independientemente de si se produjo una excepción o no, normalmente para liberar los recursos adquiridos dentro del cuerpo del bloque de manejo de excepciones. Cabe destacar que C++ no proporciona esta construcción, recomendando en su lugar la técnica de adquisición de recursos mediante inicialización (RAII), que libera recursos mediante destructores . [ 12 ] Según un artículo de 2008 de Westley Weimer y George Necula , la sintaxis de los bloques try... finallyen Java es un factor que contribuye a los defectos de software. Cuando un método necesita manejar la adquisición y liberación de 3 a 5 recursos, los programadores aparentemente no están dispuestos a anidar suficientes bloques debido a problemas de legibilidad, incluso cuando esta sería una solución correcta. Es posible usar un único bloque try... finallyincluso cuando se trata de múltiples recursos, pero eso requiere un uso correcto de valores centinela , que es otra fuente común de errores para este tipo de problema. [ 13 ] : 8:6–8:7

Python y Ruby también permiten una cláusula ( else) que se utiliza en caso de que no se haya producido ninguna excepción antes de que se alcanzara el final del ámbito del manejador.

En su conjunto, el código de manejo de excepciones podría verse así (en la sintaxis de manejo de excepciones al estilo Java ):

import java.io.IOException ; import java.util.Scanner ;try { Scanner stdin = new Scanner ( System . in ); String line = stdin . nextLine ();if ( line . length () == 0 ) { throw new IOException ( "¡La línea leída desde la consola estaba vacía!" ); }System.out.printf ( "Hola % s ! %n" , line ) ; System.out.println ( " La tarea se ejecutó correctamente." ) ; } catch ( IOException e ) { System.out.println ( " ¡Hola!" ) ; } catch ( Exception e ) { System.out.printf ( " Error : % s%n " , e.getMessage ( ) ) ; } finally { System.out.println ( " El programa está terminando . " ) ; }

C no tiene manejo de excepciones try-catch, sino que usa códigos de retorno para la verificación de errores. Las setjmpfuncioneslongjmp de la biblioteca estándar se pueden usar para implementar el manejo try-catch mediante macros. [ 14 ]

Perl 5 utiliza die`for` throwy `try-catch`. Tiene módulos CPAN que ofrecen semántica try-catch. [ 15 ]eval{}if($@){}

Semántica de terminación y reanudación

When an exception is thrown, the program searches back through the stack of function calls until an exception handler is found. Some languages call for unwinding the stack as this search progresses. That is, if function f, containing a handler H for exception E, calls function g, which in turn calls function h, and an exception E occurs in h, then functions h and g may be terminated, and H in f will handle E. This is said to be termination semantics. Alternately, the exception handling mechanisms may not unwind the stack on entry[note 1] to an exception handler, giving the exception handler the option to restart the computation, resume or unwind. This allows the program to continue the computation at exactly the same place where the error occurred (for example when a previously missing file has become available) or to implement notifications, logging, queries and fluid variables on top of the exception handling mechanism (as done in Smalltalk). Allowing the computation to resume where it left off is termed resumption semantics.

There are theoretical and design arguments in favor of either decision. C++ standardization discussions in 1989–1991 resulted in a definitive decision to use termination semantics in C++.[16]Bjarne Stroustrup cites a presentation by Jim Mitchell as a key data point:

Jim had used exception handling in half a dozen languages over a period of 20 years and was an early proponent of resumption semantics as one of the main designers and implementers of Xerox's Cedar/Mesa system. His message was

“termination is preferred over resumption; this is not a matter of opinion but a matter of years of experience. Resumption is seductive, but not valid.”

He backed this statement with experience from several operating systems. The key example was Cedar/Mesa: It was written by people who liked and used resumption, but after ten years of use, there was only one use of resumption left in the half million line system – and that was a context inquiry. Because resumption wasn't actually necessary for such a context inquiry, they removed it and found a significant speed increase in that part of the system. In each and every case where resumption had been used it had – over the ten years – become a problem and a more appropriate design had replaced it. Basically, every use of resumption had represented a failure to keep separate levels of abstraction disjoint.[10]

Entre los lenguajes de manejo de excepciones con reanudación se incluyen Common Lisp con su sistema de condiciones , PL/I, Dylan, R , [ 17 ] y Smalltalk . Sin embargo, la mayoría de los lenguajes de programación más recientes siguen el modelo de C++ y utilizan semántica de terminación.

En C++, std::uncaught_exceptions()se ofrece la opción de contar el número de excepciones en el hilo actual que se han lanzado/relanzado y que aún no han entrado en un catchbloque correspondiente. Antes de C++20 , se utilizaba otra función std::uncaught_exception()que determinaba si se estaba produciendo o no el desenrollado de la pila. [ 18 ]

Implementación del manejo de excepciones

La implementación del manejo de excepciones en los lenguajes de programación generalmente implica una cantidad considerable de soporte tanto de un generador de código como del sistema de tiempo de ejecución que acompaña a un compilador. (Fue la adición del manejo de excepciones a C++ lo que puso fin a la vida útil del compilador original de C++, Cfront . [ 19 ] ) Dos esquemas son los más comunes. El primero,El registro dinámico genera código que actualiza continuamente las estructuras sobre el estado del programa en términos de manejo de excepciones. [ 20 ] Normalmente, esto agrega un nuevo elemento aldiseño del marco de pilaque sabe qué manejadores están disponibles para la función o método asociado con ese marco; si se lanza una excepción, un puntero en el diseño dirige al entorno de ejecución al código del manejador apropiado. Este enfoque es compacto en términos de espacio, pero agrega sobrecarga de ejecución al entrar y salir del marco. Se usó comúnmente en muchas implementaciones de Ada, por ejemplo, donde la generación compleja y el soporte en tiempo de ejecución ya eran necesarios para muchas otras características del lenguaje.El Manejo Estructurado de Excepciones(SEH) de 32 bits de Microsoft utiliza este enfoque con una pila de excepciones separada. [ 21 ] El registro dinámico, al ser bastante sencillo de definir, es susceptible deprueba de corrección. [ 22 ]

El segundo esquema, y ​​el implementado en muchos compiladores C++ de calidad de producción y en Microsoft SEH de 64 bits , es untable-driven approach. This creates static tables at compile time and link time that relate ranges of the program counter to the program state with respect to exception handling.[23] Then, if an exception is thrown, the runtime system looks up the current instruction location in the tables and determines what handlers are in play and what needs to be done. This approach minimizes executive overhead for the case where an exception is not thrown. This happens at the cost of some space, but this space can be allocated into read-only, special-purpose data sections that are not loaded or relocated until an exception is actually thrown.[24] The location (in memory) of the code for handling an exception need not be located within (or even near) the region of memory where the rest of the function's code is stored. So if an exception is thrown then a performance hit – roughly comparable to a function call[25] – may occur if the necessary exception handling code needs to be loaded/cached. However, this scheme has minimal performance cost if no exception is thrown. Since exceptions in C++ are supposed to be exceptional (i.e. uncommon/rare) events, the phrase "zero-cost exceptions"[note 2] is sometimes used to describe exception handling in C++. Like runtime type identification (RTTI), exceptions might not adhere to C++'s zero-overhead principle as implementing exception handling at run-time requires a non-zero amount of memory for the lookup table.[26] For this reason, exception handling (and RTTI) can be disabled in many C++ compilers, which may be useful for systems with very limited memory[26] (such as embedded systems). This second approach is also superior in terms of achieving thread safety.

In comparison to C++ where any type may be thrown and caught, in Java only types extending java.lang.Throwable can be thrown and caught, and java.lang.Throwable has two direct descendants: java.lang.Error (indicating a serious problem a reasonable program need not catch), and java.lang.Exception (any other condition that a reasonable program may want to catch and handle). java.lang.Error is typically reserved for extremely serious problems beyond the scope of the program, such as java.lang.OutOfMemoryError, java.lang.ThreadDeath, or java.lang.AssertionError.

También se han propuesto otros esquemas de definición e implementación. Para los lenguajes que admiten metaprogramación , se han propuesto enfoques que no implican ninguna sobrecarga (más allá del soporte ya existente para la reflexión ). [ 27 ]

Manejo de excepciones basado en el diseño por contrato

Una visión diferente de las excepciones se basa en los principios del diseño por contrato y está respaldada, en particular, por el lenguaje Eiffel . La idea es proporcionar una base más rigurosa para el manejo de excepciones definiendo con precisión qué es un comportamiento "normal" y "anormal". Específicamente, el enfoque se basa en dos conceptos:

  • Fallo : la incapacidad de una operación para cumplir su contrato. Por ejemplo, una suma puede producir un desbordamiento aritmético (no cumple su contrato de calcular una buena aproximación a la suma matemática); o una rutina puede no cumplir su postcondición.
  • Excepción : un evento anómalo que ocurre durante la ejecución de una rutina (dicha rutina es la " receptora " de la excepción). Este evento anómalo se produce por el fallo de una operación invocada por la rutina.

El "principio de manejo seguro de excepciones", introducido por Bertrand Meyer en su libro "Construcción de software orientado a objetos" , sostiene que solo existen dos maneras significativas en que una rutina puede reaccionar cuando ocurre una excepción:

  • Fallo o "pánico organizado": La rutina corrige el estado del objeto restableciendo el invariante (esta es la parte "organizada") y luego falla (entra en pánico), lo que provoca una excepción en quien la llama (para que el evento anormal no se ignore).
  • Reintentar: La rutina vuelve a intentar el algoritmo, normalmente después de cambiar algunos valores para que el siguiente intento tenga más posibilidades de éxito.

En particular, no está permitido simplemente ignorar una excepción; un bloque debe reintentarse y completarse con éxito, o bien propagar la excepción a quien lo llamó.

Aquí hay un ejemplo expresado en sintaxis Eiffel. Se asume que una rutina send_fastsuele ser la mejor manera de enviar un mensaje, pero puede fallar, provocando una excepción; en ese caso, el algoritmo utiliza a continuación send_slow, que fallará con menos frecuencia. Si send_slowfalla, la rutina senden su conjunto debería fallar, lo que provocará que quien la llama reciba una excepción.

enviar ( m : MENSAJE ) es -- Enviar m a través del enlace rápido, si es posible, de lo contrario a través del enlace lento. local tried_fast , tried_slow : BOOLEAN hacer si tried_fast entonces tried_slow := True enviar_lento ( m ) sino tried_fast := True enviar_fast ( m ) fin rescate si no tried_slow entonces reintentar fin fin

Las variables locales booleanas se inicializan a False al inicio. Si send_fastfalla, el cuerpo ( docláusula) se ejecutará de nuevo, lo que provocará la ejecución de send_slow. Si esta ejecución de send_slowfalla, la rescuecláusula se ejecutará hasta el final sin retry(sin elsecláusula en el final if), lo que provocará que la ejecución de la rutina en su conjunto falle.

Este enfoque tiene la ventaja de definir claramente qué son los casos "normales" y "anormales": un caso anormal, que provoca una excepción, es aquel en el que la rutina no puede cumplir su contrato. Define una distribución clara de roles: la docláusula (cuerpo normal) se encarga de lograr, o intentar lograr, el contrato de la rutina; la rescuecláusula se encarga de restablecer el contexto y reiniciar el proceso, si esto tiene posibilidades de éxito, pero no de realizar ningún cálculo real.

Aunque las excepciones en Eiffel tienen una filosofía bastante clara, Kiniry (2006) critica su implementación porque "las excepciones que forman parte de la definición del lenguaje se representan mediante valores ENTEROS, las excepciones definidas por el desarrollador mediante valores CADENA. [...] Además, como son valores básicos y no objetos, no tienen una semántica inherente más allá de la que se expresa en una rutina auxiliar que necesariamente no puede ser infalible debido a la sobrecarga de representación en efecto (por ejemplo, no se pueden diferenciar dos enteros del mismo valor)". [ 3 ]

C++26 añade soporte para contratos, que se utilizan de la siguiente manera. [ 28 ]

int f ( const int x ) pre ( x != 1 ) // una aserción de precondición post ( r : r == x && r != 2 ) // una aserción de postcondición; r nombra el objeto resultante de f { contract_assert ( x != 3 ); // una declaración de aserción return x ; }

Excepciones no controladas

Las aplicaciones contemporáneas se enfrentan a numerosos desafíos de diseño al considerar estrategias de manejo de excepciones. Particularmente en las aplicaciones empresariales modernas, las excepciones a menudo deben traspasar los límites de los procesos y de las máquinas. Parte del diseño de una estrategia sólida de manejo de excepciones consiste en reconocer cuándo un proceso ha fallado hasta el punto en que la parte de software del proceso no puede manejarlo de manera eficiente. [ 29 ]

Si se lanza una excepción y no se captura (operacionalmente, se lanza una excepción cuando no se especifica ningún controlador aplicable), la excepción no capturada es manejada por el entorno de ejecución; la rutina que hace esto se llamamanejador de excepciones no capturadas . [ 30 ] [ 31 ] El comportamiento predeterminado más común es terminar el programa e imprimir unmensaje de erroren la consola, que generalmente incluye información de depuración como una representación en cadena de la excepción y elrastreo de la pila. [ 30 ] [ 32 ] [ 33 ] Esto a menudo se evita al tener un manejador de nivel superior (nivel de aplicación) (por ejemplo, en unbucle de eventos) que captura las excepciones antes de que lleguen al tiempo de ejecución. [ 30 ] [ 34 ]

Cabe señalar que, si bien una excepción no controlada puede provocar que el programa finalice de forma anormal (el programa puede no ser correcto si no se controla una excepción, en particular al no revertir transacciones parcialmente completadas o al no liberar recursos), el proceso finaliza normalmente (siempre que el entorno de ejecución funcione correctamente), ya que este (que controla la ejecución del programa) puede garantizar un cierre ordenado del proceso.

En un programa multihilo, una excepción no controlada en un hilo puede provocar la terminación únicamente de ese hilo, y no de todo el proceso (las excepciones no controladas en el manejador de nivel de hilo son capturadas por el manejador de nivel superior). Esto es especialmente importante para los servidores, donde, por ejemplo, un servlet (que se ejecuta en su propio hilo) puede terminar sin que el servidor en su conjunto se vea afectado.

Este manejador de excepciones no capturadas predeterminado puede ser sobrescrito, ya sea globalmente o por hilo, por ejemplo para proporcionar un registro alternativo o informes al usuario final de excepciones no capturadas, o para reiniciar hilos que terminan debido a una excepción no capturada. Por ejemplo, en Java esto se hace para un solo hilo a través de Thread.setUncaughtExceptionHandlery globalmente a través de Thread.setDefaultUncaughtExceptionHandler; en Python esto se hace modificando sys.excepthook.

Excepciones comprobadas

Java introdujo la noción de excepciones verificadas, [ 35 ] [ 36 ] que son clases especiales de excepciones. En Java, una excepción verificada es específicamente cualquier excepción java.lang.Throwableque no extienda java.lang.RuntimeExceptiono java.lang.Error. Las excepciones verificadas que un método puede generar deben formar parte de la firma del método . Por ejemplo, si un método puede lanzar una java.io.IOException, debe declarar este hecho explícitamente en la firma de su método. De no hacerlo, se genera un error de compilación. Esto se declararía de la siguiente manera (también con java.util.zip.DataFormatException):

import java.io.File ; import java.io.IOException ; import java.util.zip.DataFormatException ;// Indica que se pueden lanzar IOException y DataFormatException public void operateOnFile ( File f ) throws IOException , DataFormatException { // ... }

Según Hanspeter Mössenböck, las excepciones verificadas son menos convenientes pero más robustas. [ 37 ] Las excepciones verificadas pueden, en tiempo de compilación , reducir la incidencia de excepciones no manejadas que surgen en tiempo de ejecución en una aplicación determinada.

Kiniry escribe que "Como sabe cualquier programador de Java, el volumen de try catchcódigo en una aplicación Java típica a veces es mayor que el código comparable necesario para la comprobación explícita de parámetros formales y valores de retorno en otros lenguajes que no tienen excepciones comprobadas. De hecho, el consenso general entre los programadores de Java en las trincheras es que lidiar con excepciones comprobadas es una tarea casi tan desagradable como escribir documentación. Por lo tanto, muchos programadores informan que "resenten" las excepciones comprobadas". [ 3 ] Martin Fowler ha escrito "...en general creo que las excepciones son buenas, pero las excepciones comprobadas de Java son más problemáticas de lo que valen". [ 38 ] Hasta 2006, ningún lenguaje de programación importante había seguido a Java en la adición de excepciones comprobadas. [ 38 ] Por ejemplo, C# no requiere ni permite la declaración de ninguna especificación de excepción, con lo siguiente publicado por Eric Gunnerson: [ 39 ] [ 3 ] [ 38 ]

"El análisis de programas pequeños lleva a la conclusión de que exigir especificaciones de excepciones podría aumentar tanto la productividad de los desarrolladores como la calidad del código, pero la experiencia con grandes proyectos de software sugiere un resultado diferente: una menor productividad y un aumento mínimo o nulo en la calidad del código."

Anders Hejlsberg describe dos preocupaciones con las excepciones verificadas: [ 40 ]

  • Control de versiones: Un método puede declararse para lanzar excepciones Xy Y. En una versión posterior del código, no se puede lanzar una excepción Zdesde el método, porque haría que el nuevo código fuera incompatible con los usos anteriores. Las excepciones verificadas requieren que quienes llaman al método agreguen Za su cláusula throws o manejen la excepción. Alternativamente, Zpuede representarse erróneamente como un Xo un Y.
  • Escalabilidad: En un diseño jerárquico, cada sistema puede tener varios subsistemas. Cada subsistema puede generar varias excepciones. Cada sistema padre debe gestionar las excepciones de todos los subsistemas inferiores, lo que resulta en un número exponencial de excepciones que deben manejarse. Las excepciones verificadas requieren que todas estas excepciones se gestionen explícitamente.

Para sortear estos problemas, Hejlsberg dice que los programadores recurren a eludir la característica usando una declaración. Otra elusión es usar un manejador (o incluso un ). [ 40 ] Esto se conoce como manejo de excepciones de captura general o manejo de excepciones Pokémon por la frase del programa "¡ Hazte con todos! ". [ 41 ] Los tutoriales de Java desaconsejan el manejo de excepciones de captura general ya que puede capturar excepciones "para las cuales el manejador no fue diseñado". [ 42 ] Otra elusión desaconsejada es hacer que todas las excepciones sean subclases de , [ 43 ] haciendo así que la excepción no sea verificada. Una solución recomendada es usar un manejador de captura general o una cláusula throws pero con una superclase específica de todas las excepciones potencialmente lanzadas en lugar de la superclase general . Otra solución recomendada es definir y declarar tipos de excepción que sean adecuados para el nivel de abstracción del método llamado [ 44 ] y mapear excepciones de nivel inferior a estos tipos usando el encadenamiento de excepciones .throwsExceptiontry{...}catch(Exceptione){...}try{...}catch(Throwablet){...}java.lang.RuntimeExceptionjava.lang.Throwable

Kotlin carece de excepciones verificadas, e incluso lanzar una excepción verificada de Java desde Kotlin no obligará al cliente a manejarla desde Java. Sin embargo, esto se puede habilitar mediante la @Throwsanotación, que emitirá los throwsmetadatos a la JVM. Por ejemplo, considere el siguiente código Kotlin:

import java.io.IOException@Throws ( IOException :: class ) fun readFile () { // ... throw IOException ( "¡Error al leer el archivo!" ) }

Esto se traduciría aproximadamente a lo siguiente en la JVM:

import java.io.IOException ;public static final void readFile () throws IOException { // ... throw new IOException ( "¡Error al leer el archivo!" ); }

Mecanismos similares

Las raíces de las excepciones verificadas se remontan a la noción de especificación de excepciones del lenguaje de programación CLU . [ 45 ] Una función podía generar solo las excepciones listadas en su tipo, pero cualquier excepción que se filtrara de las funciones llamadas se convertiría automáticamente en la única excepción en tiempo de ejecución, failureen lugar de generar un error en tiempo de compilación. [ 46 ] Posteriormente, Modula-3 tuvo una característica similar. [ 47 ] Estas características no incluyen la verificación en tiempo de compilación que es fundamental en el concepto de excepciones verificadas. [ 45 ]

Las primeras versiones del lenguaje de programación C++ incluían un mecanismo opcional similar a las excepciones verificadas, llamado especificación de excepciones . Por defecto, cualquier función podía lanzar cualquier excepción, pero esto podía limitarse mediante una throwcláusula (similar a la cláusula en Java) añadida a la firma de la función, que especificaba qué excepciones podía lanzar la función. Por ejemplo, este código era válido en C++03 :throws

#include <stdexcept>using std :: domain_error ; using std :: invalid_argument ;// Esto podría ser similar a la firma de Java // void performSomeOperation(int a, int b) throws InvalidArgumentException, ArithmeticException; void performSomeOperation ( int a , int b ) throw ( invalid_argument , domain_error ) { // ... }

Las cláusulas de C++ throwpodían especificar cualquier número de tipos, incluso tipos primitivos y clases que no extendían std::exception(ya que C++ admite la generación de excepciones de cualquier tipo). Si no se especificaba ningún tipo en la throwcláusula, la función no generaría ninguna excepción.

Las especificaciones de excepción no se aplicaban en tiempo de compilación. Las violaciones resultaban en la llamada a la función de la biblioteca estándar std::unexpected()[ nota 3 ] . [ 48 ] Se podía dar una especificación de excepción vacía, lo que indicaba que la función no lanzaría ninguna excepción. Esto no se estableció como predeterminado cuando se agregó el manejo de excepciones al lenguaje porque habría requerido demasiada modificación del código existente, habría impedido la interacción con código escrito en otros lenguajes y habría tentado a los programadores a escribir demasiados manejadores a nivel local. [ 48 ] Sin embargo, el uso explícito de especificaciones de excepción vacías podría permitir a los compiladores de C++ realizar optimizaciones significativas de código y diseño de pila que están excluidas cuando el manejo de excepciones puede tener lugar en una función. [ 24 ] Algunos analistas consideraban que el uso adecuado de las especificaciones de excepción en C++ era difícil de lograr. [ 49 ] Este uso de especificaciones de excepciones se incluyó en C++98 y C++03 , se desaprobó en el estándar del lenguaje C++ de 2012 ( C++11 ), [ 50 ] y se eliminó del lenguaje en C++17 . Las cláusulas `throws` se reemplazaron por noexceptcláusulas `throws`. Una función que no lanzará ninguna excepción ahora se denotaría con la noexceptpalabra clave `throws`, y en su lugar se especifica que una función lanzará una excepción. Aunque las cláusulas `throws` se eliminan del lenguaje, escribir solo en la firma es legal y es equivalente a (ninguna excepción especificada por la cláusula denota que no puede lanzar una excepción), sin embargo, esto todavía se considera desaprobado. Para la transición de una base de código que usa cláusulas `throws`, la eliminada se puede redefinir como una macro. Esta es solo una solución temporal para permitir que el código compile, en lugar de una implementación real de excepciones verificadas. Usando esto, se puede imitar de manera similar las cláusulas de Java.noexcept(false)throwthrow()noexceptthrowthrowthrowthrows

// Si throw() está vacío, se expande a noexcept(true) (igual que noexcept), // de lo contrario se expande a noexcept(false). // Una instrucción throw sin paréntesis no se expandirá #define throw(...) noexcept(__VA_OPT__(!)true)importar std ;usando std :: runtime_error ;clase XException : public runtime_error {}; clase YException : public runtime_error {};// throw(Es...) se expandirá a noexcept(false) void performSomeOperation ( int x , int y ) throw ( XException , YException ) { if ( x > y ) { // throw sin corchete no se expandirá como una macro throw XException ( "x > y" ); } else if ( y > x ) { throw YException ( "y > x" ); } std :: println ( "x = y = {}" , x ); }// throw() se expandirá a noexcept(true) void willNotThrow ( int x ) throw () { std :: println ( "x = {}" , x ); }

También se puede especificar que una función sea noexceptcondicional a que otra función sea noexcept, de la siguiente manera:

void mightThrow ();// El primer noexcept es la cláusula noexcept, el segundo es el operador noexcept que se evalúa a un valor booleano void f () noexcept ( noexcept ( mightThrow ()));

Aunque C++ no tiene excepciones verificadas, se puede propagar el objeto lanzado hacia arriba en la pila dentro de un catchbloque escribiendo (sin especificar un objeto). Esto vuelve a lanzar el objeto capturado. Esto permite realizar operaciones dentro del bloque que lo captura, antes de permitir que el objeto continúe propagándose hacia arriba.throw;catch

Existe un analizador de excepciones no capturadas para el lenguaje de programación OCaml . [ 51 ] La herramienta informa el conjunto de excepciones generadas como una firma de tipo extendida. Pero, a diferencia de las excepciones verificadas, la herramienta no requiere anotaciones sintácticas y es externa (es decir, es posible compilar y ejecutar un programa sin haber verificado las excepciones).

En C++, también se puede realizar un manejo de excepciones tipo Pokémon. Al igual que en Java, C++ admite un bloque que captura cualquier objeto lanzado. Sin embargo, tiene la desventaja de no nombrar el objeto capturado, lo que significa que no se puede hacer referencia a él. Esto se debe a que en lenguajes como Java, solo se pueden lanzar excepciones de clases que extienden , mientras que en C++ se puede lanzar cualquier tipo (incluso primitivos), por lo que no existe una forma totalmente segura de almacenar una referencia al objeto capturado por un bloque catch-all.catch(Throwablet)catch(...)catch(...)java.lang.Throwable

importar std ;usando std :: exception ;// Capturar solo excepciones: try { // ... } catch ( const exception & e ) { // Capturar solo excepciones: std :: println ( "Se capturó una excepción: {}" , e . what ()); } catch (...) { // Capturar todos los objetos lanzados: std :: println ( "Se capturó un error desconocido" ); }

El lenguaje Rust , en lugar de usar excepciones en su totalidad, representa las excepciones recuperables como tipos de resultado . [ 52 ] [ 53 ] Esto se representa como Result<T, E>(o expected<T, E>en C++). La ventaja de los tipos de resultado sobre las excepciones verificadas es que, si bien ambos tipos de resultado y excepciones verificadas obligan a los usuarios a manejar los errores de inmediato, también pueden representarse directamente como un tipo de retorno dentro del sistema de tipos del lenguaje , a diferencia de las excepciones verificadas donde la excepción declarada potencialmente lanzada es parte de la firma de la función pero no es directamente parte de su tipo de retorno.

Verificación dinámica de excepciones

El objetivo de las rutinas de manejo de excepciones es garantizar que el código pueda gestionar condiciones de error. Para comprobar la robustez de estas rutinas, es necesario someter el código a un amplio espectro de entradas no válidas o inesperadas, como las que se pueden generar mediante inyección de fallos de software y pruebas de mutación (también conocidas como pruebas de fuzzing ). Uno de los tipos de software más difíciles para escribir rutinas de manejo de excepciones es el software de protocolo, ya que una implementación robusta de protocolo debe estar preparada para recibir entradas que no cumplan con las especificaciones pertinentes.

In order to ensure that meaningful regression analysis can be conducted throughout a software development lifecycle process, any exception handling testing should be highly automated, and the test cases must be generated in a scientific, repeatable fashion. Several commercially available systems exist that perform such testing.

In runtime engine environments such as Java or .NET, there exist tools that attach to the runtime engine and every time that an exception of interest occurs, they record debugging information that existed in memory at the time the exception was thrown (call stack and heap values). These tools are called automated exception handling or error interception tools and provide 'root-cause' information for exceptions.

Asynchronous exceptions

Asynchronous exceptions are events raised by a separate thread or external process, such as pressing Ctrl-C to interrupt a program, receiving a signal, or sending a disruptive message such as "stop" or "suspend" from another thread of execution.[54][55] Whereas synchronous exceptions happen at a specific throw statement, asynchronous exceptions can be raised at any time. It follows that asynchronous exception handling can't be optimized out by the compiler, as it cannot prove the absence of asynchronous exceptions. They are also difficult to program with correctly, as asynchronous exceptions must be blocked during cleanup operations to avoid resource leaks.

Programming languages typically avoid or restrict asynchronous exception handling, for example C++ forbids raising exceptions from signal handlers, and Java has deprecated the use of its java.lang.ThreadDeath error in Java 20 that was used to allow one thread to stop another one.[56] Another feature is a semi-asynchronous mechanism that raises an asynchronous exception only during certain operations of the program. For example, Java's java.lang.Thread::interrupt() only affects the thread when the thread calls an operation that throws java.lang.InterruptedException.[57] The similar POSIX pthread_cancelAPI has race conditions which make it impossible to use safely.[58]

Condition systems

Common Lisp , R , [ 59 ] Dylan y Smalltalk tienen un sistema de condiciones [ 60 ] (véase Sistema de condiciones de Common Lisp ) que abarca los sistemas de manejo de excepciones mencionados anteriormente. En esos lenguajes o entornos, la aparición de una condición (una "generalización de un error" según Kent Pitman ) implica una llamada a una función, y solo al final del manejador de excepciones se puede tomar la decisión de desenrollar la pila.

Las condiciones son una generalización de las excepciones. Cuando surge una condición, se busca y selecciona, en orden de pila, un manejador de condiciones apropiado para gestionarla. Las condiciones que no representan errores pueden quedar sin ser gestionadas; su único propósito puede ser proporcionar sugerencias o advertencias al usuario. [ 61 ]

Excepciones continuables

Esto se relaciona con el llamado modelo de reanudación del manejo de excepciones, en el que algunas excepciones se consideran continuables : se permite regresar a la expresión que generó la excepción, después de haber tomado medidas correctivas en el manejador. El sistema de condiciones se generaliza así: dentro del manejador de una condición no grave (también conocida como excepción continuable ), es posible saltar a puntos de reinicio predefinidos (también conocidos como reinicios ) que se encuentran entre la expresión que generó la excepción y el manejador de la condición. Los reinicios son funciones cerradas sobre un entorno léxico, lo que permite al programador reparar dicho entorno antes de salir completamente del manejador de la condición o desenrollar la pila, incluso parcialmente.

Un ejemplo es la condición ENDPAGE en PL/I; la unidad ON podría escribir las líneas de pie de página y las líneas de encabezado para la página siguiente, y luego continuar para reanudar la ejecución del código interrumpido.

Reinicia el mecanismo por separado de la política.

Además, el manejo de condiciones proporciona una separación entre mecanismo y política . Los reinicios ofrecen diversos mecanismos posibles para recuperarse de un error, pero no seleccionan el mecanismo apropiado en una situación dada. Esa es la función del manejador de condiciones, que (al estar ubicado en código de nivel superior) tiene acceso a una perspectiva más amplia.

Un ejemplo: supongamos que existe una función de biblioteca cuyo propósito es analizar una única entrada de un archivo syslog . ¿Qué debería hacer esta función si la entrada está mal formada? No hay una única respuesta correcta, ya que la misma biblioteca podría utilizarse en programas con diversos fines. En un navegador interactivo de archivos de registro, lo correcto sería devolver la entrada sin analizar para que el usuario pueda verla; pero en un programa automatizado de resumen de registros, lo correcto sería proporcionar valores nulos para los campos ilegibles, pero abortar con un error si hay demasiadas entradas mal formadas.

Es decir, la pregunta solo puede responderse en términos de los objetivos más amplios del programa, que no son conocidos por la función de la biblioteca de propósito general. Sin embargo, salir con un mensaje de error rara vez es la respuesta correcta. Por lo tanto, en lugar de simplemente salir con un error, la función puede establecer reinicios que ofrecen varias formas de continuar; por ejemplo, omitir la entrada del registro, proporcionar valores predeterminados o nulos para los campos ilegibles, solicitar al usuario los valores faltantes o desenrollar la pila y abortar el procesamiento con un mensaje de error. Los reinicios ofrecidos constituyen los mecanismos disponibles para recuperarse de un error; la selección del reinicio por parte del manejador de condiciones proporciona la política .

Crítica

El manejo de excepciones a menudo no se maneja correctamente en el software, especialmente cuando existen múltiples fuentes de excepciones; un análisis del flujo de datos de 5 millones de líneas de código Java encontró más de 1300 defectos en el manejo de excepciones. [ 13 ] Citando múltiples estudios previos de otros autores (1999–2004) y sus propios resultados, Weimer y Necula escribieron que un problema significativo con las excepciones es que "crean rutas de flujo de control ocultas que son difíciles de comprender para los programadores". [ 13 ] : 8:27 "Si bien try-catch-finally es conceptualmente simple, tiene la descripción de ejecución más complicada en la especificación del lenguaje [Gosling et al. 1996] y requiere cuatro niveles de "if" anidados en su descripción oficial en inglés. En resumen, contiene una gran cantidad de casos límite que los programadores a menudo pasan por alto." [ 13 ] : 8:13–8:14

Exceptions, as unstructured flow, increase the risk of resource leaks (such as escaping a section locked by a mutex, or one temporarily holding a file open) or inconsistent state. There are various techniques for resource management in the presence of exceptions, most commonly combining the dispose pattern with some form of unwind protection (like a finally clause), which automatically releases the resource when control exits a section of code.

Tony Hoare in 1980 described the Ada programming language as having "...a plethora of features and notational conventions, many of them unnecessary and some of them, like exception handling, even dangerous. [...] Do not allow this language in its present state to be used in applications where reliability is critical [...]. The next rocket to go astray as a result of a programming language error may not be an exploratory space rocket on a harmless trip to Venus: It may be a nuclear warhead exploding over one of our own cities."[62]

The Go developers believe that the try-catch-finally idiom obfuscates control flow,[63] and introduced the exception-like panic/recover mechanism.[64]recover() differs from catch in that it can only be called from within a defer code block in a function, so the handler can only do clean-up and change the function's return values, and cannot return control to an arbitrary point within the function.[65] The defer block itself functions similarly to a finally clause.

The Rust language does not have exceptions. It instead uses Result<T,E> (a result type) for handling runtime errors, and for serious errors the panic!() macro is used.

See also

Notes

  1. In, e.g., PL/I, a normal exit from an exception handler unwinds the stack.
  2. There is "zero [processing] cost" only if no exception is throw (although there will be a memory cost since memory is needed for the lookup table). There is a (potentially significant) cost if an exception is thrown (that is, if throw is executed). Implementing exception handling may also limit the possible compiler optimizations that may be performed.
  3. Tenga en cuenta que esta función se eliminó en C++17 y el nombre se reintrodujo posteriormente en C++23 como unaclase de tipo de resultadostd::unexpected<T, E> .

Referencias

  1. "Excepciones integradas — Documentación de Python 3.10.4" . docs.python.org . Consultado el 17 de mayo de 2022 .
  2. Bloch, Joshua (2008). «Punto 57: Utilice excepciones solo para situaciones excepcionales» . Effective Java (2.ª ed.). Addison-Wesley. pág . 241. ISBN   978-0-321-35668-0.
  3. 1 2 3 4 5 Kiniry, JR (2006). "Excepciones en Java y Eiffel: Dos extremos en el diseño y la aplicación de excepciones". Temas avanzados en técnicas de manejo de excepciones (PDF) . Notas de clase en ciencias de la computación. Vol. 4119. págs. 288–300 . doi : 10.1007/11818502_16 . ISBN   978-3-540-37443-5. S2CID 33283674 . 
  4. "Stroustrup: Preguntas frecuentes sobre estilo y técnica de C++" . www.stroustrup.com . Archivado del original el 2 de febrero de 2018. Consultado el 5 de mayo de 2018 .
  5. McCarthy, John (12 de febrero de 1979). "Historia de Lisp" . www-formal.stanford.edu . Consultado el 13 de enero de 2022 .
  6. McCarthy, John; Levin, Michael I.; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P. (14 de julio de 1961). Manual del programador de LISP 1.5 (PDF) . Recuperado el 13 de enero de 2022 .
  7. "La instrucción ON" (PDF) . Sistema operativo IBM System/360, especificaciones del lenguaje PL/I (PDF) . IBM. Julio de 1966. pág. 120. C28-6571-3. 
  8. Gabriel y Steele 2008 , pág. 3.
  9. White 1979 , pág. 194.
  10. 1 2 Stroustrup 1994 , pág. 392.
  11. "Excepciones - Documentación para el manejo de excepciones en Perl" . MetaCPAN .
  12. Stroustrup, Bjarne. "Preguntas frecuentes sobre estilo y técnica de C++" . www.stroustrup.com . Consultado el 12 de enero de 2022 .
  13. 1 2 3 4 Weimer, W; Necula, GC (2008). "Situaciones excepcionales y fiabilidad de los programas" (PDF) . ACM Transactions on Programming Languages ​​and Systems . Vol. 30, n.º 2. Archivado (PDF) del original el 23 de septiembre de 2015.  
  14. Roberts, Eric S. (21 de marzo de 1989). Implementación de excepciones en C (PDF) (Informe técnico). DEC Systems Research Center . SRC-RR-40 . Recuperado el 4 de enero de 2022 .
  15. Christiansen, Tom; Torkington, Nathan (2003). "10.12. Manejo de excepciones". Perl cookbook (2.ª ed.). Pekín: O'Reilly. ISBN  0-596-00313-7.
  16. Stroustrup 1994 , 16.6 Manejo de excepciones: reanudación frente a terminación, págs. 390–393.
  17. "R: Manejo y recuperación de condiciones" . search.r-project.org . Consultado el 5 de diciembre de 2022 .
  18. "std::uncaught_exception, std::uncaught_exceptions" . cppreference.com . cppreference . Consultado el 21 de noviembre de 2025 .
  19. Scott Meyers , El software C++ más importante... de todos los tiempos. Archivado el 28 de abril de 2011 en Wayback Machine , 2006.
  20. D. Cameron, P. Faust, D. Lenkov, M. Mehta, "Una implementación portátil del manejo de excepciones de C++", Actas de la Conferencia de C++ (agosto de 1992) USENIX .
  21. Peter Kleissner (14 de febrero de 2009). "Manejo de excepciones de Windows - Peter Kleissner" . Archivado del original el 14 de octubre de 2013. Consultado el 21 de noviembre de 2009 .Sección de manejo estructurado de excepciones basado en compilador
  22. Graham Hutton, Joel Wright, " Compilación correcta de excepciones archivadas el 11 de septiembre de 2014 en Wayback Machine ". Actas de la 7.ª Conferencia Internacional sobre Matemáticas de la Construcción de Programas , 2004.
  23. Lajoie, Josée (marzo-abril de 1994). "Manejo de excepciones: soporte del mecanismo de tiempo de ejecución". C++ Report . 6 (3).
  24. 1 2 Schilling, Jonathan L. (agosto de 1998). "Optimizing away C++ exception handling" . SIGPLAN Notices . 33 (8): 40– 47. doi : 10.1145/286385.286390 . S2CID 1522664 . 
  25. "Mejores prácticas modernas de C++ para el manejo de excepciones y errores" . Microsoft . 8 de marzo de 2021. Consultado el 21 de marzo de 2022 .
  26. 1 2 Stroustrup, Bjarne (18 de noviembre de 2019). "Excepciones y alternativas de C++" (PDF) . Recuperado el 23 de marzo de 2022 .
  27. M. Hof, H. Mössenböck, P. Pirkelbauer, " Manejo de excepciones sin sobrecarga mediante metaprogramación Archivado el 3 de marzo de 2016 en Wayback Machine ", Actas de SOFSEM'97 , noviembre de 1997, Lecture Notes in Computer Science 1338 , págs. 423-431.
  28. "Contratos para C++" (PDF) . 13 de febrero de 2025.
  29. Todas las excepciones se manejan, Jim Wilcox, "Todas las excepciones se manejan" . 22 de febrero de 2008.
  30. 1 2 3 Biblioteca para desarrolladores de Mac , " Excepciones no capturadas archivadas el 4 de marzo de 2016 en Wayback Machine "
  31. MSDN , Evento AppDomain.UnhandledException archivado el 4 de marzo de 2016 en Wayback Machine
  32. El tutorial de Python , " 8. Errores y excepciones " Archivado el 1 de septiembre de 2015 en Wayback Machine .
  33. "Java Practices -> Proporcionar un manejador de excepciones no capturadas" . www.javapractices.com . Archivado del original el 9 de septiembre de 2016. Consultado el 5 de mayo de 2018 .
  34. PyMOTW (Módulo de Python de la semana), " Manejo de excepciones Archivado el 15/09/2015 en Wayback Machine "
  35. "Google Answers: El origen de las excepciones verificadas" . Archivado del original el 6 de agosto de 2011. Consultado el 15 de diciembre de 2011 .
  36. Especificación del lenguaje Java, capítulo 11.2. http://java.sun.com/docs/books/jls/third_edition/html/exceptions.html#11.2 Archivado el 8 de diciembre de 2006 en Wayback Machine
  37. Mössenböck, Hanspeter (25 de marzo de 2002). "C# avanzado: número variable de parámetros" (PDF) . Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik. pag. 32. Archivado (PDF) desde el original el 20 de septiembre de 2011 . Consultado el 5 de agosto de 2011 . 
  38. 1 2 3 Eckel, Bruce (2006). Pensando en Java (4.ª ed.). Upper Saddle River, NJ: Prentice Hall. págs. 347–348 . ISBN   0-13-187248-6.
  39. Gunnerson, Eric (9 de noviembre de 2000). "C# y especificaciones de excepciones" . Archivado del original el 1 de enero de 2006.
  40. 1 2 Bill Venners; Bruce Eckel (18 de agosto de 2003). "El problema de las excepciones verificadas: una conversación con Anders Hejlsberg, parte II" . Recuperado el 4 de enero de 2022 .
  41. Juneau, Josh (31 de mayo de 2017). Java 9 Recipes: A Problem-Solution Approach . Apress. p. 226. ISBN  978-1-4842-1976-8.
  42. "Ventajas de las excepciones (Tutoriales de Java: Clases esenciales: Excepciones)" . Download.oracle.com. Archivado del original el 26/10/2011 . Consultado el 15/12/2011 .
  43. "Excepciones no controladas: la controversia (Tutoriales de Java: Clases esenciales: Excepciones)" . Download.oracle.com. Archivado del original el 17 de noviembre de 2011. Consultado el 15 de diciembre de 2011 .
  44. Bloch 2001:178 Bloch, Joshua (2001). Guía eficaz del lenguaje de programación Java . Addison-Wesley Professional. ISBN 978-0-201-31005-4.
  45. 1 2 "Bruce Eckel's MindView, Inc: ¿Necesita Java excepciones verificadas?" . Mindview.net. Archivado del original el 5 de abril de 2002. Recuperado el 15 de diciembre de 2011 .
  46. Liskov, BH; Snyder, A. (noviembre de 1979). "Gestión de excepciones en CLU" (PDF) . IEEE Transactions on Software Engineering . SE-5 (6): 546– 558. Bibcode : 1979ITSEn...5..546L . doi : 10.1109/TSE.1979.230191 . S2CID 15506879. Consultado el 19 de diciembre de 2021 . 
  47. "Módulo-3 - Tipos de procedimiento" ..cs.columbia.edu. 8 de marzo de 1995. Archivado del original el 9 de mayo de 2008. Consultado el 15 de diciembre de 2011 .
  48. 1 2 Bjarne Stroustrup , El lenguaje de programación C++ , Tercera edición, Addison Wesley , 1997. ISBN 0-201-88954-4págs. 375-380.
  49. Reeves, JW (julio de 1996). "Diez directrices para las especificaciones de excepciones". C++ Report . 8 (7).
  50. Sutter, Herb (3 de marzo de 2010). "Informe de viaje: Reunión de estándares ISO C++ de marzo de 2010" . Archivado del original el 23 de marzo de 2010. Recuperado el 24 de marzo de 2010 .
  51. "OcamlExc - Un analizador de excepciones no capturadas para Objective Caml" . Caml.inria.fr. Archivado del original el 6 de agosto de 2011. Consultado el 15 de diciembre de 2011 .
  52. "std::result - Rust" . doc.rust-lang.org . Archivado del original el 09/10/2023 . Consultado el 09/10/2023 .
  53. "stdlib: Agregar módulo de resultados · rust-lang/rust@c1092fb" . github.com . 2011-10-29. Archivado del original el 2023-10-09 . Recuperado el 2023-10-09 .
  54. "Excepciones asíncronas en Haskell - Marlow, Jones, Moran (ResearchIndex)" . Citeseer.ist.psu.edu. Archivado del original el 23 de febrero de 2011. Consultado el 15 de diciembre de 2011 .
  55. Freund, Stephen N.; Mitchell, Mark P. Excepciones asíncronas seguras para Python (PDF) (Informe técnico) . Consultado el 4 de enero de 2022 .
  56. "Desuso de la primitiva de hilo de Java" . Java.sun.com. Archivado del original el 26 de abril de 2009. Consultado el 15 de diciembre de 2011 .
  57. "Interrupciones (Tutoriales de Java™ > Clases esenciales de Java > Concurrencia)" . docs.oracle.com . Consultado el 5 de enero de 2022 .
  58. Felker, Rich. "Cancelación de hilos y fugas de recursos" . ewontfix.com . Consultado el 5 de enero de 2022 .
  59. "R: Manejo y recuperación de condiciones" . search.r-project.org . Consultado el 25 de marzo de 2024 .
  60. De qué se tratan realmente las condiciones (excepciones) (24 de marzo de 2008). "De qué se tratan realmente las condiciones (excepciones)" . Danweinreb.org. Archivado del original el 1 de febrero de 2013. Consultado el 18 de septiembre de 2014 .
  61. "9.1 Conceptos del sistema de condiciones" . Franz.com. 25 de julio de 2022. Archivado del original el 7 de junio de 2024. Consultado el 7 de junio de 2024 .
  62. CAR Hoare. "La ropa vieja del emperador". Conferencia del Premio Turing de 1980.
  63. "Preguntas frecuentes" . Archivado del original el 3 de mayo de 2017. Consultado el 27 de abril de 2017. Creemos que acoplar excepciones a una estructura de control, como en el patrón try-catch-finally, da como resultado un código complejo. Además, tiende a incentivar a los programadores a etiquetar como excepcionales demasiados errores comunes, como no poder abrir un archivo.
  64. Pánico y recuperación Archivado el 24/10/2013 en Wayback Machine , Ir a wiki
  65. Bendersky, Eli (8 de agosto de 2018). "Sobre los usos y abusos de los pánicos en Go" . Sitio web de Eli Bendersky . Recuperado el 5 de enero de 2022. La limitación específica es que recover solo se puede llamar en un bloque de código defer, que no puede devolver el control a un punto arbitrario, sino que solo puede realizar limpiezas y ajustar los valores de retorno de la función.

Obras citadas

  • Gabriel, Richard P.; Steele , Guy L. (2008). Un patrón de evolución del lenguaje (PDF) . LISP50: Celebrando el 50.º aniversario de Lisp. págs. 1–10 . doi : 10.1145/1529966.1529967 . ISBN  978-1-60558-383-9.
  • Goodenough, John B. (1975a). Manejo estructurado de excepciones . Actas del 2.º simposio ACM SIGACT-SIGPLAN sobre principios de lenguajes de programación - POPL '75. págs. 204–224 . doi : 10.1145/512976.512997 . 
  • Goodenough, John B. (1975). "Manejo de excepciones: Problemas y una notación propuesta" (PDF) . Communications of the ACM . 18 (12): 683– 696. CiteSeerX 10.1.1.122.7791 . doi : 10.1145/361227.361230 . S2CID 12935051 .  
  • Stroustrup, Bjarne (1994). El diseño y la evolución de C++ (1.ª  ed.). Reading, Mass.: Addison-Wesley. ISBN 0-201-54330-3.
  • White, Jon L (mayo de 1979). NIL - Una perspectiva (PDF) . Actas de la Conferencia de Usuarios de Macsyma de 1979.