¿Por qué afirmar es una macro y no una función?

7 minutos de lectura

avatar de usuario
Bar Itai

Mi profesor me preguntó eso en clase, y me preguntaba por qué es una macro en lugar de una función.

  • Cuando implemento mis propias afirmaciones, siempre lo hago como macros porque quiero poder regresar desde la función de afirmación, no solo desde la función de afirmación. Una macro puede hacer algo y hacer que la función de llamada regrese: ¡éxito!

    – Martín G.

    13 de agosto de 2014 a las 12:16

  • @Martin: … y una práctica de programación bastante cuestionable. ¿Una macro que oculta una declaración de salto?

    – AnT apoya a Rusia

    13 de agosto de 2014 a las 15:15

  • @AndreyT: Eso sí que sería cuestionable. Así que el nombre de la macro no debería ocultar el salto. He usado tales macros antes, pueden ser bastante claras si se nombran bien.

    – Ben Voigt

    13 de agosto de 2014 a las 17:28

  • Por lo que puedo decir, la pregunta y todas las respuestas se aplican igualmente a C++ también.

    – Shafik Yaghmour

    15 de agosto de 2014 a las 12:09

  • encuentro el g_return familia de macros definida por GLib un buen ejemplo de esas afirmaciones nombradas explícitamente.

    – José M. Benítez

    21 de agosto de 2014 a las 9:29

avatar de usuario
Shafik Yaghmour

La explicación simple sería que el estándar requiere assert ser una macro, si nos fijamos en el borrador del estándar C99(por lo que puedo decir, las secciones son las mismas en proyecto de norma C11 también) sección 7.2 Diagnósticos párrafo 2 dice:

La macro de aserción 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 razón dada en Justificación de la Norma Internacional—Lenguajes de Programación—C es:

Puede ser difícil o imposible hacer afirmar una función verdadera, por lo que está restringida a la forma macro.

lo cual no es muy informativo, pero podemos ver por qué en otros requisitos. Volviendo a la sección 7.2 párrafo 1 dice:

[…]Si NDEBUG se define como un nombre de macro en el punto del archivo de origen donde se incluye, la macro de afirmación se define simplemente como

#define assert(ignore) ((void)0)

La macro de aserción se redefine de acuerdo con el estado actual de NDEBUG cada vez que se incluye.

Esto es importante ya que nos permite una manera fácil de desactivar las aserciones en el modo de publicación en el que es posible que desee asumir el costo de los cheques potencialmente costosos.

y el segundo requisito importante es que se requiere usar las macros __FILE__, __LINE__ y __func__que se trata en la sección 7.2.1.1 La macro de afirmación que dice:

[…] la macro de afirmación 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 el flujo de error estándar en un formato definido por la implementación.165) Luego llama a la función abortar.

donde nota al pie 165 dice:

El mensaje escrito podría ser de la forma:

Assertion failed: expression, function abc, file xyz, line nnn.

Tenerlo como una macro permite que las macros __FILE__ etc… para ser evaluado en la ubicación adecuada y, como señala Joachim, ser una macro le permite insertar el original expresión en el mensaje que genera.

El borrador del estándar C++ requiere que el contenido del cassert encabezado son los mismos que los assert.h encabezado de la biblioteca Standrd C:

El contenido es el mismo que el encabezado de la biblioteca C estándar.

Ver también: ISO C 7.2.

¿Por qué (vacío)0?

Por que usar (void)0 a diferencia de alguna otra expresión que no hace nada? Podemos encontrar algunas razones, primero, así es como se ve la sinopsis de la afirmación en la sección 7.2.1.1:

void assert(scalar expression);

y dice (énfasis mío):

La macro de afirmación pone pruebas de diagnóstico en los programas; eso se expande a una expresión vacía.

la expresion (void)0 es consistente con la necesidad de terminar con un expresión vacía.

Suponiendo que no tuviéramos ese requisito, otras posibles expresiones podrían tener efectos no deseados, como permitir usos de assert en el modo de lanzamiento que no estaría permitido en el modo de depuración, por ejemplo, usando 0 nos permitiría usar assert en una tarea y cuando se usa correctamente probablemente generaría una expression result unused advertencia. En cuanto a usar un declaración compuesta como sugiere un comentario, podemos ver desde la macro multilínea C: do/while(0) vs scope block que pueden tener efectos no deseados en algunos casos.

  • Creo que otra gran razón es porque una función requeriría que la expresión tuviera un tipo particular, pero ese tipo tiene que ser compatible con cualquier otro tipo (técnicamente, tiene que ser compatible con lo que pueda aparecer en un if/while declaración)

    – Drew McGowen

    13 de agosto de 2014 a las 13:19

  • @Drew McGowen: en C99 simplemente tendría un parámetro de tipo _Bool como ese “tipo particular”. Cualquier tipo escalar es implícitamente convertible a _Bool.

    – AnT apoya a Rusia

    13 de agosto de 2014 a las 15:14


avatar de usuario
Robert Allan Hennigan Leahy

  1. Permite capturar el archivo (a través de __FILE__) y número de línea (hasta __LINE__)
  2. Permite la assert ser sustituido por una expresión válida que no hace nada (es decir, ((void)0)) al construir en modo de liberación

  • Para ser precisos, por ((void)0).

    – mafso

    13 de agosto de 2014 a las 12:05

  • Además, también permite que la expresión real se exprese como una cadena en el mensaje de error.

    – Un tipo programador

    13 de agosto de 2014 a las 12:05

  • @RobertAllanHenniganLeahy: Lo que está diciendo. ¿Es esa afirmación (según el estándar) no reemplazada por la cadena vacía, sino por la cadena ((void)0) cuando se define NDEBUG.

    – Martín York

    13 de agosto de 2014 a las 14:20

  • assert se requiere para ser válido expresión en todo momento. “Cadena vacía” no es una expresión válida, por lo que assert no se puede reemplazar por una cadena vacía. Puedo usar assert como esto: a = (assert(b > 0), b). Esta es una expresión perfectamente válida. Si assert de repente se convirtió en una cadena vacía, esta expresión dejaría de ser válida.

    – AnT apoya a Rusia

    13 de agosto de 2014 a las 15:07


  • Todos ustedes malinterpretaron. Que carne Joachim era esa assert(1==2) puede imprimir un mensaje como “Afirmación fallida: 1==2”. Eso no es posible a menos que sea una macro.

    – perro naranja

    14 de agosto de 2014 a las 6:40

avatar de usuario
Jeegar Patel

Esta macro está deshabilitada si al momento de incluir ya se ha definido una macro con el nombre NDEBUG. Esto permite que un codificador incluya tantas llamadas de aserción como sea necesario en un código fuente mientras depura el programa y luego las deshabilita todas 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 errores 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 de función y no podrá controlar todas esas afirmaciones en el modo de liberación.

Si usas la función entonces _FILE__, __LINE__ y __func__ dará el valor del código de esa función de aserción. No es esa línea de llamada o la línea de la función de llamada.

  • Hacerlo como función aumentará algunas llamadas de función y no podrá controlar todas esas afirmaciones en el modo de liberación.

    – Jeegar Patel

    13 de agosto de 2014 a las 12:05

Algunas afirmaciones pueden ser costosas de llamar. Acaba de escribir una rutina de inversión de matriz de alto rendimiento y agrega una verificación de cordura

assert(is_identity(matrix * inverse))

hasta el final. Bueno, tus matrices son bastante grandes, y si assert es una función, llevaría mucho tiempo hacer el cálculo antes de pasarla a assert. Tiempo que realmente no quiere gastar si no está 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.

Haciendo assert una macro en su lugar, puede eliminar el cálculo por completo cuando las aserciones están desactivadas.

avatar de usuario
Amir Saniyan

¿Por qué afirmar es una macro y no una función?

Porque debería compilarse en DEPURAR y no debe compilarse en LIBERAR modo.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad