library - ¿Por qué es assert una macro y no una función?
errno h in c (5)
Mi profesor me lo preguntó en clase y me preguntaba por qué es una macro en lugar de una función.
¿Por qué es assert una macro y no una función?
Porque debe compilarse en modo DEBUG y no debe compilarse en modo RELEASE .
- Permite capturar el archivo (a través de
__FILE__
) y el número de línea (hasta__LINE__
) - Permite que la
assert
sea sustituida por una expresión válida que no hace nada (es decir,((void)0)
) cuando se construye en modo de liberación
Algunas afirmaciones pueden ser costosas de llamar. Acaba de escribir una rutina de inversión de matriz de alto rendimiento y agrega un control de cordura
assert(is_identity(matrix * inverse))
hasta el final. Bueno, tus matrices son bastante grandes, y si assert
es una función, tomaría mucho tiempo hacer el cálculo antes de pasarlo a assert. Tiempo que realmente no quieres gastar si no estás depurando.
O tal vez la afirmación es relativamente barata, pero está contenida en una función muy corta que se llamará en un ciclo interno. U otras circunstancias similares.
Al hacer que assert
una macro, puede eliminar el cálculo por completo cuando las aserciones están desactivadas.
Esta macro está deshabilitada si, en el momento de incluirla, ya se ha definido una macro con el nombre NDEBUG. Esto permite que un codificador incluya tantas llamadas afirmativas como sea necesario en un código fuente mientras depura el programa y luego deshabilita todas ellas para la versión de producción simplemente incluyendo una línea como:
#define NDEBUG
al comienzo de su código, antes de la inclusión de <assert.h>
.
Por lo tanto, esta macro está diseñada para capturar errores de programación, no de usuario o de tiempo de ejecución, ya que generalmente se desactiva después de que un programa sale de su fase de depuración.
Hacerlo como función aumentará algunas llamadas a funciones y no podrá controlar todas esas afirmaciones en el modo de lanzamiento.
Si usa la función, entonces _FILE__
, __LINE__
y __func__
le darán el valor del código de la función assert. No es esa línea de llamada o la línea de la función de llamada.
La explicación simple sería que la norma requiere que se assert
que sea una macro, si miramos el borrador del estándar C99 ( hasta donde puedo decir que las secciones son las mismas en el borrador del estándar C11 también ) sección 7.2
Diagnóstico, el párrafo 2 dice:
La macro assert se implementará como una macro, no como una función real. Si se suprime la definición de macro para acceder a una función real, el comportamiento no está definido.
¿Por qué requiere esto ?, la justificación dada en Justificación para los lenguajes de programación estándar internacional-C es:
Puede ser difícil o imposible hacer que afirme una verdadera función, por lo que se restringe a la forma macro.
que no es muy informativo, pero podemos ver a partir de otros requisitos por qué. Volviendo a la sección 7.2
párrafo 1 dice:
[...] Si NDEBUG se define como un nombre de macro en el punto en el archivo fuente donde está incluido, la macro assert se define simplemente como
#define assert(ignore) ((void)0)
La macro assert se redefine de acuerdo con el estado actual de NDEBUG cada vez que se incluye.
Esto es importante ya que nos permite una forma fácil de desactivar las afirmaciones en el modo de lanzamiento, donde es posible que desee tomar el costo de las comprobaciones potencialmente costosas.
y el segundo requisito importante es que se requiere el uso de las macros __FILE__
, __LINE__
y __func__
, que se trata en la sección 7.2.1.1
La macro assert que dice:
[...] la macro assert escribe información sobre la llamada particular que falló, [...] estos últimos son respectivamente los valores de las macros de preprocesamiento __FILE_ _ y __LINE_ _ y del identificador __func_ _) en la corriente de error estándar en un formato definido por la implementación. 165) Luego llama a la función abortar.
donde la nota 165
dice:
El mensaje escrito podría ser de la forma:
Assertion failed: expression, function abc, file xyz, line nnn.
__FILE__
como macro permite que las macros __FILE__
etc ... se evalúen en la ubicación correcta y, como Joachim señala que es una macro, le permite insertar la expresión original en el mensaje que genera.
El borrador del estándar C ++ requiere que el contenido del encabezado cassert
sea el mismo que el encabezado assert.h de la biblioteca Standrd C:
El contenido es el mismo que el encabezado de la biblioteca estándar C.
Ver también: ISO C 7.2.
¿Por qué (vacío) 0?
¿Por qué usar (void)0
en oposición a alguna otra expresión que no hace nada? Podemos encontrar algunas razones, primero esta es la apariencia de la sinopsis en la sección 7.2.1.1
:
void assert(scalar expression);
y dice ( énfasis mío ):
La macro assert pone las pruebas de diagnóstico en los programas; se expande a una expresión vacía
la expresión (void)0
es consistente con la necesidad de terminar con una expresión vacía .
Suponiendo que no tuviéramos ese requisito, otras expresiones posibles podrían tener efectos no deseados como permitir usos de assert
en modo de lanzamiento que no estarían permitidos en modo de depuración, por ejemplo, usar plain 0
nos permitiría usar assert
en una asignación y cuando se usa correctamente probablemente generaría una expression result unused
advertencia expression result unused
. En cuanto al uso de una declaración compuesta como sugiere un comentario, podemos ver desde la macro multilínea C: do / while (0) vs bloque de ámbito que tienen efectos no deseados en algunos casos.