2010-10-29 14 views
7

La aserción se utiliza para verificar si se cumple una condición (precondición, condición posterior, invariantes) y ayudar a los programadores a encontrar los agujeros durante la fase de depuración.Cuál es la estrategia si falla la aserción

Por ejemplo,

void f(int *p) 
{ 
    assert(p); 
    p->do(); 
} 

Mi pregunta es qué tenemos que asumir la condición no se pudo cumplir en el modo de disparo y manejar el caso en consecuencia?

void f(int *p) 
{ 
    assert(p); 

    if (p) 
    { 
    p->do(); 
    } 
} 

Después de todo, la afirmación significa que la condición que prueba NUNCA debe ser falsa. Pero si, si no lo verificamos y falla, el programa se bloquea. Suena como un dilema. ¿Cómo lidian ustedes con eso?

+2

¿Duplicado de [diseño por pruebas de contrato por afirmación o por excepción?] (Http://stackoverflow.com/questions/117171/design-by-contract-tests-by-assert-or-by-exception) (Hay una buena discusión sobre los pros y los contras de los diferentes enfoques, no existe realmente un consenso sobre el tema) Ver también [¿Cuándo deberían permanecer las afirmaciones en el código de producción?] (http: // stackoverflow.com/questions/17732/when-should-assertions-stay-in-production-code) –

+0

No creo que alguna vez haya visto una pregunta obtener tantas respuestas en tan poco tiempo. –

+0

Sí, y cada uno se contradice :) – EboMike

Respuesta

20

Si la afirmación falla, el programa debería bloquearse.

Una falla de afirmación significa que el programador cometió un error fundamental en su comprensión de cómo es posible que proceda el flujo del programa. Esta es una ayuda al desarrollo, no una ayuda a la producción. En producción, uno podría manejar excepciones, ya que "podrían" ocurrir, mientras que las afirmaciones "nunca" fallarían.

Si estás en el campamento que dice: "Ah, pero ¿y si las afirmaciones fallan en la producción? ¡Necesito atraparlas!" entonces te estás perdiendo el punto. Pregúntate, en tal caso, ¿por qué no lanzas una excepción (o manejas el error)?

En términos generales, afirman es no sólo una abreviatura de "si no se cumple la condición, excepción tiro" (bueno, a veces eso es la semántica operacional, pero no es la semántica denotativa). Más bien, una afirmación que falla significa que la aplicación está en , un estado que el desarrollador no cree que sea posible. ¿Realmente quiere que el código continúe ejecutándose en tal caso? Claramente (yo diría), No.

+0

+1 tiene razón señor. – frast

+0

Nótese que mi respuesta * no aborda * explícitamente la noción de si las aserciones deben o no incluirse en las versiones de producción (no deberían), ya que está más allá del alcance de la pregunta. – user359996

0

Las aserciones son un código de depuración, no un código de operación. No los use para detectar errores de entrada.

0

Las afirmaciones se utilizan para detectar errores en las pruebas. La teoría es que has probado lo suficiente como para saber que funcionará una vez que lo hayas liberado.

Si existe la posibilidad de que la condición surja en el funcionamiento de la vida real, no confíe en las aserciones, use excepciones u otro mecanismo de error.

+0

¿Debo eliminar las afirmaciones en el modo de lanzamiento si se prueban exhaustivamente? ¿O simplemente dejarlos allí en caso de cambios futuros por otro desarrollador que mantiene el código? –

+1

@Eric, por lo general se usa una macro 'assert' que se compila automáticamente en un bloque vacío de código cuando se crea para su lanzamiento. Entonces no hay razón para deshacerse de ellos, serán útiles para cualquiera que modifique el código. Actúan como documentación también. –

0

Las afirmaciones son útiles para la depuración como mencionaste. Nunca deben entrar en el código de producción (compilado, está bien envolverlos en #ifdefs por supuesto)

Si se encuentra con un problema que no puede controlar y necesita el control en su producción código que haría algo como:

void f(int *p) 
{ 

    if (!p) 
    { 
    do_error("FATAL, P is null."); 
    } 

    p->do(); 
} 

Dónde do_error es una función que registra un error y limpiamente, sale.

2

La programación defensiva siempre es la mejor. Siempre debe suponer que, a pesar de todas sus pruebas, su aplicación se enviará con errores.Como tal, le conviene agregar comprobaciones NULL en situaciones en las que puede evitar una deferencia de puntero NULL y simplemente seguir adelante.

Sin embargo, hay situaciones donde simplemente no hay una manera fácil de evitar un bloqueo, y en esos casos, la afirmación es su única forma de detectar el problema durante su ciclo de desarrollo.

Sin embargo, un punto importante, afirma, también se utiliza para detectar problemas importantes con la integridad de sus datos. Si continúa más allá de esos asertos, puede arriesgarse a corromper los datos. En esos casos, puede ser mejor bloquear en lugar de destruir sus datos. (Obviamente, cualquier tipo de controlador de fallos que al menos muestra una interfaz de usuario razonable con una descripción de error sería preferible).

+0

Subir. Estoy de acuerdo con su idea de que a veces bloquear un programa con una buena notificación de IU es mejor que dejarlo funcionar en mal estado sin previo aviso. –

+0

@Eric: si puede determinar que el programa está en "mal estado", puede manejarlo, en consecuencia (* por ejemplo * con una notificación de UI). Se requiere un * assert * para este caso, ya que arrojar una excepción o incluir un código de manejo de errores funcionará bien. Además, si no cree al 100% que es * imposible * que el programa se encuentre en ese estado, * assert * no es la construcción correcta. Debería, nuevamente, simplemente probar el estado y lanzar una excepción. o manejar el error * Assert * no dice "I * hope * this is true", dice, "I * assert * esto es (necesariamente) cierto". – user359996

+0

Puede detectar un estado incorrecto, pero no necesariamente puede recuperarse de él. Si una variable tiene un valor que posiblemente no debería tener, ¿cómo te recuperas de eso? Adivina el valor correcto? Es una indicación de que algo más podría estar completamente roto que lleva a que esta variable sea mala. Tal vez incluso la memoria destrozada. No puedes recuperarte de eso. Además, por motivos de rendimiento, no desea gastar demasiado tiempo en la comprobación de errores en una compilación de lanzamiento. (Mis requisitos son probablemente diferentes a los tuyos, principalmente hago juegos; cada rama cuenta en algunas funciones básicas). – EboMike

0

Digo déjalos en la versión de lanzamiento. Es probable que haya errores en cualquier compilación. Tener afirmado en su producto significa que puede identificar el problema más fácilmente cuando recibe un informe de problema de un usuario.

No ponga mucho esfuerzo en el manejo de las excepciones. Simplemente asegúrate de que puedes obtener la excepción completa, incluido stacktrace. Eso se aplica a la versión de lanzamiento en particular.

1

Estrictamente hablando, el segundo código tiene redundancia.

void f(int *p) 
{ 
    assert(p); 
    if (p) // Beats the purpose of assertion 
    { 
    p->do(); 
    } 
} 

Se ha producido un error de aserción. Algo que es inesperado/no controlado. En el código anterior, ya sea

1) Usted está manejando adecuadamente el caso donde p es nulo. (No llamando a p-> do()), que supuestamente es lo correcto/esperado. Sin embargo, entonces la afirmación es una falsa alarma .

2) Por otro lado, si al no llamar a p-> do(), algo irá mal (tal vez más en el código o en el resultado), entonces la afirmación es correcta, pero no debería haber ningún punto en continuar de todos modos.

En el código anterior, el programador está trabajando muy duro para manejar casos que son erróneos de todos modos.

Dicho esto, a algunas personas les gusta tratar aseveraciones como , algo salió mal, pero veamos si todavía obtenemos la salida correcta. OMI, esa es una mala estrategia y crea confusiones durante la corrección de errores.

0

Dado que muchas personas están comentando poner afirmaciones en modo de lanzamiento:

En lo que trabajo, la eficiencia es muy importante (a veces, la ejecución de grandes conjuntos de datos tarda decenas de horas a pocos días para completar). Por lo tanto, tenemos macros especiales que solo se afirman en el código de depuración (se ejecutan durante QA, etc.). Como ejemplo, una afirmación dentro de un bucle for es definitivamente una sobrecarga y es posible que desee evitarlo en el código de lanzamiento. Después de todo, si todo está bien, las afirmaciones no deben fallar.

Un ejemplo donde los códigos de liberación son buenos es - si no se supone que la lógica llegue a una rama de código en particular. En este caso, assert (0) está bien [por lo tanto, cualquier tipo de afirmación (0) siempre se puede dejar en el código de liberación].

Cuestiones relacionadas