¿#pragma once versus incluir guardias? [duplicar]

Resuelto Matt Price asked hace 15 años • 13 respuestas

Estoy trabajando en una base de código que se sabe que solo se ejecuta en Windows y se compila en Visual Studio (se integra estrechamente con Excel, por lo que no irá a ninguna parte). Me pregunto si debería optar por las protecciones de inclusión tradicionales o utilizarlas #pragma oncepara nuestro código. Creo que dejar que el compilador se encargue #pragma onceproducirá compilaciones más rápidas y será menos propenso a errores al copiar y pegar. También es un poco menos feo.;)

Nota: para obtener tiempos de compilación más rápidos, podríamos usar Redundant Include Guards , pero eso agrega un estrecho acoplamiento entre el archivo incluido y el archivo incluido. Por lo general, está bien porque la protección debe basarse en el nombre del archivo y solo cambiaría si fuera necesario cambiar el nombre de inclusión de todos modos.

Matt Price avatar Jul 17 '09 22:07 Matt Price
Aceptado

No creo que haga una diferencia significativa en el tiempo de compilación, pero #pragma oncees muy compatible con todos los compiladores, pero en realidad no forma parte del estándar. El preprocesador puede ser un poco más rápido, ya que es más sencillo comprender su intención exacta.

#pragma oncees menos propenso a cometer errores y requiere menos código para escribir.

Para acelerar más el tiempo de compilación, simplemente declare hacia adelante en lugar de incluirlo en archivos .h cuando pueda.

Prefiero usar #pragma once.

Consulte este artículo de Wikipedia sobre la posibilidad de utilizar ambos .

Brian R. Bondy avatar Jul 17 '2009 15:07 Brian R. Bondy

Solo quería agregar a esta discusión que estoy compilando en VS y GCC, y solía usar include guards. Ahora me cambié a #pragma once, y la única razón para mí no es el rendimiento, la portabilidad o el estándar, ya que realmente no me importa qué es estándar siempre que VS y GCC lo admitan, y eso es lo siguiente:

#pragma oncereduce las posibilidades de errores.

Es muy fácil copiar y pegar un archivo de encabezado en otro archivo de encabezado, modificarlo para adaptarlo a sus necesidades y olvidarse de cambiar el nombre del protector de inclusión. Una vez incluidos ambos, le llevará un tiempo localizar el error, ya que los mensajes de error no son necesariamente claros.

Cookie avatar Jul 22 '2011 16:07 Cookie

#pragma oncetiene errores que no se pueden corregir . Nunca debe usarse.

Si su #includeruta de búsqueda es lo suficientemente complicada, es posible que el compilador no pueda distinguir entre dos encabezados con el mismo nombre base (por ejemplo, a/foo.hy b/foo.h), por lo que #pragma onceen uno de ellos suprimirá ambos . También es posible que no pueda distinguir dos inclusiones relativas diferentes (por ejemplo, #include "foo.h"y que #include "../a/foo.h"hagan referencia al mismo archivo), por lo que #pragma onceno podrá suprimir una inclusión redundante cuando debería haberlo hecho.

Esto también afecta la capacidad del compilador para evitar releer archivos con #ifndefguardias, pero eso es solo una optimización. Con #ifndeflas protecciones, el compilador puede leer de forma segura cualquier archivo que no esté seguro de haber visto ya; si está mal, sólo tiene que hacer un poco de trabajo extra. Siempre que no haya dos encabezados que definan la misma macro de protección, el código se compilará como se esperaba. Y si dos encabezados definen la misma macro de protección, el programador puede entrar y cambiar uno de ellos.

#pragma onceno tiene tal red de seguridad: si el compilador se equivoca acerca de la identidad de un archivo de encabezado, de cualquier manera , el programa no podrá compilar. Si encuentra este error, sus únicas opciones son dejar de usar #pragma onceo cambiar el nombre de uno de los encabezados. Los nombres de los encabezados son parte de su contrato API, por lo que cambiar el nombre probablemente no sea una opción.

(La versión corta de por qué esto no se puede solucionar es que ni la API del sistema de archivos de Unix ni de Windows ofrecen ningún mecanismo que garantice indicarle si dos nombres de ruta absolutos se refieren al mismo archivo. Si tiene la impresión de que los números de inodo se pueden usar para eso, lo siento, estás equivocado.)

(Nota histórica: la única razón por la que no eliminé #pragma onceGCC #importcuando tenía la autoridad para hacerlo, hace aproximadamente 12 años, fue que los encabezados del sistema de Apple dependían de ellos. En retrospectiva, eso no debería haberme detenido).

(Dado que esto ha aparecido dos veces en el hilo de comentarios: los desarrolladores de GCC se esforzaron bastante en hacerlo #pragma oncelo más confiable posible; consulte el informe de error 11569 de GCC . Sin embargo, la implementación en las versiones actuales de GCC aún puede fallar en condiciones plausibles condiciones, como granjas de construcción que sufren desfase de reloj. No sé cómo es la implementación de ningún otro compilador, pero no esperaría que nadie lo hubiera hecho mejor ).

zwol avatar Jan 19 '2016 18:01 zwol

Hasta que el día #pragma oncese convierta en estándar (eso no es actualmente una prioridad para los estándares futuros), le sugiero que lo use Y use guardias, de esta manera:

#ifndef BLAH_H
#define BLAH_H
#pragma once

// ...

#endif

Las razones son:

  • #pragma onceno es estándar, por lo que es posible que algún compilador no proporcione la funcionalidad. Dicho esto, todos los compiladores principales lo admiten. Si un compilador no lo sabe, al menos lo ignorará.
  • Como no existe un comportamiento estándar para #pragma once, no debes asumir que el comportamiento será el mismo en todos los compiladores. Los guardias se asegurarán de que al menos la suposición básica sea la misma para todos los compiladores que al menos implementen las instrucciones de preprocesador necesarias para los guardias.
  • En la mayoría de los compiladores, #pragma onceacelerará la compilación (de un cpp) porque el compilador no volverá a abrir el archivo que contiene esta instrucción. Por lo tanto, tenerlo en un archivo podría ayudar o no, según el compilador. Escuché que g++ puede hacer la misma optimización cuando se detectan guardias, pero hay que confirmarlo.

Al usar los dos juntos, obtienes lo mejor de cada compilador para esto.

Ahora, si no tiene algún script automático para generar los guardias, podría ser más conveniente simplemente usar #pragma once. Solo sepa lo que eso significa para el código portátil. (Estoy usando VAssistX para generar guardias y pragma una vez rápidamente)

Casi siempre deberías pensar tu código de forma portátil (porque no sabes de qué está hecho el futuro), pero si realmente crees que no debe compilarse con otro compilador (código para hardware integrado muy específico, por ejemplo) entonces deberías consultar la documentación del compilador para #pragma oncesaber qué estás haciendo realmente.

Klaim avatar Jul 17 '2009 15:07 Klaim