12

Modern C and C++ compilers support the non-standard #pragma once, preprocessor directive, which serve a similar purpose to the classic header guards:

#ifndef hopefully_unique_identifier_that_doesnt_hurt_the_code
#define hopefully_unique_identifier_that_doesnt_hurt_the_code
  // some code here
#endif

One problem, I'm aware of, with the classic approach is that once you've included a header, you have to #undef the header guard macro to include it again (doing so, to me, is a major code-smell, but that's beside the point here). The same problem arises with the #pragma once approach, but without the possibility of allowing the header to be included more than once.

Another problem with the classic-approach is that you may, accidentally, define the same macro in unrelated places, thus either not including the header as expected or doing some other nasty stuff, that I can't imagine. This is reasonably easy to avoid in practice, by keeping to certain conventions such as basing the macros to UUID-like objects (i.e. random strings) or (the less optimal approach), basing them on the name of the file, they are defined in.

I have only rarely experienced any of these, potential problems, in real life, so I don't really consider them to be major problems.

The only potential real life problem I can think of with #pragma once, is that it's not a standard thing -- you're relying on something that may not be available everywhere, even if it is present, in practice, everywhere (*).

So, what potential problems exist with #pragma once, besides the ones I've already mentioned? Am I having too much faith in having it available, in practice, everywhere?

(*) Some minor compiler that only a handful of people use, excluded.

Martin Ueding
  • 8,245
  • 6
  • 46
  • 92
Clearer
  • 2,166
  • 23
  • 38
  • 4
    _"... you have to #undef the header guard macro to include it again ..."_ I don't get what you mean? – user0042 Dec 15 '17 at 10:46
  • 3
    "The same problem arises with the #pragma once approach, but without the possibility of allowing the header to be included more than once." That is not some drawback but the sole purpose of it. If you want to have a header included multiple times, you don't need a header guard at all – Gerhardh Dec 15 '17 at 10:49
  • `#pragma` once was added to speed up compilation. You can use both the pragma and the `#ifndef`. – Michaël Roy Dec 15 '17 at 13:21

3 Answers3

12

One problem I have encountered with using #pragma once was when including the same file that is located at multiple locations. With #pragma once it is deemed different, not with #ifndef/#define guard.

piwi
  • 5,136
  • 2
  • 24
  • 48
  • 1
    I've never had to include the same file in multiple locations and it seems like a bit of a weird thing to require. I suppose it could happen by accident, but I'm having a hard time seeing how this can happen by design -- have you ever experienced a situation where this has happened by design? – Clearer Dec 15 '17 at 11:22
  • 1
    @Clearer It was several years ago so I don't remember the precise context. But it was due how the build was configured, with header files copied in the build directory, for Qt. – piwi Dec 15 '17 at 11:50
  • @piwi does that remain a relevant concern nowadays? Rather than copying header files around, seems their location could just as easily be added to the include search path. – Adam Barnes Sep 28 '21 at 10:34
  • @AdamBarnes No I don't think so, this should not be a concern, and for years now I have used `#pragma once` and don't have any problems with it. But since the question was about the "danger" of using pragma statement, it is still a risk nonetheless, depending on the build environment. – piwi Sep 29 '21 at 09:32
7

I have worked with a decent set of compilers so far:

  • GCC
  • Clang/LLVM
  • IBM XLC
  • Intel C++ Compiler

The only compiler that does not support #pragma once is the IBM XLC compiler, that that one does not even support C++11, so I am not interested. If you need to work with the IBM XLC Compiler on Blue Gene/Q, then you cannot use #pragma once.

A long time ago, certain compilers did not understand the include guard idiom and would repeatedly open the header file only to find that the preprocessor reduced the content to nothing. With these compilers, using #pragma once would give compile time benefit. However, this has been implemented in major compilers, such that this makes no difference nowadays.

Perhaps you have some special compiler for your embedded system. That one might be unable to use #pragma once.

In general I prefer #pragma once because when you duplicate a header file in order to do incremental refactoring by duplication or extending a class, you cannot forget to change the name of the include guard macro.

Therefore I do not know of any hard problem you have with #pragma once, except for certain compilers.

Martin Ueding
  • 8,245
  • 6
  • 46
  • 92
  • 5
    *"Perhaps that was changed by now."* Yes, that was a long time ago. Now it says "The compiler recognizes the #include guard idiom and implements the multiple include optimization the same way as the #pragma once directive if no non-comment code or preprocessor directive comes before or after the standard form of the idiom" (https://msdn.microsoft.com/en-us/library/4141z1cx.aspx) – Bo Persson Dec 15 '17 at 11:05
  • 1
    @BoPersson: Thank you! I have never used MSVC++ myself, just the ones listed in my post. I have updated the section about the Microsoft compiler. – Martin Ueding Dec 16 '17 at 12:08
  • I'm not sure what platform you are using, but the [IBM XL C/C++ for Linux compiler](https://www.ibm.com/support/knowledgecenter/SSXVZZ_13.1.6/com.ibm.xlcpp1316.lelinux.doc/language_ref/standard_features.html) fully supports C++11. – Nicole Trudeau Dec 22 '17 at 15:24
  • @trudeaun: It is the IBM Blue Gene/Q installation JUQUEEN in Jülich, Germany. The last time I tried to compile Chroma (a lattice quantum chromodynamics package), it did not work with the IBM compiler. – Martin Ueding Dec 24 '17 at 12:53
  • `ixlc` (iSeries = AS400) doesn't support it? But `xlc` / `xlc_r`(AIX = Unix) does? – Sandburg Sep 14 '18 at 16:26
  • I confirm : IBMi (iSeries, as400) doesn't support `#pragma once`(and it is stuck in C++03, no C++11, even TR1 beta testing features). But it supports (thanks shubniggurath) other pragma `#pragma convert(850)` very usefull. – Sandburg Feb 01 '19 at 10:31
5

In using #pragma once you are giving up portability. You are no longer writing C or C++, but something allowed as a compiler extension.

That could cause you headaches if your code ever targets a different platform.

It's for this reason that I never use it.

Given that the name and location of a file is unique, I use that as my include guard. Furthermore because I have in the past targetted very old preprocessors, I use as a habit

#if !defined(foo)
#define foo 1
/*code*/
#endif

which has worked on every platform I've encountered since 1996.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 5
    Yeah. Range-looping thru a nested macro is also super portable.. – rustyx Dec 15 '17 at 11:11
  • That problem is one of the problems I already know about, and don't really about, since it's not a problem in real life. Since unknown pragmas must be ignored, I'm neither giving up portability simply by using pragma once, nor am I no longer writing C or C++. – Clearer Dec 15 '17 at 11:20
  • 1
    @Clearer: It was a problem in my real life. Different platforms can implement `#pragma once` differently. – Bathsheba Dec 15 '17 at 11:23
  • Can you provide an example of two different implementations? – Clearer Dec 15 '17 at 11:24
  • 4
    Most software gives up portability as soon as it starts to do something interesting. – Michaël Roy Dec 15 '17 at 13:22
  • @MichaëlRoy: Perhaps, but it's good design to restrict portability to interface layers, if you get my meaning. – Bathsheba Dec 15 '17 at 13:27
  • 1
    @Bathsheba: Regarding “Given that the name and location of a file is unique”, what do you mean? You choose `foo` as `PATH_TO_FILE`? Or do you use the preprocessor with something like `__FILE__` to get the path? If it is the latter, symlinks might become a problem for you, if some build environment is sufficiently messed up. – Martin Ueding Dec 16 '17 at 12:12
  • 1
    `#pragma` is plebicited by devs regarding evolution of the language: https://hackernoon.com/undefining-the-c-pre-processor-c4eeb3d06e1f . IBM is particular, dont design your code for it, have in mind they are exceptions. And more than anything, don't carry its contrains to other plateforms (lowest denominator). – Sandburg Sep 14 '18 at 16:30