¿Por qué son malas las especificaciones de excepción?

47

Hace más de 10 años, en la escuela, te estaban enseñando a usar los especificadores de excepción. Ya que mi experiencia es como uno de ellos, los programadores de Torvaldish C que evitan obstinadamente a C ++ a menos que sean forzados a hacerlo, solo termino en C ++ esporádicamente, y cuando lo hago, todavía uso especificadores de excepción, ya que eso es lo que me enseñaron.

Sin embargo, la mayoría de los programadores de C ++ parecen fruncir el ceño ante los especificadores de excepción. He leído el debate y los argumentos de varios gurús de C ++, como estos . Hasta donde lo entiendo, se reduce a tres cosas:

  1. Los especificadores de excepciones usan un sistema de tipos que es inconsistente con el resto del idioma ("sistema de tipo de sombra").
  2. Si su función con un especificador de excepción arroja otra cosa que no sea lo que usted especificó, el programa terminará de manera incorrecta e inesperada.
  3. Los especificadores de excepción se eliminarán en el próximo estándar de C ++.

¿Me estoy perdiendo algo aquí o son todas estas razones?

Mis propias opiniones:

Con respecto a 1): ¿Y qué. C ++ es probablemente el lenguaje de programación más inconsistente jamás creado, sintaxis. Tenemos las macros, el goto / labels, la horda (hoard?) De comportamiento indefinido- / no especificado- / implementación definida, los tipos de enteros mal definidos, todas las reglas de promoción de tipo implícito, palabras clave de casos especiales como amigo, auto , registro, explícito ... Y así sucesivamente. Alguien podría probablemente escribir varios libros gruesos de todas las rarezas en C / C ++. Entonces, ¿por qué las personas reaccionan contra esta inconsistencia particular, que es un defecto menor en comparación con muchas otras características mucho más peligrosas del lenguaje?

En relación con 2): ¿No es mi responsabilidad? Hay muchas otras formas en que puedo escribir un error fatal en C ++, ¿por qué este caso en particular es peor? En lugar de escribir throw(int) y luego lanzar Crash_t, puedo afirmar que mi función devuelve un puntero a int, luego hacer un encasillado explícito y devolver un puntero a un Crash_t. El espíritu de C / C ++ siempre ha sido dejar la mayor parte de la responsabilidad al programador.

¿Qué hay de las ventajas entonces? Lo más obvio es que si su función intenta lanzar explícitamente cualquier tipo distinto de lo que especificó, el compilador le dará un error. Creo que la norma es clara respecto a esto (?). Los errores solo ocurrirán cuando su función llame a otras funciones que a su vez arrojan el tipo incorrecto.

Proveniente de un mundo de programas de C incrustados y deterministas, sin duda preferiría saber exactamente qué me ofrecerá una función. Si hay algo en el lenguaje que lo respalde, ¿por qué no usarlo? Las alternativas parecen ser:

void func() throw(Egg_t);

y

void func(); // This function throws an Egg_t

Creo que hay una gran posibilidad de que la persona que llama ignore / olvide implementar el try-catch en el segundo caso, menos en el primer caso.

Tal como lo entiendo, si cualquiera de estos dos formularios decide lanzar repentinamente otro tipo de excepción, el programa se bloqueará. En el primer caso porque no está permitido lanzar otra excepción, en el segundo caso porque nadie esperaba que lanzara una SpanishInquisition_t y, por lo tanto, esa expresión no se encuentra donde debería haber estado.

En el caso de este último, tener una captura de último recurso (...) en el nivel más alto del programa realmente no parece ser mejor que un bloqueo del programa: "Oye, en algún lugar de tu programa algo arrojó un extraño , Excepción no controlada.". No puedes recuperar el programa una vez que estés tan lejos de donde se lanzó la excepción, lo único que puedes hacer es salir del programa.

Y desde el punto de vista del usuario, no les importa si reciben un cuadro de mensaje malvado del sistema operativo que dice "El programa terminó. Blablabla en la dirección 0x12345" o un cuadro de mensaje malvado de su programa que dice "Excepción no controlada" : myclass.func.something ". El error sigue ahí.

Con el próximo estándar de C ++ no tendré otra opción que abandonar los especificadores de excepción. Pero preferiría escuchar algún argumento sólido de por qué son malos, en lugar de "Su Santidad lo ha declarado y por lo tanto es así". Quizás haya más argumentos en contra de ellos que los que enumeré, o tal vez haya más en ellos de lo que creo.

    
pregunta 14.10.2011 - 11:57

3 respuestas

47

Las especificaciones de excepción son malas porque se aplican de forma débil y, por lo tanto, no logran mucho, y también son malas porque obligan al tiempo de ejecución a verificar excepciones inesperadas para que puedan terminar (), en su lugar de invocar UB, esto puede desperdiciar una cantidad significativa de rendimiento.

En resumen, las especificaciones de excepción no se aplican lo suficientemente enérgicamente en el lenguaje para hacer que el código sea más seguro, y su implementación según lo especificado fue una gran pérdida de rendimiento.

    
respondido por el DeadMG 14.10.2011 - 12:08
16

Esta entrevista con Anders Hejlsberg es bastante famosa. En él, explica por qué el equipo de diseño de C # descartó las excepciones marcadas en primer lugar. En pocas palabras, hay dos razones principales: la capacidad de versión y la escalabilidad.

Sé que el OP se está enfocando en C ++ mientras que Hejlsberg está discutiendo sobre C #, pero los puntos que Hejlsberg señala son perfectamente aplicables a C ++ también.

    
respondido por el CesarGon 14.10.2011 - 15:28
16

Una razón por la que nadie los usa es porque su suposición principal es incorrecta:

  

"La [ventaja] más obvia es que si su función intenta lanzar explícitamente   cualquier tipo que no sea lo que usted especificó, el compilador le dará una   error. "

struct foo {};
struct bar {};

struct test
{
    void baz() throw(foo)
    {
        throw bar();
    }
};

int main()
{
    test x;
    try { x.baz(); } catch(bar &b) {}
}

Este programa compila con sin errores ni advertencias .

Además, a pesar de que la excepción hubiera sido detectada , el programa aún termina.

Editar: para responder a un punto de tu pregunta, captura (...) (o mejor, captura (std :: exeption &) u otra clase base, y luego captura (... )) sigue siendo útil incluso si no sabes exactamente qué salió mal.

Considere que su usuario ha presionado un botón de menú para "guardar". El controlador del botón de menú invita a la aplicación a guardar. Esto podría fallar por miles de razones: el archivo estaba en un recurso de red que desapareció, había un archivo de solo lectura y no se podía guardar, etc. Pero al controlador no le importa por qué algo falló; solo le importa que haya tenido éxito o haya fallado. Si tuvo éxito, todo está bien. Si falla, puede avisar al usuario. La naturaleza exacta de la excepción es irrelevante. Además, si escribe un código apropiado, seguro para excepciones, significa que cualquier error de este tipo puede propagarse a través de su sistema sin desactivarlo, incluso para el código que no le importa el error. Esto hace que sea más fácil extender el sistema. Por ejemplo, ahora guarda a través de una conexión de base de datos. ¿Va a propagar el lanzamiento (SQLException) en la pila de funciones hasta el final, o simplemente lo clasificará como "error" y lo manejará correctamente junto con todas las otras cosas que podrían salir mal?

    
respondido por el Kaz Dragon 14.10.2011 - 15:31

Lea otras preguntas en las etiquetas