156

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).
101010
  • 41,839
  • 11
  • 94
  • 168
  • 10
    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). – Matteo Italia May 16 '14 at 13:21
  • 10
    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? – Yakk - Adam Nevraumont May 16 '14 at 13:23
  • 1
    For the third bulletpoint look at the related question: [Is #pragma once a safe include guard?](http://stackoverflow.com/q/787533) It got a situation where header guards work but `#pragma once` usually doesn't. – AliciaBytes May 16 '14 at 13:25
  • 1
    [possible duplicate](http://stackoverflow.com/questions/1695807/why-isnt-c-cs-pragma-once-an-iso-standard?rq=1) in that it answers this question without mentioning C++11. – Yakk - Adam Nevraumont May 16 '14 at 13:26
  • 4
    Well, it is not coded in any official document, but you can regard it as *de facto* standard. – Siyuan Ren May 16 '14 at 13:34
  • @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). – James Kanze May 16 '14 at 16:13
  • 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! – Patrick Fromberg Jun 07 '18 at 13:53

2 Answers2

121

#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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 12
    Not just remote mounts. Hardlinks, softlinks, subst constructs (on Windows). It can get really messy. – Tonny May 16 '14 at 21:04
  • 4
    @Tonny 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. – James Kanze May 19 '14 at 08:12
  • @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. – mornfall May 19 '14 at 10:53
  • 1
    @mornfall 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. – James Kanze May 19 '14 at 11:57
  • 55
    Why can't compiler use SHA-1 or MD5 checksums to identify the files? – Sergey Jun 19 '15 at 23:16
  • @Sergey collision may be. – Anis LOUNIS aka AnixPasBesoin Mar 02 '16 at 18:33
  • 6
    @AnixPasBesoin Collisions for SHA-1 are highly unlikely. – Sergey Mar 03 '16 at 20:17
  • 4
    I think that once you start doing crypto on every include file, any claims of speeding up your compile time go out the window. – Mark VY Mar 07 '16 at 05:37
  • 39
    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. – T.E.D. Apr 29 '16 at 16:03
  • 6
    @Mark VY, I bet hashing is few orders faster that tokenization. – Peter K May 03 '16 at 13:36
  • Really? You could be right, I have clue how fast MD5 is. But still, wow. – Mark VY May 03 '16 at 14:06
  • @Sergey 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 ;) – mip Jul 26 '16 at 00:40
  • 50
    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. – mip Jul 26 '16 at 01:03
  • 5
    @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. – Anonymous Coward Jun 13 '17 at 17:12
  • 9
    @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. – mip Jun 14 '17 at 14:21
  • 3
    Please expand this answer to explain how files not being on a local disk matters. – einpoklum Oct 25 '18 at 15:55
  • 1
    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. – caligari Jun 06 '19 at 12:58
36

Section §16.6 of the Standard (N3936 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.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • 11
    Note that you can both `#pragma` and `#define` header-guard. – Yakk - Adam Nevraumont May 16 '14 at 13:24
  • 19
    _"Any pragma that is not recognized by the implementation is ignored"_. Does it mean that the message: _Warning: unrecognized pragma directive_ is non conforming? – rodrigo May 16 '14 at 13:26
  • 6
    "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. – Alex May 16 '14 at 13:26
  • 5
    @rodrigo I don't see why that should be non conforming, it can still ignore it and warn in case you misspelled it. – AliciaBytes May 16 '14 at 13:27
  • 3
    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. – Matteo Italia May 16 '14 at 13:27
  • 21
    @Yakk: If somebody writes `#define` header-guard, he/she has NO reason to write `#pragma once` as well. – Nawaz May 16 '14 at 13:30
  • 7
    @Nawaz A compiler can keep a cache of every file (by path) which has been `#pragma once`d, and in the event that it is `#include`d again can skip the `#include` (not even open the file). [gcc does the same](https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html) with header guards, but it is very, very fragile. The `#pragma` one is easy to do, the header guard one is hard. – Yakk - Adam Nevraumont May 16 '14 at 13:43
  • @Yakk: How is the gcc approach fragile? – David Rodríguez - dribeas May 16 '14 at 13:58
  • 2
    @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` fails – Yakk - Adam Nevraumont May 16 '14 at 14:05
  • 4
    @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 direction – David Rodríguez - dribeas May 16 '14 at 14:27
  • @DavidRodríguez-dribeas: header-guards might break (so it *is* fragile), no? – Nawaz May 16 '14 at 14:50
  • 4
    @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. – Yakk - Adam Nevraumont May 16 '14 at 14:58
  • @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... – David Rodríguez - dribeas May 16 '14 at 15:12
  • @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.) – James Kanze May 16 '14 at 16:11
  • 2
    @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. – supercat May 16 '14 at 17:30
  • 3
    @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. – David Rodríguez - dribeas May 16 '14 at 18:14
  • 1
    @rodrigo 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. – R. Martinho Fernandes May 21 '16 at 11:04