Tiendo a agregar muchas afirmaciones a mi código C ++ para facilitar la depuración sin afectar el rendimiento de las versiones de lanzamiento. Ahora, assert
es una macro C pura diseñada sin los mecanismos de C ++ en mente.
C ++, por otro lado std::logic_error
, define , que debe lanzarse en los casos en que haya un error en la lógica del programa (de ahí el nombre). Lanzar una instancia podría ser la alternativa perfecta, más C ++ ish a assert
.
El problema es que assert
, y abort
tanto por terminado el programa inmediatamente sin llamar a los destructores, por lo tanto, sin esperar la limpieza, mientras que lanzar una excepción de tiempo de ejecución añade manualmente los costos innecesarios. Una forma de evitar esto sería crear una macro de aserción propia SAFE_ASSERT
, que funciona como la contraparte de C, pero arroja una excepción en caso de falla.
Puedo pensar en tres opiniones sobre este problema:
- Cíñete a la afirmación de C. Dado que el programa se termina inmediatamente, no importa si los cambios se han desenrollado correctamente. Además, usar
#define
s en C ++ es igual de malo. - Lanza una excepción y capturala en main () . Permitir que el código omita los destructores en cualquier estado del programa es una mala práctica y debe evitarse a toda costa, al igual que las llamadas a terminate (). Si se lanzan excepciones, deben detectarse.
- Lanza una excepción y deja que termine el programa. Una excepción que finaliza un programa está bien y, debido a
NDEBUG
esto, esto nunca sucederá en una versión de lanzamiento. La captura es innecesaria y expone los detalles de implementación del código interno amain()
.
¿Existe una respuesta definitiva a este problema? ¿Alguna referencia profesional?
Editado: Saltar destructores no es, por supuesto, un comportamiento indefinido.
fuente
logic_error
es el error lógico. Un error en la lógica del programa se llama error. No resuelve errores lanzando excepciones.static_assert
donde sea apropiado si lo tiene disponible.std::bug
?std::abort()
; simplemente generará una señal que hará que el proceso termine.Respuestas:
Las afirmaciones son completamente apropiadas en código C ++. Las excepciones y otros mecanismos de manejo de errores no están pensados para lo mismo que las afirmaciones.
El manejo de errores es para cuando existe la posibilidad de recuperar o informar un error al usuario. Por ejemplo, si hay un error al intentar leer un archivo de entrada, es posible que desee hacer algo al respecto. Los errores pueden deberse a errores, pero también pueden ser simplemente la salida adecuada para una entrada determinada.
Las afirmaciones son para cosas como verificar que se cumplan los requisitos de una API cuando la API normalmente no se verificaría, o para verificar cosas que el desarrollador cree que está garantizado por construcción. Por ejemplo, si un algoritmo requiere una entrada ordenada, normalmente no lo comprobaría, pero es posible que tenga una afirmación para comprobarlo para que las compilaciones de depuración marquen ese tipo de error. Una afirmación siempre debe indicar un programa que funciona incorrectamente.
Si está escribiendo un programa en el que un apagado incorrecto podría causar un problema, es posible que desee evitar las afirmaciones. El comportamiento indefinido estrictamente en términos del lenguaje C ++ no califica como tal problema aquí, ya que hacer una afirmación probablemente ya sea el resultado de un comportamiento indefinido o la violación de algún otro requisito que podría evitar que una limpieza funcione correctamente.
Además, si implementa aserciones en términos de una excepción, potencialmente podría ser detectado y 'manejado' aunque esto contradiga el propósito mismo de la aserción.
fuente
3
lugar de1
a su código, en general, no debería desencadenar una aserción. Las afirmaciones son solo errores del programador, no del usuario de la biblioteca o error de la aplicación.Las afirmaciones son para depurar . El usuario de su código enviado nunca debería verlos. Si se acierta una afirmación, su código debe corregirse.
CWE-617: Afirmación accesible
Las excepciones son por circunstancias excepcionales . Si se encuentra uno, el usuario no podrá hacer lo que quiere, pero puede continuar en otro lugar.
El manejo de errores es para el flujo normal del programa. Por ejemplo, si le pide al usuario un número y obtiene algo que no se puede analizar, eso es normal , porque la entrada del usuario no está bajo su control y siempre debe manejar todas las situaciones posibles de forma natural. (Por ejemplo, repita hasta que tenga una entrada válida, diciendo "Lo siento, inténtelo de nuevo" en el medio).
fuente
Las afirmaciones se pueden usar para verificar invariantes de implementación interna, como el estado interno antes o después de la ejecución de algún método, etc. Si la afirmación falla, significa que la lógica del programa está rota y no puede recuperarse de esto. En este caso, lo mejor que puede hacer es romper lo antes posible sin pasar una excepción al usuario. Lo realmente bueno de las afirmaciones (al menos en Linux) es que el volcado del núcleo se genera como resultado de la terminación del proceso y, por lo tanto, puede investigar fácilmente el seguimiento de la pila y las variables. Esto es mucho más útil para comprender la falla lógica que el mensaje de excepción.
fuente
¡No ejecutar destructores debido a abortar () no es un comportamiento indefinido!
Si lo fuera, también sería un comportamiento indefinido llamar
std::terminate()
, y entonces, ¿cuál sería el punto de proporcionarlo?assert()
es tan útil en C ++ como en C. Las afirmaciones no son para el manejo de errores, son para abortar el programa inmediatamente.fuente
abort()
es para abortar el programa de inmediato. Sin embargo, tiene razón en que las aserciones no son para el manejo de errores, sin embargo, assert intenta manejar el error abortando. ¿No debería lanzar una excepción y dejar que la persona que llama maneje el error si puede? Después de todo, la persona que llama está en una mejor posición para determinar si la falla de una función hace que no valga la pena hacer otra cosa. Tal vez la persona que llama está tratando de hacer tres cosas no relacionadas y aún podría completar los otros dos trabajos y simplemente descartar este.assert
está definido para llamarabort
(cuando la condición es falsa). En cuanto a lanzar excepciones, no, eso no siempre es apropiado. Algunas cosas no pueden ser manejadas por la persona que llama. La persona que llama no puede determinar si un error lógico en una función de biblioteca de terceros es recuperable o si se pueden corregir datos corruptos.En mi humilde opinión, las afirmaciones son para verificar condiciones que, si se violan, hacen que todo lo demás sea una tontería. Y por tanto no puedes recuperarte de ellos o más bien, recuperarse es irrelevante.
Los agruparía en 2 categorías:
Ambos son ejemplos triviales pero no demasiado alejados de la realidad. Por ejemplo, piense en algoritmos ingenuos que devuelven índices negativos para usar con vectores. O programas integrados en hardware personalizado. O más bien porque sucede una mierda .
Y si hay tales errores de desarrollo, no debe confiar en ningún mecanismo de recuperación o manejo de errores implementado. Lo mismo se aplica a los errores de hardware.
fuente