#pragma once vs include guards?

2018-12-31 04:49发布

I'm working on a codebase that is known to only run on windows and be compiled under Visual Studio (it integrates tightly with excel so it's not going anywhere). I'm wondering if I should go with the traditional include guards or use #pragma once for our code. I would think letting the compiler deal with #pragma once will yield faster compiles and is less error prone when copying and pasting. It is also slightly less ugly ;)

Note: to get the faster compile times we could use Redundant Include Guards but that adds a tight coupling between the included file and the including file. Usually it's ok because the guard should be based on the file name and would only change if you needed to change in the include name anyways.

13条回答
后来的你喜欢了谁
2楼-- · 2018-12-31 05:29

Until the day #pragma once becomes standard (that's not currently a priority for the future standards), I suggest you use it AND use guards, this way:

#ifndef BLAH_H
#define BLAH_H
#pragma once

// ...

#endif

The reasons are :

  • #pragma once is not standard, so it is possible that some compiler don't provide the functionality. That said, all major compiler supports it. If a compiler don't know it, at least it will be ignored.
  • As there is no standard behavior for #pragma once, you shouldn't assume that the behavior will be the same on all compiler. The guards will ensure at least that the basic assumption is the same for all compilers that at least implement the needed preprocessor instructions for guards.
  • On most compilers, #pragma once will speed up compilation (of one cpp) because the compiler will not reopen the file containing this instruction. So having it in a file might help, or not, depending on the compiler. I heard g++ can do the same optimization when guards are detected but it have to be confirmed.

Using the two together you get the best of each compiler for this.

Now, if you don't have some automatic script to generate the guards, it might be more convenient to just use #pragma once. Just know what that means for portable code. (I'm using VAssistX to generate the guards and pragma once quickly)

You should almost always think your code in a portable way (because you don't know what the future is made of) but if you really think that it's not meant to be compiled with another compiler (code for very specific embedded hardware for example) then you should just check your compiler documentation about #pragma once to know what you're really doing.

查看更多
深知你不懂我心
3楼-- · 2018-12-31 05:30

I generally don't bother with #pragma once as my code sometimes does have to compile with something other than MSVC or GCC (compilers for embedded systems don't always have the #pragma).

So I have to use #include guards anyway. I could also use #pragma once as some answers suggest, but there doesn't seem to be much reason and it will often cause needless warnings on the compilers that don't support it.

I'm not sure what time savings the pragma might bring. I've heard that compilers generally already recognize when a header has nothing but comments outside of the guard macros and will do the #pragma once equivalent in that case (ie., never processing the file again). But I'm not sure if it's true or just a case of compilers could do this optimization.

In either case, it's just easier for me to use #include guards which will work everywhere and not worry about it further.

查看更多
伤终究还是伤i
4楼-- · 2018-12-31 05:30

For those who would like to use #pragma once and include guards together: If you are not using MSVC, then you won't get much optimization from #pragma once.

And you shouldn't put "#pragma once" into a header that supposed to be included multiple times with each inclusion possibly having a different effect.

Here is a detailed discussion with examples about #pragma once usage.

查看更多
人间绝色
5楼-- · 2018-12-31 05:34

I just wanted to add to this discussion that I am just compiling on VS and GCC, and used to use include guards. I have now switched to #pragma once, and the only reason for me is not performance or portability or standard as I don't really care what is standard as long as VS and GCC support it, and that is that:

#pragma once reduces possibilities for bugs.

It is all too easy to copy and paste a header file to another header file, modify it to suit ones needs, and forget to change the name of the include guard. Once both are included, it takes you a while to track down the error, as the error messages aren't necessarily clear.

查看更多
不流泪的眼
6楼-- · 2018-12-31 05:34

From a software tester's perspective

#pragma once is shorter than an include guard, less error prone, supported by most compilers, and some say that it compiles faster (which is not true [any longer]).

But I still suggest you go with standard #ifndef include guards.

Why #ifndef?

Consider a contrived class hierarchy like this where each of the classes A, B, and C lives inside its own file:

a.h

#ifndef A_H
#define A_H

class A {
public:
  // some virtual functions
};

#endif

b.h

#ifndef B_H
#define B_H

#include "a.h"

class B : public A {
public:
  // some functions
};

#endif

c.h

#ifndef C_H
#define C_H

#include "b.h"

class C : public B {
public:
  // some functions
};

#endif

Now let's assume you are writing tests for your classes and you need to simulate the behaviour of the really complex class B. One way to do this would be to write a mock class using for example google mock and put it inside a directory mocks/b.h. Note, that the class name hasn't changed but it's only stored inside a different directory. But what's most important is that the include guard is named exactly the same as in the original file b.h.

mocks/b.h

#ifndef B_H
#define B_H

#include "a.h"
#include "gmock/gmock.h"

class B : public A {
public:
  // some mocks functions
  MOCK_METHOD0(SomeMethod, void());
};

#endif

What's the benefit?

With this approach you can mock the behaviour of class B without touching the original class or telling C about it. All you have to do is put the directory mocks/ in the include path of your complier.

Why can't this be done with #pragma once?

If you would have used #pragma once, you would get a name clash because it cannot protect you from defining the class B twice, once the original one and once the mocked version.

查看更多
素衣白纱
7楼-- · 2018-12-31 05:38

#pragma once has unfixable bugs. It should never be used.

If your #include search path is sufficiently complicated, the compiler may be unable to tell the difference between two headers with the same basename (e.g. a/foo.h and b/foo.h), so a #pragma once in one of them will suppress both. It may also be unable to tell that two different relative includes (e.g. #include "foo.h" and #include "../a/foo.h" refer to the same file, so #pragma once will fail to suppress a redundant include when it should have.

This also affects the compiler's ability to avoid rereading files with #ifndef guards, but that is just an optimization. With #ifndef guards, the compiler can safely read any file it isn't sure it has seen already; if it's wrong, it just has to do some extra work. As long as no two headers define the same guard macro, the code will compile as expected. And if two headers do define the same guard macro, the programmer can go in and change one of them.

#pragma once has no such safety net -- if the compiler is wrong about the identity of a header file, either way, the program will fail to compile. If you hit this bug, your only options are to stop using #pragma once, or to rename one of the headers. The names of headers are part of your API contract, so renaming is probably not an option.

(The short version of why this is unfixable is that neither the Unix nor the Windows filesystem API offer any mechanism that guarantees to tell you whether two absolute pathnames refer to the same file. If you are under the impression that inode numbers can be used for that, sorry, you're wrong.)

(Historical note: The only reason I didn't rip #pragma once and #import out of GCC when I had the authority to do so, ~12 years ago, was Apple's system headers relying on them. In retrospect, that shouldn't have stopped me.)

(Since this has now come up twice in the comment thread: The GCC developers did put quite a bit of effort into making #pragma once as reliable as possible; see GCC bug report 11569. However, the implementation in current versions of GCC can still fail under plausible conditions, such as build farms suffering from clock skew. I do not know what any other compiler's implementation is like, but I would not expect anyone to have done better.)

查看更多
登录 后发表回答