Is #pragma once part of the C++11 standard?
Asked Answered
M

2

161

Traditionally, the standard and portable way to avoid multiple header inclusions in C++ was/is to use the #ifndef - #define - #endifpre-compiler directives scheme also called macro-guard scheme (see code snippet below).

#ifndef MY_HEADER_HPP
#define MY_HEADER_HPP
...
#endif

In most implementations/compilers (see picture below) however, there's a more "elegant" alternative that serves the same purpose as the macro-guard scheme called #pragma once. #pragma once has several advantages compared to the macro-guard scheme, including less code, avoidance of name clashes, and sometimes improved compile speed.

enter image description here

Doing some research, I realized that although #pragma once directive is supported by almost all known compilers, there's a turbidness on whether #pragma once directive is part of the C++11 standard or not.

Questions:

  • Could someone clarify whether #pragma once directive is part of the C++11 standard or not?
  • If it's not part of the C++11 standard, are there any plans on including it on later releases (e.g., C++14 or later)?
  • It would also be nice if someone could further elaborate on the advantages/disadvantages in using either one of the techniques (i.e., macro-guard versus #pragma once).
Minor answered 16/5, 2014 at 13:20 Comment(7)
Incidentally, using double underscores for the header guards is prohibited by the standard, that reserves for the implementation all the symbols starting with double underscore (besides others).Sooth
Using a leading underscore followed by a capital letter is also barred. Second, where is the turbidness? I just see compiler support, I see noone claiming it is part of the standard?Keane
For the third bulletpoint look at the related question: Is #pragma once a safe include guard? It got a situation where header guards work but #pragma once usually doesn't.Headrail
possible duplicate in that it answers this question without mentioning C++11.Keane
Well, it is not coded in any official document, but you can regard it as de facto standard.Cope
@C.R. Except when you run into a compiler which doesn't implement it (or an organization of your files which means that it doesn't work---in practice, it is impossible for a compiler to know if two names name the same file, or two different files).Mediation
Phantastic, after many decades I find a place where I can puke about macro include guards. As Sergey says, most problems of pragma once can be solved with hashes. And where it is still ambiguous, the code is badly wrong anyway, so why care. Macro include guards are pathetic!Saturday
M
123

#pragma once is not standard. It is a widespread (but not universal) extension, which can be used

  • if your portability concerns are limited, and
  • you can be sure that all of your include files are always on a local disk.

It was considered for standardization, but rejected because it cannot be implemented reliably. (The problems occur when you have files accessible through several different remote mounts.)

It's fairly easy to ensure that there are no include guard conflicts within a single development. For libraries, which may be used by many different developments, the obvious solution is to generate a lot of random characters for the include guard when you create it. (A good editor can be set up to do this for you whenever you open a new header.) But even without this, I've yet to encounter any problems with conflicts between libraries.

Mediation answered 16/5, 2014 at 16:8 Comment(17)
Not just remote mounts. Hardlinks, softlinks, subst constructs (on Windows). It can get really messy.Talos
@Talos There are a lot of things which could confuse the compiler. There are generally ways to work around them, however. For example, under Unix, the compiler could to a stat, and look at the inode number. If the inode numbers are different, the files are different. If the inode numbers are the same, and the files are on the same file system, the files are the same. Hard links must be on the same file system, and it's possible to resolve soft links. But remote mounts can result in the same file appearing locally to be on two different file systems.Mediation
@JamesKanze, the problem with that is that it only approximates "sameness": you can see the same file on multiple filesystems, and won't be able to tell it's the same file. Looking at a cryptohash of the file may be more reliable (until you run into multiple copies of the file with different line endings or other subtle difference, anyway). But yes, those are all pretty convoluted scenarios.Essie
@Essie They're scenarios that I've already seen. Not with includes, but I did have a colleague once who, during a long compile, noticed that there were some files belonging to him in /tmp, deleted them, and found his home directory had disappeared. Due to the same file system being mounted multiple times.Mediation
Why can't compiler use SHA-1 or MD5 checksums to identify the files?Damalas
@Damalas collision may be.Cumulation
@AnixPasBesoin Collisions for SHA-1 are highly unlikely.Damalas
I think that once you start doing crypto on every include file, any claims of speeding up your compile time go out the window.Endamage
I really don't see the point in not putting something in the standard if every major compiler supports it. There are things actually in the standard far less supported than this. Also, it seems pretty silly to complain about edge issues, when we are talking about include files, where filename clashes are already a huge issue. It would have been nice if this demand for a 100% issue-free feature had been applied to the concept of #included header files in general.Waistcloth
@Mark VY, I bet hashing is few orders faster that tokenization.Tachygraphy
Really? You could be right, I have clue how fast MD5 is. But still, wow.Endamage
@Damalas one day someone will have a very good time debugging one of those "highly unlikely" collisions. And after that event all programmers relying on "high unlikeness" will be executed ;)Cacophonous
If your code includes some file from different locations through symbolic links or weird mounts then it is already not portable. Therefore arguing that pragma once can not portably implement something that is inherently not portable (and shouldn't be even considered) is yet another nonsense of C++ upside down world.Cacophonous
@doc C++ includes have no notion of symbolic links or mounted files. They just specify a path/filename and it is up to the compiler-OS combo to figure out how to locate that file. With some OSes and filesystems it is possible to guarantee file identity and with others it is not. This is a limitation outside the C++ language which the comitee acknowledges and, since it wants C++ to be widely usable, works around it by not having #pragma once. Besides, header guards is an already solved problem not in need of another solution which further enlarges the language and implementations.Worden
@JoseAntonioDuraOlmos I agree that symbolic links are an OS feature, which is out of scope of C++ language. Hence question arises why C++ comitee should consider something that is out of scope of the language? Trying to guarantee something that is not their responsibility does not make any sense IMO. DOS have supported only 8+3 chars per file name, yet no one argued that #include has to be removed, because one can blindly misuse the directive. #pragma once does not restrict portability in any way, providing that you won't exploit symbolic links to break the compilation.Cacophonous
Please expand this answer to explain how files not being on a local disk matters.Caddoan
It is the developer responsability to identify the code to be a closure for lexical scopes (this is the reason for macro-guards existence). With #pragma once that is delegated to the preprocessor (identifiers based on the file path) and it becomes messy, for example, with duplicated included libraries in project or linked directories.Cutup
F
36

Section §16.6 of the Standard (N4140 draft) describes #pragma directives as:

A preprocessing directive of the form

# pragma pp-tokensopt new-line

causes the implementation to behave in an implementation-defined manner. The behavior might cause translation to fail or cause the translator or the resulting program to behave in a non-conforming manner. Any pragma that is not recognized by the implementation is ignored.

Basically #pragma once is an implementation specific instance of a #pragma directive, and no, it's not standard. Yet.

It is often widely supported by most "major compilers" including GCC and Clang and is therefore sometimes recommended to avoid include-guards boilerplate.

Flaccid answered 16/5, 2014 at 13:22 Comment(17)
Note that you can both #pragma and #define header-guard.Keane
"Any pragma that is not recognized by the implementation is ignored". Does it mean that the message: Warning: unrecognized pragma directive is non conforming?Airminded
"and is therefore the recommended way to avoid include-guards boilerplate" - a very bold statement. It's a non-standard way, and the benefits of using it are few and have hardly been relevant in my experience, so I had to take my +1 away.Plateau
@Airminded I don't see why that should be non conforming, it can still ignore it and warn in case you misspelled it.Headrail
Well, we did have some problems of clashing include guards names, so I wouldn't say #pragma offers no advantage... ATL (IIRC) solved this by putting GUIDs in header guards, but they are ugly.Sooth
@Yakk: If somebody writes #define header-guard, he/she has NO reason to write #pragma once as well.Woodchuck
@Nawaz A compiler can keep a cache of every file (by path) which has been #pragma onced, and in the event that it is #included again can skip the #include (not even open the file). gcc does the same with header guards, but it is very, very fragile. The #pragma one is easy to do, the header guard one is hard.Keane
@Yakk: How is the gcc approach fragile?Hypallage
@DavidRodriguez Do you know if clang supports it? Are the patterns the same? Intel? MSVC? Another compiler you have yet to consider (maybe because it hasn't been written yet)? #pragma once is supported by every compiler (and by the standard is ignored by those who do not understand it), and the optimization (to not load the file again) is easy and trivial compared to gcc's approach. Will a variation of the pattern work in gcc? Better check the gcc's solution and audit it. If you follow the accepted pattern, gcc will work, but what if you don't? Btw, #pragma once\n#ifdef FOO\n#endif failsKeane
@Yakk: what about Another compiler you have yet to consider (maybe because it hasn't been written yet)? -- #pragma once is not standard, this is a reason against using it. The include guards are standard, and whether the most efficient or not it is not fragile (it might be slow, but not fragile). The rules in the documentation in gcc are perfectly clear: I will optimize if there are no non-empty lines outside of the guard (this is done after removing comments). When you say fragile it sounds like might break, but if that is the case you are pointing in the wrong directionHypallage
@DavidRodríguez-dribeas: header-guards might break (so it is fragile), no?Woodchuck
@DavidRodríguez a #pragma that is not understood must be ignored: a new compiler could invent a different meaning for #pragma once, but not likely. The fragility I was referring to was the fragility of the optimization, not of the include guards mechanism. Hence, my initial point, you can use both, and a reason to use both is that the #pragma once optimization is easy and trivial and widely supported, while the #define guard optimization is not trivial and is fragile, but is guaranteed to generate the behavior (outside of speed) you want. Hence, the suggestion to use both.Keane
@Nawaz: not more than the rest of the language. I work in a company with a few thousand developers and a code base that extend years. There is a naming convention that guarantees that there won't be collisions in header guards (and we don't use guids or any other randomly generated info). Of course there are bugs every so often when someone makes a typo...Hypallage
@DavidRodríguez-dribeas My editor inserts the include guards for me whenever I open a non-existent header. (I've actually configured it so that it will generate the include guards using different conventions, depending on where the file is in the file system; my own convention inserts 24 random characters at the end, so there's really 0 risk of conflict.)Mediation
@rodrigo: The only restrictions the Standard places on diagnostic messages are that (1) certain constructs are required to produce at least one diagnostic, and (2) the production of diagnostics from a valid program must not overload the system such that compilation either cannot succeed or would fail to produce a required diagnostic. From my reading of the standard, a compiler that always output "Warning: This compiler's diagnostic facilities are useless" as its sole diagnostic would comply with the letter of the standard, though probably not the intent.Spacious
@supercat: One member of the committee told me that after carefully discussing it, there was an agreement that an empty line is a valid diagnostic. A compiler that prints a blank line, and either produces the result or not is a compliant compiler. Good luck selling that to any customer.Hypallage
@Airminded late to the party, but if you want you can justify that as not ignoring it, but as recognizing it and the implementation-specific behaviour associated with it being to output the message "Warning: unrecognized pragma directive". This makes "Any pragma that is not recognized by the implementation is ignored." pretty unenforceable, and it can only work as a recommendation at best. In fact, even if you would take it as an rule instead of a recommendation, the sentence right before it explicitly allows a compiler to break that rule.Chasse

© 2022 - 2024 — McMap. All rights reserved.