#pragma가 안전한 포함 가드입니까?


310

사용할 때 컴파일러 최적화가있어 #pragma once컴파일 속도가 더 빠릅니다. 나는 이것이 표준이 아니므로 크로스 플랫폼 호환성 문제가 발생할 수 있음을 알고 있습니다.

이것이 비 Windows 플랫폼 (gcc)에서 대부분의 최신 컴파일러가 지원하는 것입니까?

플랫폼 컴파일 문제를 피하고 폴 가드의 추가 작업을 피하고 싶습니다.

#pragma once
#ifndef HEADER_H
#define HEADER_H

...

#endif // HEADER_H

걱정해야합니까? 이것에 대한 추가 정신 에너지를 소비해야합니까?


3
비슷한 질문을 한 후에 #pragma onceVS 2008의 클래스 뷰 문제를 피하는 것으로 나타 났습니다 . 나는 #pragma once이러한 이유로 인해 경비원을 제거하고 그들을 모두 교체하는 과정에 있습니다.
SmacL

답변:


188

사용 #pragma once은 최신 컴파일러에서 작동해야하지만 표준 #ifndef포함 가드 를 사용하지 않는 이유는 없습니다 . 잘 작동합니다. 한 가지주의 할 점은 GCC가 3.4#pragma once 이전 버전을 지원하지 않았다는 것 입니다.

또한 적어도 GCC에서는 표준 #ifndefinclude guard를 인식 하고 최적화 하므로보다 느려서는 안됩니다 #pragma once.


12
전혀 느려서는 안됩니다 (GCC 사용).
Jason Coco

54
그런 식으로 구현되지 않았습니다. 대신, 파일이 처음 #ifndef로 시작하고 #endif로 끝나는 경우 gcc는 파일을 기억하고 파일을 열지 않고도 향후 포함되는 파일을 항상 건너 뜁니다.
Jason Coco

10
#pragma once파일이 전처리되지 않기 때문에 일반적으로 더 빠릅니다. ifndef/define/endif어쨌든 전처리가 필요합니다.이 블록 후에는 (이론적으로) 컴파일 가능한 것을 가질 수 있기 때문입니다.
Andrey

13
가드 매크로 최적화에 관한 GCC 문서 : gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
Adrian

38
포함 가드를 사용하려면 #ifndef FOO_BAR_H일반적으로 "foo_bar.h"와 같은 파일에 대해 와 같은 새 기호를 정의해야하는 추가 요구 사항이 있습니다 . 나중에이 파일의 이름을 바꾸는 경우이 규칙에 따라 포함 가드를 조정해야합니까? 또한 코드 트리에서 두 개의 다른 위치에 두 개의 서로 다른 foo_bar.h가있는 경우 각각에 대해 서로 다른 두 개의 심볼을 고려해야합니다. 짧은 대답은 사용하는 #pragma once것이고 그것을 지원하지 않는 환경에서 실제로 컴파일 해야하는 경우 계속 진행하여 해당 환경에 대한 포함 가드를 추가하십시오.
Brandin

327

#pragma once 하나의 단점 (비표준이 아닌 것)이 있으며 다른 위치에 동일한 파일이있는 경우 (빌드 시스템이 파일을 복사하기 때문에 파일이 있음) 컴파일러는 다른 파일이라고 생각합니다.


36
그러나 당신은 또한 HEADERFILENAME_H의 형태로 iften되는 다른 #DEFINE 이름을 생성 귀찮게하지 않고 다른 위치에 같은 이름을 가진 두 개의 파일이있을 수 있습니다
바 가스

69
같은 #define WHATEVER를 가진 두 개 이상의 파일을 가질 수 있습니다. 이로 인해 재미를 끝내지 않으므로 pragma를 한 번 사용하는 것이 좋습니다.
Chris Huang-Leaver

107
설득력이 없습니다 ... 빌드 시스템을 파일을 복사하지 않고 심볼릭 링크를 대신 사용하는 시스템으로 변경하거나 각 번역 단위의 한 위치에서만 동일한 파일을 포함하십시오. 인프라 스트럭처는 재구성해야하는 혼란스러워 보입니다.
Yakov Galka

3
그리고 다른 디렉토리에 같은 이름을 가진 다른 파일이 있다면 #ifdef 접근 방식은 동일한 파일이라고 생각합니다. 따라서 하나에는 단점이 있고 다른 하나에는 단점이 있습니다.
rxantos

3
@rxantos, 파일이 다르면 #ifdef매크로 값도 다를 수 있습니다.
Motti

63

나는 표준에 있었으면 좋겠다 #pragma once. 포함 경비원은 실제로 큰 문제는 아니지만 (언어를 배우는 사람들에게는 설명하기가 약간 어려워 보이지만) 피할 수있는 사소한 성가신 것처럼 보입니다.

실제로, 시간의 99.98 % 이후,이 #pragma once동작은 원하는 동작입니다. 헤더의 다중 포함 방지가 컴파일러에 의해 자동으로 처리되고 #pragma이중 포함을 허용 하는 것이 있으면 좋을 것 입니다.

그러나 우리는 우리가 가진 것을 가지고 있습니다 (당신이 없을 수도 있음을 제외하고 #pragma once).


48
내가 정말로 원하는 것은 표준 #import지시어입니다.
John

10
표준 수입 지시문이 올 것입니다 : isocpp.org/blog/2012/11/… 그러나 아직 여기는 없습니다. 나는 그것을 강력히지지한다.
AHelps

6
@A Vaporware를 도와줍니다. 거의 5 년이 지났습니다. 아마도 2023 년에 당신은이 의견으로 돌아와서 "나는 당신에게 그렇게 말 했어요"라고 말할 것입니다.
doug65536

기화기는 아니지만 기술 사양 단계에만 있습니다. 모듈은 Visual Studio 2015 ( blogs.msdn.microsoft.com/vcblog/2015/12/03/… ) 및 clang ( clang.llvm.org/docs/Modules.html ) 에서 구현됩니다 . 그리고 #import가 아닌 import입니다.
AHelps

C ++ 20으로 만들어야합니다.
Ionoclast Brigham

36

성능상의 이점에 대해 잘 모르지만 확실히 작동합니다. 모든 C ++ 프로젝트에서 사용합니다 (MS 컴파일러를 사용하도록 허가 됨). 나는 그것을 사용하는 것보다 더 효과적이라는 것을 알았습니다.

#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif

동일한 작업을 수행하고 추가 매크로로 전처리기를 채우지 않습니다.

GCC는 #pragma once공식적 으로 3.4 버전을 지원합니다 .


25

GCC는 #pragma once3.4부터 지원됩니다. 추가 컴파일러 지원에 대해서는 http://en.wikipedia.org/wiki/Pragma_once 를 참조하십시오 .

#pragma once가드를 포함 하는 것과 달리 내가 사용하는 큰 장점은 복사 / 붙여 넣기 오류를 피하는 것입니다.

우리는 대부분 새로운 헤더 파일을 처음부터 시작하는 것이 아니라 기존 헤더 파일을 복사하여 필요에 맞게 수정하기 만하면됩니다. #pragma once포함 가드 대신 사용하여 작업 템플릿을 만드는 것이 훨씬 쉽습니다 . 템플릿을 수정해야할수록 오류가 발생할 가능성이 줄어 듭니다. 다른 파일에 동일한 포함 가드가 있으면 이상한 컴파일러 오류가 발생하고 무엇이 잘못되었는지 파악하는 데 시간이 걸립니다.

TL; DR : #pragma once사용하기 쉽습니다.


11

나는 그것을 사용하고 새 헤더를 만들기 위해 훨씬 적게 입력해야하기 때문에 기쁘게 생각합니다. Windows, Mac 및 Linux의 세 가지 플랫폼에서 제대로 작동했습니다.

성능 정보는 없지만 #pragma와 include guard의 차이점은 C ++ 문법 구문 분석의 속도와 비교할 때 아무것도 아닙니다. 그게 진짜 문제입니다. 예를 들어 C # 컴파일러로 같은 수의 파일과 행을 컴파일하여 차이점을 확인하십시오.

결국 경비원이나 pragma를 사용하는 것은 전혀 중요하지 않습니다.


#pragma를 한 번 싫어하지만 상대적인 이점을 지적 해 주셔서 감사합니다. "++"구문 분석은 "정상적인"운영 환경에서 다른 것보다 훨씬 비쌉니다. 컴파일 타임에 문제가 있으면 아무도 원격 파일 시스템에서 컴파일하지 않습니다.
Tom

1
C ++ 구문 분석 속도 저하와 C # 비교 C #에서는 작은 C ++ 파일마다 수천 개의 LOC 헤더 파일 (iostream, 누구나?)을 구문 분석 할 필요가 없습니다. 그러나 미리 컴파일 된 헤더를 사용하여이 문제를 더 작게 만드십시오.
Eli Bendersky

11

' #pragma once'를 사용하면 아무런 영향을 미치지 않을 수 있습니다 (어느 곳에서나 지원되지는 않지만 점점 더 널리 지원됩니다). 어쨌든 조건부 컴파일 코드를 사용해야합니다 #pragma once. 컴파일러는 아마 어쨌든 최적화 할 것입니다. 그러나 대상 플랫폼에 따라 다릅니다. 모든 대상이 지원하는 경우 계속 사용하십시오. 그러나 pragma 만 사용하고 지원하지 않는 컴파일러로 이식하면 모든 지옥이 풀릴 수 있기 때문에 의식적인 결정이되어야합니다.


1
어쨌든 경비원을 지원해야한다는 데 동의하지 않습니다. #pragma를 한 번 (또는 보호자) 사용하면 이것이없는 충돌이 발생하기 때문입니다. 따라서 체인 도구에서 지원하지 않으면 프로젝트가 컴파일되지 않으며 이전 K & R 컴파일러에서 일부 C를 컴파일하려는 경우와 정확히 동일한 상황에 있습니다. 가드를 추가하려면 최신 chaintool을 얻거나 코드를 변경해야합니다. 프로그램이 컴파일되었지만 작동하지 않으면 모든 것이 깨질 것입니다.
kriss

5

#pragma를 한 번 읽은 후에 파일을 다시 열지 않아도 성능상의 이점이 있습니다. 가드를 사용하면 컴파일러는 파일을 다시 포함해서는 안되는 정보를 얻기 위해 파일을 열어야합니다 (시간이 많이 소요될 수 있음).

이론적으로는 일부 컴파일러는 각 컴파일 단위에 대해 읽기 코드가없는 파일을 자동으로 열지 않기 때문입니다.

어쨌든 모든 컴파일러의 경우는 아니므로 크로스 플랫폼 코드의 경우 #pragma를 피해야합니다. 표준화되지 않았으며 표준화 된 정의 및 효과가 없습니다. 그러나 실제로는 경비원보다 훨씬 낫습니다.

결국 이 경우 각 컴파일러의 동작을 확인하지 않고도 컴파일러에서 최상의 속도를 얻을 수 있는 더 좋은 제안 은 pragma를 한 번 사용하고 가드를 사용하는 것입니다.

#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once

#include "Thing.h"

namespace MyApp
{
 // ...
}

#endif

그렇게하면 크로스 플랫폼과 컴파일 속도를 최대한 활용할 수 있습니다.

타이핑이 길어지면 개인적으로 도구를 사용하여 모든 것을 매우 사악한 방식으로 만들 수 있습니다 (Visual Assist X).


Visual Studio는 #include 가드를 그대로 최적화하지 않습니까? 다른 (더 나은?) 컴파일러가 그렇게하고 꽤 쉽다고 생각합니다.
Tom

1
왜 당신은 pragma뒤에 넣어 ifndef? 이점이 있습니까?
user1095108

1
@ user1095108 일부 컴파일러는 헤더 가드를 구분 기호로 사용하여 파일에 한 번 인스턴스화해야하는 코드 만 포함되어 있는지 알 수 있습니다. 일부 코드가 헤더 가드 외부에 있으면 전체 파일을 두 번 이상 인스턴스화 할 수 있습니다. 동일한 컴파일러가 pragma를 한 번 지원하지 않으면 해당 명령을 무시합니다. 따라서 pragma를 헤더 가드 안에 한 번만 두는 것이 헤더 헤더를 "최적화"할 수 있도록하는 가장 일반적인 방법입니다.
Klaim

4

항상 그런 것은 아닙니다.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566 두 파일을 모두 포함하는 좋은 예가 있지만 동일한 타임 스탬프와 내용 (동일한 파일 이름이 아님)으로 인해 동일하다고 생각되는 경우 .


10
컴파일러의 버그 일 것입니다. (바로 가기를 시도하면 안됩니다).
rxantos

4
#pragma once비표준이므로 컴파일러가 수행하기로 결정한 것은 "올바른"것입니다. 물론 "예상"과 "유용"에 대해 이야기 할 수 있습니다.
user7610

2

매우 큰 나무 (때로는 distcc 사용)에서 gcc 3.4 및 4.1을 사용하면 #pragma를 대신 사용하거나 표준 포함 가드와 함께 사용하면 속도가 향상되지 않습니다.

나는 실제로 저축이 없기 때문에 이전 버전의 gcc 또는 다른 컴파일러를 어떻게 혼란스럽게 할 가치가 있는지 알지 못합니다. 나는 다양한 de-linter를 모두 시도하지는 않았지만 많은 사람들을 혼란스럽게 할 것이라고 확신합니다.

나도 초기에 채택되기를 원하지만 "ifndef가 완벽하게 작동 할 때 왜 필요한가?"라는 주장을 볼 수 있습니다. C의 많은 어두운 구석과 복잡성을 감안할 때 경비원은 가장 쉽고 자기 설명하는 것 중 하나입니다. 전 처리기의 작동 방식에 대한 지식이 조금이라도 있다면 설명이 필요합니다.

그러나 상당히 빠른 속도로 관찰되면 질문을 업데이트하십시오.


2

오늘날 구식 포함 경비원은 #pragma만큼 빠릅니다. 컴파일러가 특수하게 처리하지 않더라도 #ifndef WHATEVER 및 WHATEVER가 정의되면 여전히 중지됩니다. 파일을 여는 것은 오늘날 더러워집니다. 개선이 필요하더라도 몇 초 정도의 시간이 소요됩니다.

#pragma를 한 번만 사용하면 아무런 이점이 없습니다. 다른 포함 가드와 충돌을 피하기 위해 다음과 같은 것을 사용합니다. CI_APP_MODULE_FILE_H-> CI = 회사 이니셜; APP = 응용 프로그램 이름; 나머지는 자명하다.


19
타이핑이 훨씬 적은 이점이 있습니까?
Andrey

1
그러나 몇 밀리 초에서 수십만 시간은 몇 분입니다. 대규모 프로젝트는 각각 수십 개의 헤더를 포함하여 수만 개의 파일로 구성됩니다. 현재 많은 코어 CPU가 주어지면 입 / 출력, 특히 많은 작은 파일을 여는 것이 주요 병목 현상 중 하나입니다.
데이먼

1
"오늘의 구식 포함 경비원은 #pragma만큼 빠릅니다." 오늘날, 그리고 수년 전. GCC 사이트에서 가장 오래된 문서 는 2001 년부터 2.95 이며, 포함 가드 최적화는 새로운 것이 아닙니다. 최근 최적화되지 않았습니다.
Jonathan Wakely

4
주요 이점은 경비원이 오류가 발생하기 쉽고 경박하다는 것입니다. 서로 다른 디렉토리에 동일한 이름을 가진 두 개의 서로 다른 파일을 가지고 있거나 포함 가드를 복사하는 동안 복사-붙여 넣기 오류를 만드는 것은 너무 쉽습니다. Pragma는 한 번 오류가 발생하기 쉽지 않으며 모든 주요 PC 플랫폼에서 작동합니다. 당신이 그것을 사용할 수 있다면, 그것은 더 나은 스타일입니다.
AHelps

2

가장 큰 차이점은 컴파일러가 포함 가드를 읽기 위해 헤더 파일을 열어야한다는 것입니다. 이에 비해 pragma는 한 번 컴파일러가 파일을 추적하고 동일한 파일에 대한 다른 포함을 가로 질러 파일 IO를 수행하지 않도록합니다. 무시해도 좋을지 모르지만 거대한 프로젝트, 특히 헤더가없는 프로젝트에는 원칙이 포함되어있어 쉽게 확장 할 수 있습니다.

그러나 요즘 컴파일러 (GCC 포함)는 pragma와 같은 경비원을 한 번만 취급 할만큼 똑똑합니다. 즉, 파일을 열지 않고 파일 IO 페널티를 피합니다.

pragma를 지원하지 않는 컴파일러에서는 약간 성가신 수동 구현을 보았습니다.

#ifdef FOO_H
#include "foo.h"
#endif

이름 충돌과 잠재적 오타 오류를 피하기 위해 개인적으로 #pragma once 접근 방식을 좋아합니다. 또한 비교하면 더 우아한 코드입니다. 즉, 휴대용 코드의 경우 컴파일러가 그것에 대해 불평하지 않는 한 두 가지 모두 다 치지 않아야합니다.


1
"현재 GCC를 포함한 컴파일러는 pragma와 같은 가드를 한 번 처리 할 수있을만큼 똑똑하다"고 말했다. 그들은 수십 년 동안 그것을 해왔으며 아마도 #pragma once존재했던 것보다 더 길었을 것입니다 !
Jonathan Wakely

당신이 나를 오해했다고 생각합니다. pragma 전에 한 번 말하면 모든 컴파일러는 전 처리기 단계에서 여러 번 포함 된 동일한 h 파일에 대해 여러 개의 IO penanties를 발생시킵니다. 최신 구현은 전 처리기 단계에서 더 나은 파일 캐싱을 사용합니다. 그럼에도 불구하고, 프 래그 마없이 전 처리기 단계는 여전히 가드 외부의 모든 것을 포함합니다. pragma를 사용하면 전체 파일이 제거됩니다. 이러한 관점에서 pragma는 여전히 유리합니다.
Shammi

1
아니요, 괜찮습니다. 괜찮은 컴파일러는 #pragma없이 전체 파일을 한 번 남겨두고 두 번째로 파일을 열지 않으며 두 번째로 보지도 않습니다 ( gcc.gnu.org/onlinedocs/ 참조) cpp / Once-Only-Headers.html (캐싱과는 관련이 없음)
Jonathan Wakely

1
귀하의 링크에서 최적화는 cpp에서만 발생하는 것처럼 보입니다. 어쨌든 캐싱이 작동합니다. 전처리 기는 가드 외부에 코드를 포함시키는 방법을 알고 있습니다. 예 ... extern int foo; #ifndef INC_GUARD #define INC_GUARD class ClassInHeader {}; #endif이 경우 전처리 기는 extern int foo를 포함해야합니다. 동일한 파일을 여러 번 포함하는 경우 여러 번. 우리가 한 번 #pragma의 차이점을 이해하고 경비원을 포함하고 다양한 컴파일러가 두 컴파일러와 어떻게 동작 하는지를 알기 만한다면 하루 종일이 문제에 대해 논쟁하지 않아도됩니다.)
Shammi

1
그것은 분명히 최적화를 적용하지 않습니다.
Jonathan Wakely

1

msvc 또는 Qt (최대 Qt 4.5)를 사용하면 GCC (최대 3.4)이므로 msvc 모두 지원하므로을 #pragma once사용하지 않을 이유가 없습니다 #pragma once.

소스 파일 이름은 일반적으로 동일한 클래스 이름과 동일하며 때로는 클래스 이름을 변경하기 위해 리 팩터 가 필요 하며 클래스 이름을 변경해야 #include XXXX하므로 수동 유지 관리 #include xxxxx가 현명한 작업이 아니라고 생각 합니다. Visual Assist X 확장을 사용하더라도 "xxxx"를 유지하는 것은 필수적인 작업이 아닙니다.


1

사람들은 헤더 파일을 한 번만 자동으로 포함하는 것이 항상 바람직하다고 생각하는 사람들에 대한 추가 참고 사항 : 수십 년 동안 헤더 파일을 두 번 또는 여러 번 포함하여 코드 생성기를 작성합니다. 특히 프로토콜 라이브러리 스텁 생성을 위해 추가 도구와 언어없이 이식성이 뛰어나고 강력한 코드 생성기를 사용하는 것이 매우 편합니다. 이 블로그 X-Macros가 보여 주듯이이 구성표를 사용하는 유일한 개발자는 아닙니다 . 자동 보호 기능이 없으면이 작업을 수행 할 수 없습니다.


C ++ 템플릿으로 문제를 해결할 수 있습니까? C ++ 템플릿 방식으로 인해 매크로가 필요한 경우는 거의 없습니다.
Clearer

1
우리의 장기적인 전문 경험은 성숙한 언어, 소프트웨어 및 도구 인프라를 항상 사용하면 서비스 제공 업체 (내장 시스템)로서 생산성과 유연성이 크게 향상된다는 것입니다. C ++ 기반 임베디드 시스템 소프트웨어 및 스택을 개발하는 경쟁 업체는 일부 개발자가 업무에 더 만족할 수 있습니다. 그러나 일반적으로 출시 시간, 기능 및 유연성면에서 여러 번 뛰어난 성능을 발휘합니다. Nether는 동일한 도구를 반복해서 사용하여 생산성을 과소 평가했습니다. 웹-데브는 대신 많은 프레임 워크에 어려움을 겪습니다.
Marcel

참고 사항 : DRY 원칙 자체에 대해 모든 헤더 파일에 가드 / # pragma를 한 번 포함하지는 않습니다. X-Macro기능 에서 귀하의 요점을 볼 수 있지만 include의 주된 용도는 아닙니다. DRY를 고수한다면 헤더 보호 해제 / # pragma multi와 같은 다른 방법으로 사용해서는 안됩니까?
caiohamamura

DRY는 "반복 반복하지 않음"을 나타냅니다. 그것은 인간에 관한 것입니다. 기계가하는 일은 그 패러다임과 아무 관련이 없습니다. C ++ 템플릿은 많이 반복되고 C- 컴파일러는 루프 풀림과 같은 작업을 수행하며 모든 컴퓨터는 믿을 수 없을 정도로 빠르고 거의 모든 것을 반복합니다.
마르셀
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.