pragma once

520 views
Skip to first unread message

Сергей Кривонос

unread,
Oct 25, 2016, 2:15:55 AM10/25/16
to ISO C++ Standard - Future Proposals
Hi,

I see everyone use ifdef or pragma once. It is better to use #pragma multiple and make #pragma once default.

--
Thanks
Sergei Krivonos
skype: sergio_krivonos

Ren Industries

unread,
Oct 25, 2016, 10:37:07 AM10/25/16
to std-pr...@isocpp.org
#pragma once isn't part of the standard, I don't think.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGz9X0c%3DCNLVEGgJ7%3DGJ2W%2B7fsvN7h%2BtbiFyf5Cx3aab0Xb%3DrQ%40mail.gmail.com.

D. B.

unread,
Oct 25, 2016, 10:41:13 AM10/25/16
to std-pr...@isocpp.org
There's a lot of text missing, where you explain the background, motivation, details, and considerations of your proposal.

T. C.

unread,
Oct 25, 2016, 4:19:33 PM10/25/16
to ISO C++ Standard - Future Proposals, sergeik...@gmail.com
Yes, let's gratuitously break all existing code that actually relies on multiple inclusion, starting from <cassert>. Why not? /s 

Hans Guijt

unread,
Oct 26, 2016, 2:57:40 AM10/26/16
to ISO C++ Standard - Future Proposals, sergeik...@gmail.com
Making #pragma once the default would break a lot of code, but there is something to be said for standardizing this particular #pragma. While it may be somewhat counter-intuitive to standardize a compiler-specific instruction, the fact is that AFAIK this particular pragma is already supported by every C++ compiler except one (the Sun compiler). I don't think it is necessary to describe the motivation at great length; the benefits of avoiding multiple inclusion should be understood by everyone here.

Obviously, modules are an even better solution, but it is as yet unclear if and when modules will arrive; they might be a decade in the future for all we know. Until then we will need to do _something_ to avoid multiple inclusion, and #pragma once is not only a much cleaner solution than include guards, but also already so widely supported that it might as well be in the standard already. Why not just make it formal?

Matthew Woehlke

unread,
Oct 26, 2016, 10:52:45 AM10/26/16
to std-pr...@isocpp.org
On 2016-10-26 02:57, Hans Guijt wrote:
> #pragma once is [...] already so widely supported that it might as well
> be in the standard already. Why not just make it formal?

Please do your due diligence before making claims like this, or, for
that matter, asking for "obvious" features. I can assure you, this is
not the first time someone has asked to standardize #pragma once.

The problem, besides that the C++ language says nothing about headers in
the first place, is that determining if a header has been included or
not is much more complicated than you think, even on platforms that
*actually have a file system* (which is _not_ a requirement for a C++
compiler!).

What should happen if you include the same header (same inode) via
different names? What about if the compiler sees two identical include
directives (same path, same <> vs. "") that resolve to different files
with different contents? What about platforms with esoteric file
systems, or *no* file system?

Yes, there are implementations that "mostly work", but there is a higher
bar for standardization. Any serious proposal needs to address the
problems that have been raised historically. Claims / requests like
"compilers already do this, why not just make it official" are inadequate.

--
Matthew

Hans Guijt

unread,
Oct 26, 2016, 11:28:20 AM10/26/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
On Wednesday, October 26, 2016 at 4:52:45 PM UTC+2, Matthew Woehlke wrote:
Please do your due diligence before making claims like this, or, for
that matter, asking for "obvious" features. I can assure you, this is
not the first time someone has asked to standardize #pragma once.

So you are saying this is a common request from the community, which is not being answered by the standards committee because ...?
 
The problem, besides that the C++ language says nothing about headers in
the first place, is that determining if a header has been included or
not is much more complicated than you think, even on platforms that
*actually have a file system* (which is _not_ a requirement for a C++
compiler!).

You're reading a really strange copy of the standard then. Mine has a table of "C++ library headers", and a definition what a header-name is. And a great portion of it is dedicated to describing all those headers. Perhaps you are confused by the fact that headers do not have to be files?
 
What should happen if you include the same header (same inode) via
different names? What about if the compiler sees two identical include
directives (same path, same <> vs. "") that resolve to different files
with different contents? What about platforms with esoteric file
systems, or *no* file system?

This is an attempt at complication of a painfully simple feature. You don't want it, so you just ask meaningless questions that have trivial answers in the hope of convincing others it is all incredibly complicated, while it really isn't.

As for answers: it doesn't matter, as long as some kind of deterministic behaviour is specified. I don't see a problem with including a file twice if a symlink exists; after all, you could just copy it and include it twice that way as well. Nobody is asking for a text comparison (or heaven forbid, a semantic comparison). I have some trouble imagining how the same header name could resolve to two different files during the same compilation. And the presence of a file system does not matter - one way or another, headers are found and read by the compiler. That process can be influenced using #pragma once, based on nothing more than the h-char-sequence or q-char-sequence of the header name.

The whole thing is completely trivial, widely used, and supported in all but one modern C++ compilers.

Yes, there are implementations that "mostly work", but there is a higher
bar for standardization. Any serious proposal needs to address the
problems that have been raised historically. Claims / requests like
"compilers already do this, why not just make it official" are inadequate.

Can you show an example of #pragma once failing? Otherwise claims like "mostly work" are just so much noise in the wind. Your "historical problems" seem a collection of far-fetched issues that have no bearing on real-life development. And thanks to your comments, I can now add the claim that this is a feature commonly requested by the C++ community.


Ville Voutilainen

unread,
Oct 26, 2016, 11:34:51 AM10/26/16
to ISO C++ Standard - Future Proposals
On 26 October 2016 at 18:28, Hans Guijt <hgui...@xs4all.nl> wrote:
> On Wednesday, October 26, 2016 at 4:52:45 PM UTC+2, Matthew Woehlke wrote:
>>
>> Please do your due diligence before making claims like this, or, for
>> that matter, asking for "obvious" features. I can assure you, this is
>> not the first time someone has asked to standardize #pragma once.
>
>
> So you are saying this is a common request from the community, which is not
> being answered by the standards committee because ...?

Because #pragma once doesn't work, for the reasons Matthew explained.

>> What should happen if you include the same header (same inode) via
>> different names? What about if the compiler sees two identical include
>> directives (same path, same <> vs. "") that resolve to different files
>> with different contents? What about platforms with esoteric file
>> systems, or *no* file system?
>
>
> This is an attempt at complication of a painfully simple feature. You don't

No, it's an attempt to make the feature work reliably.

> want it, so you just ask meaningless questions that have trivial answers in
> the hope of convincing others it is all incredibly complicated, while it
> really isn't.

Funny, the compiler vendors don't think the answers are trivial, because they
oppose standardizing #pragma once. Because it doesn't work.

>
> As for answers: it doesn't matter, as long as some kind of deterministic
> behaviour is specified. I don't see a problem with including a file twice if

So you don't see a problem with including a file twice even if it has a #pragma
once in it?

> The whole thing is completely trivial

That's a very mistaken belief.

Andrey Semashev

unread,
Oct 26, 2016, 12:15:12 PM10/26/16
to std-pr...@isocpp.org
On 10/26/16 18:28, Hans Guijt wrote:
> On Wednesday, October 26, 2016 at 4:52:45 PM UTC+2, Matthew Woehlke wrote:
>
> The problem, besides that the C++ language says nothing about
> headers in
> the first place, is that determining if a header has been included or
> not is much more complicated than you think, even on platforms that
> *actually have a file system* (which is _not_ a requirement for a C++
> compiler!).
>
> You're reading a really strange copy of the standard then. Mine has a
> table of "C++ library headers", and a definition what a header-name is.
> And a great portion of it is dedicated to describing all those headers.
> Perhaps you are confused by the fact that headers do not have to be files?

For the record, header-name is defined in [lex.header]. In particular,
it says:

The sequences in both forms of header-names are mapped in an
implementation-defined manner to headers or to external source file
names as specified in 16.2.

And 16.2 leaves the interpretation of the header-name pretty much to the
implementation. There may well be no files at all in the filesystem that
correspond to headers described in the standard.

> What should happen if you include the same header (same inode) via
> different names? What about if the compiler sees two identical include
> directives (same path, same <> vs. "") that resolve to different files
> with different contents? What about platforms with esoteric file
> systems, or *no* file system?
>
> This is an attempt at complication of a painfully simple feature. You
> don't want it, so you just ask meaningless questions that have trivial
> answers in the hope of convincing others it is all incredibly
> complicated, while it really isn't.

I think you should do more research before making claims like that.

> As for answers: it doesn't matter, as long as some kind of deterministic
> behaviour is specified. I don't see a problem with including a file
> twice if a symlink exists; after all, you could just copy it and include
> it twice that way as well.

I'm certainly not ok with ignoring symlinks. There are quite a few
symlinks in my /usr/include; if headers used #pragma once there and it
didn't work, it would be a disaster.

> Nobody is asking for a text comparison (or
> heaven forbid, a semantic comparison). I have some trouble imagining how
> the same header name could resolve to two different files during the
> same compilation.

The same header can be accessible by different header-name tokens,
depending on where it is included from and what -I switches were
specified. Conversely, the same header-name may resolve to different
headers depending on the implementation and where they are included from.

<root>
|
|-a
| |- x.h
| |- a.h
|
|-b
| |- x.h
| |- b.h
|
|- main.cpp

main.cpp:

#include "a/a.h"
#include "b/b.h"

a.h:

#include "x.h"

b.h:

#include "x.h"

If the implementation includes the directory of the including header
into the lookup list for the "" syntax (most compilers do) then a.h and
b.h will include different x.h.

> And the presence of a file system does not matter -
> one way or another, headers are found and read by the compiler. That
> process can be influenced using #pragma once, based on nothing more than
> the h-char-sequence or q-char-sequence of the header name.

Wrong, as shown above.

> Yes, there are implementations that "mostly work", but there is a
> higher
> bar for standardization. Any serious proposal needs to address the
> problems that have been raised historically. Claims / requests like
> "compilers already do this, why not just make it official" are
> inadequate.
>
> Can you show an example of #pragma once failing? Otherwise claims like
> "mostly work" are just so much noise in the wind. Your "historical
> problems" seem a collection of far-fetched issues that have no bearing
> on real-life development. And thanks to your comments, I can now add the
> claim that this is a feature commonly requested by the C++ community.

FWIW, #pragma once was broken in gcc prior to 3.4, it was "confused by
symlinks and hardlinks" [1] before that. The bug is long fixed, but it
indicates that those "historical problems" are not imaginary. You may
also google around and see a few more recent bugs related to #pragma
once, like this[2] for example.

[1]: https://gcc.gnu.org/gcc-3.4/changes.html
[2]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58770

Nicol Bolas

unread,
Oct 26, 2016, 12:24:12 PM10/26/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com, hgui...@xs4all.nl
On Wednesday, October 26, 2016 at 11:28:20 AM UTC-4, Hans Guijt wrote:
On Wednesday, October 26, 2016 at 4:52:45 PM UTC+2, Matthew Woehlke wrote:
Please do your due diligence before making claims like this, or, for
that matter, asking for "obvious" features. I can assure you, this is
not the first time someone has asked to standardize #pragma once.

So you are saying this is a common request from the community, which is not being answered by the standards committee because ...?
 
The problem, besides that the C++ language says nothing about headers in
the first place, is that determining if a header has been included or
not is much more complicated than you think, even on platforms that
*actually have a file system* (which is _not_ a requirement for a C++
compiler!).

You're reading a really strange copy of the standard then. Mine has a table of "C++ library headers", and a definition what a header-name is.

At no point does the standard specify that "header-names" are files on a file system. They are merely "header-names".
 
And a great portion of it is dedicated to describing all those headers. Perhaps you are confused by the fact that headers do not have to be files?
 
What should happen if you include the same header (same inode) via
different names? What about if the compiler sees two identical include
directives (same path, same <> vs. "") that resolve to different files
with different contents? What about platforms with esoteric file
systems, or *no* file system?

This is an attempt at complication of a painfully simple feature.

And yet, this complication is precisely the problem.

With include guards, the question is effectively moot. Every user does their best to avoid include guard clashing, so there is no question of what "same file" means. It means having included that define.

But with #pragma once, all those questions matter now. If you say that it's all implementation-defined,

Yes, #pragma once works in the most common cases: basic loose files without any hard-links or whatever. But not everyone does that. So what should the behavior of #pragma once be in that circumstance?

The standard specifies behavior. If some behavior cannot be fully pinned down, then the standard cannot specify what happens.


You don't want it, so you just ask meaningless questions that have trivial answers in the hope of convincing others it is all incredibly complicated, while it really isn't.

As for answers: it doesn't matter, as long as some kind of deterministic behaviour is specified. I don't see a problem with including a file twice if a symlink exists; after all, you could just copy it and include it twice that way as well. Nobody is asking for a text comparison (or heaven forbid, a semantic comparison). I have some trouble imagining how the same header name could resolve to two different files during the same compilation. And the presence of a file system does not matter - one way or another, headers are found and read by the compiler. That process can be influenced using #pragma once, based on nothing more than the h-char-sequence or q-char-sequence of the header name.

So you want #pragma once to work based on just the character sequence in the include? So in one file, I #include a file with a directory ("ghl/some_file.h"). But for files in the "ghl" directory, there would be no need for such a directory. So by your reasoning, "ghl/some_file.h" would be considered different from "some_file."h

That's not a functional idea.
 
The whole thing is completely trivial, widely used, and supported in all but one modern C++ compilers.

Oddly enough, it's not supported the way you just defined it. So how "trivial" can it be if you got the behavior wrong?

Andrey Semashev

unread,
Oct 26, 2016, 12:39:08 PM10/26/16
to std-pr...@isocpp.org
BTW, this SO answer and the discussion in the comments might be
enlightening:

http://stackoverflow.com/questions/1143936/pragma-once-vs-include-guards/34884735#34884735

Viacheslav Usov

unread,
Oct 26, 2016, 12:44:23 PM10/26/16
to ISO C++ Standard - Future Proposals, Matthew Woehlke, hgui...@xs4all.nl
On Wed, Oct 26, 2016 at 6:24 PM, Nicol Bolas <jmck...@gmail.com> wrote:

> With include guards, the question is effectively moot. Every user does their best to avoid include guard clashing, so there is no question of what "same file" means. It means having included that define.

Could we perhaps modify the proposal as follows:

#pragma once is implementation-defined behaviour.

#pragma once(SOME_GUARD_SYMBOL) is equivalent to having used SOME_GUARD_SYMBOL as an include guard.

Granted, this is not as succinct as #pragma once, but it still has the benefit of specifying the include guard symbol just in one place, thus eliminating the possibility of mistakes like

#ifndef  SOME_GUARD_SYMBOL
#define SAME_GUARD_SYMBOL
#endif  // SUM_GUARD_SYMBOL

Cheers,
V.

hgui...@xs4all.nl

unread,
Oct 26, 2016, 1:22:15 PM10/26/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com, hgui...@xs4all.nl
On Wednesday, October 26, 2016 at 6:24:12 PM UTC+2, Nicol Bolas wrote:
You're reading a really strange copy of the standard then. Mine has a table of "C++ library headers", and a definition what a header-name is.

At no point does the standard specify that "header-names" are files on a file system. They are merely "header-names".

I completely fail to see how that is in any way material to the discussion. The purpose of #pragma once, or include guards, is to stop multiple inclusion. Nowhere, absolutely nowhere, is it specified that we only want to stop multiple inclusion if the underlying mechanism happens to be "files".
 
This is an attempt at complication of a painfully simple feature.

And yet, this complication is precisely the problem.

With include guards, the question is effectively moot. Every user does their best to avoid include guard clashing, so there is no question of what "same file" means. It means having included that define.

If you throw the whole issue into the lap of the user you end up with the exact same problem, reversed: what if two different files have the same include guards? This has actually happened to me - I mistakenly put the same include guards into two different files. Silly mistake, but it certainly made for some interesting debugging... The two situations are symmetric, but #pragma once has a clear performance advantage (since there is no need to re-parse the entire header to find out whether what look like include guards, really are), not to mention having much cleaner semantics.
 
Yes, #pragma once works in the most common cases: basic loose files without any hard-links or whatever. But not everyone does that. So what should the behavior of #pragma once be in that circumstance?

You're throwing out the baby with the bath water. Any project that has some weird setup where the same header is included multiple times into the same compilation unit through hardlinks can still use include guards. Everybody else can use #pragma once. Or if you truly think it's an issue, make the compiler finds whatever passes for a unique identifier for includeable objects (whether they are files or not), and use that as your identifier. That will work for everyone.
 
The standard specifies behavior. If some behavior cannot be fully pinned down, then the standard cannot specify what happens.

Don't make me laugh. The standard is absolutely chockful of implementation defined situations, not to mention throwing around undefined behaviours like they were going out of style. Adding one more bit of implementation-defined behaviour will not make a difference.
 
You don't want it, so you just ask meaningless questions that have trivial answers in the hope of convincing others it is all incredibly complicated, while it really isn't.

As for answers: it doesn't matter, as long as some kind of deterministic behaviour is specified. I don't see a problem with including a file twice if a symlink exists; after all, you could just copy it and include it twice that way as well. Nobody is asking for a text comparison (or heaven forbid, a semantic comparison). I have some trouble imagining how the same header name could resolve to two different files during the same compilation. And the presence of a file system does not matter - one way or another, headers are found and read by the compiler. That process can be influenced using #pragma once, based on nothing more than the h-char-sequence or q-char-sequence of the header name.

So you want #pragma once to work based on just the character sequence in the include? So in one file, I #include a file with a directory ("ghl/some_file.h"). But for files in the "ghl" directory, there would be no need for such a directory. So by your reasoning, "ghl/some_file.h" would be considered different from "some_file."h

Don't twist my words. The compiler is gifted with a method for finding and loading whatever is specified after #include (whether files, or something else). If it can figure out where to load it from, it can also figure out whether it has done that before. Remembering that #pragma once was set in that particular entity is also no hardship.

Your example, using a directory-separator, is implementation defined as well - you rely on the fact that the compiler first searches in the same path as the file currently being parsed. There is no guarantee this is the case; it's just something that _mostly works_. There is in fact no guarantee that directories even exist on a system (and unlike the silly notion that files might not exist, there are operating systems out there that do not support directories). Why are you fine with that, yet insist on finding trouble in what I can only think of as an extended use of the exact same information?

As for the whole "but it doesn't have to be files" discussion... Where the standard says "implementation defined", it is talking about things like search paths, not about beaming in the information from the planet Vulkan by directly flipping bits in memory. Maybe the problem is that the entire section of the standard needs tightening - with a proper discussion of files, directories, search paths, etc. As it is, cross-platform compatibility of source code exists solely by the grace of Microsoft supporting / as a directory separator even though Windows does not support it...


Matthew Woehlke

unread,
Oct 26, 2016, 1:47:53 PM10/26/16
to std-pr...@isocpp.org
On 2016-10-26 13:22, hgui...@xs4all.nl wrote:
> On Wednesday, October 26, 2016 at 6:24:12 PM UTC+2, Nicol Bolas wrote:
>> With include guards, the question is effectively moot. Every user does
>> their best to avoid include guard clashing, so there is no question of what
>> "same file" means. It means having included that define.
>
> If you throw the whole issue into the lap of the user you end up with the
> exact same problem, reversed: what if two different files have the same
> include guards?

...then you don't include the second one. This is not an unreasonable
choice; what if the two files with different contents are different
versions of the "conceptually" same file? If you include it twice (which
is what #pragma once would presumably do), you are also going to have
problems. Possibly worse problems than if you only include one of them
(e.g. if they are different but source-compatible).

> This has actually happened to me - I mistakenly put the same include
> guards into two different files. Silly mistake, but it certainly
> made for some interesting debugging...

There are tools to avoid this sort of problem.

> The two situations are symmetric, but #pragma once has a clear
> performance advantage (since there is no need to re-parse the entire
> header to find out whether what look like include guards, really
> are)

No, it has no such "clear performance advantage". Compilers can and do
implement the same optimization for traditional include guards, so this
is moot. As you would know if you had actually *read* the SO answer
Andrey posted.

Do. Your. Homework.

> Any project that has some weird setup where the same header is
> included multiple times into the same compilation unit through
> hardlinks can still use include guards.

Right. And since those headers may come from third party libraries,
*everyone* must use include guards.

It's more likely that a leaf project might get away with #pragma once
than a widely used library.

> Or if you truly think it's an issue, make the compiler finds whatever
> passes for a unique identifier for includeable objects (whether they
> are files or not), and use that as your identifier.

Right. That's the problem that *you* need to solve.

>> The standard specifies behavior. If some behavior cannot be fully pinned
>> down, then the standard cannot specify what happens.
>
> Don't make me laugh. The standard is absolutely chockful of implementation
> defined situations, not to mention throwing around undefined behaviours
> like they were going out of style. Adding one more bit of
> implementation-defined behaviour will not make a difference.

But what is the *point* of standardizing a feature if *the entire
definition of the feature* is that it's implementation defined? In what
way is that different from the situation today?

(Wait... that's *already* the case; #pragma is already implementation
defined. What exactly, then, are you proposing to change?)

> The compiler is gifted with a method for finding and loading whatever
> is specified after #include (whether files, or something else). If it
> can figure out where to load it from, it can also figure out whether
> it has done that before.

Not if it finds an identical file in a different path.

--
Matthew

hgui...@xs4all.nl

unread,
Oct 26, 2016, 1:52:19 PM10/26/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 26, 2016 at 6:15:12 PM UTC+2, Andrey Semashev wrote:
> Nobody is asking for a text comparison (or
> heaven forbid, a semantic comparison). I have some trouble imagining how
> the same header name could resolve to two different files during the
> same compilation.

The same header can be accessible by different header-name tokens,
depending on where it is included from and what -I switches were
specified. Conversely, the same header-name may resolve to different
headers depending on the implementation and where they are included from.

Do you agree that ultimately, the compiler manages to find each header, somewhere in the multiverse, in a way that is at least repeatable? I'm not asking for it to be a file. I'm not asking for the characters after #include to be unique. I'm not asking for specific behaviour in the presence of directories or search paths or whatever. I'm only asking that if you compile something twice in a row on the same sytem with the same compiler, it will include the same header at each include statement it encounters.

Because if there is a repeatable mapping, it means that headers have an identity. And that means you can reliably determine that something was included before, and you can skip that include operation if you performed it before and encountered #pragma once.

Now, maybe you need to figure out the "true name" of the header (underlying inode, or whatever passes for an inode in your implementation). That does not, however, seem like an insurmountable problem.
 
FWIW, #pragma once was broken in gcc prior to 3.4, it was "confused by
symlinks and hardlinks" [1] before that. The bug is long fixed, but it
indicates that those "historical problems" are not imaginary. You may
also google around and see a few more recent bugs related to #pragma
once, like this[2] for example.

"The bug is long fixed" says it all, really. If it was a fundamental problem, the bug could never have been fixed.

As for your bug[2] - I'm amazed that identifying, opening, and parsing the file is actually faster than simply identifying it, but don't you agree that this is probably just an implementation issue that could be easily fixed? If you stick all the #pragma once files in a linked list (as likely a choice as any, given that the number of include files in a given compilation is normally relatively low) then loading it with a huge number of files will be slow. As one of the comments says, this could be fixed by using a hashmap.


Andrey Semashev

unread,
Oct 26, 2016, 2:31:37 PM10/26/16
to std-pr...@isocpp.org
On 10/26/16 20:52, hgui...@xs4all.nl wrote:
> On Wednesday, October 26, 2016 at 6:15:12 PM UTC+2, Andrey Semashev wrote:
>
> The same header can be accessible by different header-name tokens,
> depending on where it is included from and what -I switches were
> specified. Conversely, the same header-name may resolve to different
> headers depending on the implementation and where they are included
> from.
>
> Do you agree that ultimately, the compiler manages to find each header,
> somewhere in the multiverse, in a way that is at least repeatable? I'm
> not asking for it to be a file. I'm not asking for the characters after
> #include to be unique. I'm not asking for specific behaviour in the
> presence of directories or search paths or whatever. I'm only asking
> that if you compile something twice in a row on the same sytem with the
> same compiler, it will include the same header at each include statement
> it encounters.

Yes, invoking the compiler twice should yield the same result on the
same input. But that is not what #pragma once is about.

> Because if there is a repeatable mapping, it means that headers have an
> identity.

True.

> And that means you can reliably determine that something was
> included before,

Not true (at least, it doesn't follow). Locating and opening a file is
one thing. Ensuring that two paths refer to one file is another.

> FWIW, #pragma once was broken in gcc prior to 3.4, it was "confused by
> symlinks and hardlinks" [1] before that. The bug is long fixed, but it
> indicates that those "historical problems" are not imaginary. You may
> also google around and see a few more recent bugs related to #pragma
> once, like this[2] for example.
>
> "The bug is long fixed" says it all, really. If it was a fundamental
> problem, the bug could never have been fixed.

The link to the SO answer provided an insight on this from a GCC
developer. In the comments he admits that that bug is not fully fixed
(something I was not aware myself when I wrote my first reply in this
thread).

> As for your bug[2] - I'm amazed that identifying, opening, and parsing
> the file is actually faster than simply identifying it, but don't you
> agree that this is probably just an implementation issue that could be
> easily fixed?

If it's that easy to fix, it would've been already, don't you think?

Regarding performance, the compiler can already optimize away opening
and parsing files in some cases, when (a) it knows that the file has
include guards and (b) it is absolutely sure that is the same file has
been parsed before. That gives include guards practically the same
performance as you would expect from #pragma once. I believe, that is
what gcc does and recent MSVC as well.

The upside of inlcude guards is that there is no harm (aside from
performance drop) if (b) cannot be proved and the file is parsed again -
the preprocessor will just skip the file content. The problem with
#pragma once is that there is no longer this fallback, and if a mistake
is made in (b) then the code is miscompiled. And I gather, (b) is very
hard or plainly impossible to implement to work in all cases.

Matthew Woehlke

unread,
Oct 26, 2016, 2:46:30 PM10/26/16
to std-pr...@isocpp.org
On 2016-10-26 13:52, hgui...@xs4all.nl wrote:
> Now, maybe you need to figure out the "true name" of the header (underlying
> inode, or whatever passes for an inode in your implementation). That does
> not, however, seem like an insurmountable problem.

Apparently (disclaimer: I am repeating statements made elsewhere, though
I think I can guess the underlying reasoning) this is harder than you
think. I suspect that computing a hash of the file is the only reliable
method. (We'll assume the contents are at least constant during
compilation, since if they aren't, you're probably doomed no matter what.)

--
Matthew

Nicol Bolas

unread,
Oct 26, 2016, 3:03:36 PM10/26/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com, hgui...@xs4all.nl


On Wednesday, October 26, 2016 at 1:22:15 PM UTC-4, hgui...@xs4all.nl wrote:
On Wednesday, October 26, 2016 at 6:24:12 PM UTC+2, Nicol Bolas wrote:
This is an attempt at complication of a painfully simple feature.

And yet, this complication is precisely the problem.

With include guards, the question is effectively moot. Every user does their best to avoid include guard clashing, so there is no question of what "same file" means. It means having included that define.

If you throw the whole issue into the lap of the user you end up with the exact same problem, reversed: what if two different files have the same include guards? This has actually happened to me - I mistakenly put the same include guards into two different files. Silly mistake, but it certainly made for some interesting debugging... The two situations are symmetric, but #pragma once has a clear performance advantage (since there is no need to re-parse the entire header to find out whether what look like include guards, really are), not to mention having much cleaner semantics.

The solutions are not symmetric.

In the case of include guards, a programmer has the power to change what include guard a header uses. Failing that (if they don't own the conflicting headers, for example), the user can write a header wrapper which has its own unique include guard that undefines the include guard if present, includes the file, and then redefines it.

With #pragma once, you have to go mucking about with the filesystem to fix it.

Diagnosing the problem with include guards is much easier as well. You just read the two headers and see that their include guards match. By contrast, diagnosing #pragma once issues requires deep filesystem manipulation.

I would much rather have an imperfect tools where problems are both easily detected and resolved than an imperfect tool that fails in ways that requires out-of-C++ resolutions.

Yes, #pragma once works in the most common cases: basic loose files without any hard-links or whatever. But not everyone does that. So what should the behavior of #pragma once be in that circumstance?

You're throwing out the baby with the bath water. Any project that has some weird setup where the same header is included multiple times into the same compilation unit through hardlinks can still use include guards.

But that's not a thing that the writer of a library typically has control over. Such things can happen via package managers or users installing them to one place and hard-linking them to others.

Everybody else can use #pragma once. Or if you truly think it's an issue, make the compiler finds whatever passes for a unique identifier for includeable objects (whether they are files or not), and use that as your identifier. That will work for everyone.
 
The standard specifies behavior. If some behavior cannot be fully pinned down, then the standard cannot specify what happens.

Don't make me laugh. The standard is absolutely chockful of implementation defined situations, not to mention throwing around undefined behaviours like they were going out of style. Adding one more bit of implementation-defined behaviour will not make a difference.

But those situations are explicitly listed as being implementation-defined. How do you specify when #pragma once's behavior would be implementation-defined and when it could be reasonably relied upon?

After all, "#pragma once" is already implementation-defined.

Andrey Semashev

unread,
Oct 26, 2016, 3:07:32 PM10/26/16
to std-pr...@isocpp.org
I think, hashing doesn't work either in some cases. Like in my example:

<root>
|
|-a
| |- x.h
| |- a.h
|
|-b
| |- x.h
| |- b.h
|
|- main.cpp

main.cpp:

#include "a/a.h"
#include "b/b.h"

a.h:

#include "x.h"

b.h:

#include "x.h"

If both x.h contain something like this:

#pragma once

#define MY_VAR(x) x ## __COUNTER__

int MY_VAR(my_var) = 10;

the implementation would only define my_var0 and not my_var1 while
compiling main.cpp.

Even if the implementation tests that the two x.h are in different
directories, they can be symlinked to one file. And if compilation is
done over a network file system that does not expose symlinks (like
Samba, for instance), the compiler might not be able to know that.

Nevin Liber

unread,
Oct 26, 2016, 3:39:16 PM10/26/16
to std-pr...@isocpp.org
On Wed, Oct 26, 2016 at 1:46 PM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
Apparently (disclaimer: I am repeating statements made elsewhere, though
I think I can guess the underlying reasoning) this is harder than you
think. I suspect that computing a hash of the file is the only reliable
method. (We'll assume the contents are at least constant during
compilation, since if they aren't, you're probably doomed no matter what.)

Include guards are about "do these files have the same contents"?

#pragma once is about "do these names refer to the same file (roughly, bits from the same spot on a disk)", which is a question compilers generally punt on by just asking the OS for the answer.

They are different questions.

Let's say I have these files:

a/z.h
b/y.h (hard link to a/z.h)
c/x.h (symlink to a/z.h)
d/w.h (copy of a/z.h)
e/z.h (e/ is the same filesystem as a/ mounted at a different location)

Include guards would say that all of the files have the same contents.

I would expect that #pragma once would consider a/z.h, b/y.h and c/x.h to be the same file, and d/z.h to be different from those three and e/z.h to be different from those four.  Another possibility is that a/z.h,b/y.h, c/x.h are considered different files because they have different base names.

We could make a #once feature give the same answer as include guards, but as Matthew pointed out, that would requiring hashing (fingerprinting) files to determine if two files have the same contents when the OS cannot determine if two filenames either refer to the same file or have different content (differing file sizes implies different content, for example).  The advantage of #once over include guards is that include guards can be mistaken (using the same guard in two unrelated files).

I wonder how much fingerprint hashing would slow down compilation?
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Bo Persson

unread,
Oct 26, 2016, 4:25:27 PM10/26/16
to std-pr...@isocpp.org
It fails here already.

On a development machine you can have network mounts to ANY existing
file system. Most of them don't have an "inode" or a "whatever".

How are you to get the "true name" there?

Standardizing "#pragma once" not only requires that the standards
committee specifies this in all the details, but also that every
implementation of a C++ compiler will have to have test cases for this.

Are we to require that they test their compiler with mounts to every
existing operating system? How else will we know that it works?


Bo Persson





Aaron McDaid

unread,
Oct 26, 2016, 5:48:54 PM10/26/16
to ISO C++ Standard - Future Proposals, sergeik...@gmail.com
Hi all,
I'd like to join this conversation by asking everybody to "reset" a little. There is no reason whatsoever to think that we need to get distracted about filesystem issues.

Also, frankly, the tone of some commentators here is very arrogant. This is my first time in an isocpp.org forum and I'm already not impressed with the community.


When '#include' is encountered, the compiler will use some arbitrary mechanism to convert the included "pseudo-file-name" into a long string of characters. One possible proposal is that, once that long string is fully read it, it will be hashed. If a given hash has been encountered before, and the first characters of the long string are `#pragma once`, then only the first include will be retained.

That's simple to understand and to define precisely in the standard. I guess it's just a tweak of Phase 4 of the translation phases: http://en.cppreference.com/w/cpp/language/translation_phases

I do not know, nor care, whether the behaviour I have just defined is equivalent to the behaviour currently in any compiler. If there is a subtle difference in some extreme case, then I don't care. Are you including two different files with the same contents? If so, I don't care if we change the behaviour a little!

There needn't be a speed issue. A naive implemention would be forced to read in all of the header file before processing any of it. But it would be easy to optimize this with a little "lookahead" to confirm that a given included-file is indeed novel and therefore the compiler can proceed with 'single-pass' compilation.

Of course, maybe there is some issue I have overlooked. I'm not claiming to be perfect, but I do believe I'm not the only person who thinks '#pragma once' could be standardized in some form if we just showed a little imagination.

If this has been argued to death before, please provide links to the discussion to prove

Aaron

T. C.

unread,
Oct 26, 2016, 6:03:50 PM10/26/16
to ISO C++ Standard - Future Proposals

On Wednesday, October 26, 2016 at 5:48:54 PM UTC-4, Aaron McDaid wrote:

If this has been argued to death before, please provide links to the discussion to prove

Aaron McDaid

unread,
Oct 26, 2016, 6:17:46 PM10/26/16
to ISO C++ Standard - Future Proposals
So perhaps next time, we can say something like:

"If we define 'same file' as meaning 'having the same contents', then '#pragma once' is easy to unambigously define. But implementors currently feel it would be too much work and computationally challenging to check if a given included-string-from-a-file has been included before. So, it is easy to standardize, just challenging for the compiler developers. The only alternative is to define '#pragma once' as meaning 'refers to the same file' but that's impossible to do reliably in practice (symlinks ...). Therefore, this isn't going anywhere anytime soon and has been discussed before. Crucially, it's difficult to say anything new about it."

Aaron

Ren Industries

unread,
Oct 26, 2016, 6:29:52 PM10/26/16
to std-pr...@isocpp.org
That's what was said, just with more purple prose.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Thiago Macieira

unread,
Oct 27, 2016, 5:02:39 PM10/27/16
to std-pr...@isocpp.org
On quarta-feira, 26 de outubro de 2016 18:44:19 PDT Viacheslav Usov wrote:
> #pragma once(SOME_GUARD_SYMBOL) is equivalent to having used
> SOME_GUARD_SYMBOL as an include guard.
>
> Granted, this is not as succinct as #pragma once, but it still has the
> benefit of specifying the include guard symbol just in one place, thus
> eliminating the possibility of mistakes like
>
> #ifndef SOME_GUARD_SYMBOL
> #define SAME_GUARD_SYMBOL
> #endif // SUM_GUARD_SYMBOL

The comment on the endif is irrelevant. The #ifndef/#define problem is highly
unlikely ever since Copy & Paste was invented.

So, yes, there is a benefit of #pragma once with a guard symbol that you define,
but it's very minimal because it doesn't solve the real problem that guard
macros have, namely that of clashes between libraries. It only saves you some
typing.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Thiago Macieira

unread,
Oct 27, 2016, 5:16:37 PM10/27/16
to std-pr...@isocpp.org
On quarta-feira, 26 de outubro de 2016 12:03:35 PDT Nicol Bolas wrote:
> The *solutions* are not symmetric.
>
> In the case of include guards, a programmer has the power to change what
> include guard a header uses. Failing that (if they don't own the
> conflicting headers, for example), the user can write a header wrapper
> which has its own unique include guard that undefines the include guard if
> present, includes the file, and then redefines it.

Let me echo Nicol's arguments here. The solutions are not symmetric. There's a
transfer of time-of-failure and of responsibility between the two solutions.

With include guards, the responsibility of ensuring the guard is correct and
unique lies with the author of the header in question. Any problems that
happen, will (to a 99% approximation) happen while developing that header in
the first place. So it's highly unlikely that the author developer will ship
such a broken header to other people.

With the pragma, the responsibility of ensuring the de-duplication lies with
the compiler author and the issues happen at time of compilation, possibly on
someone else's machine. The author of the header has disclaimed the problem
and just made it SEP.

We can compound the fact with multiple software packages, developed by
different people. With include guards, the producer of a library wouldn't ship
the library in the first place with the problem, so the consumer of said
library won't have the problem. With the pragma, since the library author made
it SEP, it is now the responsibility of the library consumer to figure out the
problem.

And note that library consumer does not mean "another developer". It can be
just a user who downloaded the source and wants to compile. Said user may not
have the technical expertise to diagnose the problem in the first place. Asking
for help on technical forums may not help because would-be helpers would need
to know the layout of the filesystem -- they can't just look at the copy of the
library that they may have locally.

> > You're throwing out the baby with the bath water. Any project that has
> > some weird setup where the same header is included multiple times into the
> > same compilation unit through hardlinks can still use include guards.
>
> But that's not a thing that the writer of a library typically has control
> over. Such things can happen via package managers or users installing them
> to one place and hard-linking them to others.

That's the transfer of responsibility I mentioned.

> > Don't make me laugh. The standard is absolutely chockful of implementation
> > defined situations, not to mention throwing around undefined behaviours
> > like they were going out of style. Adding one more bit of
> > implementation-defined behaviour will not make a difference.
>
> But those situations are *explicitly* listed as being
> implementation-defined. How do you specify when #pragma once's behavior
> would be implementation-defined and when it could be reasonably relied upon?
>
> After all, "#pragma once" is *already* implementation-defined.

At best, we can have the standard say that #pragma once is recognised (will
not cause a compilation error by itself) and its behaviour is implementation
defined. The standard can give some hints on what it wants compiler developers
to do, but the actual choice of what to implement lies with the compiler
developer.

Including doing nothing. That's a perfectly valid "implementation-defined"
solution.

That means #pragma once can't be relied upon as a portable feature, even if
standardised.

Miro Knejp

unread,
Oct 27, 2016, 5:37:22 PM10/27/16
to std-pr...@isocpp.org
Am 27.10.2016 um 23:16 schrieb Thiago Macieira:
> On quarta-feira, 26 de outubro de 2016 12:03:35 PDT Nicol Bolas wrote:
>> The *solutions* are not symmetric.
>>
>> In the case of include guards, a programmer has the power to change what
>> include guard a header uses. Failing that (if they don't own the
>> conflicting headers, for example), the user can write a header wrapper
>> which has its own unique include guard that undefines the include guard if
>> present, includes the file, and then redefines it.
> Let me echo Nicol's arguments here. The solutions are not symmetric. There's a
> transfer of time-of-failure and of responsibility between the two solutions.
>
> With include guards, the responsibility of ensuring the guard is correct and
> unique lies with the author of the header in question. Any problems that
> happen, will (to a 99% approximation) happen while developing that header in
> the first place. So it's highly unlikely that the author developer will ship
> such a broken header to other people.
I can't really follow the logic here. Nobody is going to intenionally
create headers with conflicting guards in the same library. But that
doens't guarantee uniqueness at all. I can't possibly know whether my
library guard will or will not conflict with the guard of some other
library that happens to also have the same file name. In that scenario
the user is faced with the same problem you describe later.

But one thing does make me wonder. A previous post mentioned using
#pragma once as a marker for the compiler to compute hashes of the
content. I wonder whether it's faster to compute said hash than to scan
for the header guard. Because if it is, we do have a portable and
reliable way of making preventing multiple inclusion of headers
containing #pragma once. The standard doens't even have to specify *how*
the identity is established, only that the content of a header that
contains #pragma once must only be processed once per TU. It won't
prevent the overhead of opening and reading the file but that's just the
status quo with guards anyway.

Nevin Liber

unread,
Oct 27, 2016, 6:18:22 PM10/27/16
to std-pr...@isocpp.org
On Thu, Oct 27, 2016 at 4:39 PM, Miro Knejp <miro....@gmail.com> wrote:
But one thing does make me wonder. A previous post mentioned using #pragma once as a marker for the compiler to compute hashes of the content. I wonder whether it's faster to compute said hash than to scan for the header guard.

There are three possibilities:
  1. It is the same file according to the OS (e.g.: inode and device match).  There is no reason to re-open the file.
  2. It must have different contents according to the OS (e.g. the size doesn't match).  You probably could compute the fingerprint hash at the same time you are reading the file.  You still have to scan for include guards, as that is just normal macro processing.
  3. The OS doesn't know if the files have the same content.  I'm pretty sure this would require an extra pass to compute the fingerprint hash, as you shouldn't be processing the file until you are sure it is unique.  You still have to scan for include guards as that is just normal macro processing.
Typically people use cryptographic hashes for fingerprinting (MD5, SHA, etc.), which are slow to compute.

All files have to go through (2) or (3) the first time they are encountered.  (2) is slower than just scanning for including guards as it is strictly doing more work.  (3) is slower than (2) because it requires an extra I/O, cache-unfriendly pass.

In practical terms, (3) won't happen all that often, so it shouldn't dominate the compilation time.

Because if it is, we do have a portable and reliable way of making preventing multiple inclusion of headers containing #pragma once. The standard doens't even have to specify *how* the identity is established, only that the content of a header that contains #pragma once must only be processed once per TU. It won't prevent the overhead of opening and reading the file but that's just the status quo with guards anyway.

If we agree that #once is about files with the same exact content (before things like tokenizing), yes we know how to do it, but we don't know what the actual cost is because it hasn't been implemented and benchmarked.  It will be slower than the status quo.  And you still have to process include guards because they are just normal macro processing and they aren't going away any time soon.

Andrey Semashev

unread,
Oct 27, 2016, 7:06:23 PM10/27/16
to ISO C++ Standard - Future Proposals
On Fri, Oct 28, 2016 at 1:17 AM, Nevin Liber <ne...@eviloverlord.com> wrote:
>
> There are three possibilities:
>
> 1. It is the same file according to the OS (e.g.: inode and device match).
> 2. It must have different contents according to the OS (e.g. the size doesn't
> match).

I think, the above two bullets cannot be implemented reliably. Event
the size may not be available if the source is read from a pipe.

> 3. The OS doesn't know if the files have the same content. I'm pretty sure
> this would require an extra pass to compute the fingerprint hash, as you
> shouldn't be processing the file until you are sure it is unique. You still
> have to scan for include guards as that is just normal macro processing.

As I shown earlier, basing the decision on the hash can result in
incorrect result. IMO, content equivalence is not the right criteria
for skipping file inclusion.

I think a proposal similar to the previously mentioned "#once
guard_phrase" has more potential. It does give the user a way to
indicate file equivalence (through the guard name) and also is
arguably less expensive than computing a hash value.

Miro Knejp

unread,
Oct 27, 2016, 7:47:13 PM10/27/16
to std-pr...@isocpp.org
Am 28.10.2016 um 01:06 schrieb Andrey Semashev:
> On Fri, Oct 28, 2016 at 1:17 AM, Nevin Liber <ne...@eviloverlord.com> wrote:
>> There are three possibilities:
>>
>> 1. It is the same file according to the OS (e.g.: inode and device match).
>> 2. It must have different contents according to the OS (e.g. the size doesn't
>> match).
> I think, the above two bullets cannot be implemented reliably. Event
> the size may not be available if the source is read from a pipe.
>
>> 3. The OS doesn't know if the files have the same content. I'm pretty sure
>> this would require an extra pass to compute the fingerprint hash, as you
>> shouldn't be processing the file until you are sure it is unique. You still
>> have to scan for include guards as that is just normal macro processing.
> As I shown earlier, basing the decision on the hash can result in
> incorrect result. IMO, content equivalence is not the right criteria
> for skipping file inclusion.
Your example doesn't work precisely *because* the behavior of #pragma
once isn't specified. Anyone can come up with a contrived example that
breaks a feature which hasn't been defined yet, implying the feature
must be impossible because it cannot solve an artificial use case it
probably isn't even designed to solve.

If such a utility is added (whatever it ends up being called) and it has
clearly defined rules then this "trick" only works if it conforms to the
rules of the new feature. If it doesn't then you have to simply resolve
to include guards for such preprocessor voodoo. But at that point you as
the author of the project know whether you can use this feature or not
because it has (hopefully) clearly specified behavior and as such you
should then know whether or not you can setup your files the way described.
>
> I think a proposal similar to the previously mentioned "#once
> guard_phrase" has more potential. It does give the user a way to
> indicate file equivalence (through the guard name) and also is
> arguably less expensive than computing a hash value.
>
It unfortunately doesn't solve the problem of having to come up wth a
guard that is unique within the multiverse. For example boost_date_time
has an include guard called ISO_FORMAT_HPP___. That's not exactly the
most unique guard in the world. The less this relies on human
(non-)creativity or discipline the better.

Like many modern C++ improvements it's trying to create a sensible
default. If that default doesn't work for (hopefully rare) use cases
then you can always revert to the previous method.

Thiago Macieira

unread,
Oct 27, 2016, 10:43:10 PM10/27/16
to std-pr...@isocpp.org
Em quinta-feira, 27 de outubro de 2016, às 23:39:04 PDT, Miro Knejp escreveu:
> > Let me echo Nicol's arguments here. The solutions are not symmetric.
> > There's a transfer of time-of-failure and of responsibility between the
> > two solutions.
> >
> > With include guards, the responsibility of ensuring the guard is correct
> > and unique lies with the author of the header in question. Any problems
> > that happen, will (to a 99% approximation) happen while developing that
> > header in the first place. So it's highly unlikely that the author
> > developer will ship such a broken header to other people.
>
> I can't really follow the logic here. Nobody is going to intenionally
> create headers with conflicting guards in the same library. But that
> doens't guarantee uniqueness at all. I can't possibly know whether my
> library guard will or will not conflict with the guard of some other
> library that happens to also have the same file name. In that scenario
> the user is faced with the same problem you describe later.

The scenario is like this: I'm the developer of application A that uses
libraries L1 and L2. Those libraries are not shipped with my sources, you have
to download and install them yourself.

When I'm developing my application, I will notice if there's an include guard
clash (for example, if both libraries have a config.h file and that uses
CONFIG_H as the guard). If I do detect that, I will most likely open a bug
report to either L1 or L2 or both, and if needed I may add a workaround to my
application sources. When you get my sources and install L1 and L2, you will
be able to compile because I've already fixed the issue.

But if L1 and L2 used #pragma once, it's possible that everything works for me
because of the way that I installed things. But then you download my sources
and that of L1 and L2. Given the way that you placed them, the compiler
concludes that L1/config.h L2/config.h are the same, so it will fail to include
one of them, causing a compilation error.

The transfer of responsibility is that what I had to detect and correct is now
your responsibility and that of the compiler author who didn't realise your
corner case was possible. The change of time-of-failure is that it instead
happened when you-the-user compiles, not when I-the-developer developed the
code.

Viacheslav Usov

unread,
Oct 28, 2016, 5:04:57 AM10/28/16
to ISO C++ Standard - Future Proposals
On Thu, Oct 27, 2016 at 11:02 PM, Thiago Macieira <thi...@macieira.org> wrote:

> The #ifndef/#define problem is highly unlikely ever since Copy & Paste was invented.

People are strange creatures. Sometimes they will type the same thing twice even if copy & paste is available. Sometimes they will make a mistake while doing that. I have seen that happen.

Cheers,
V.

Andrey Semashev

unread,
Oct 28, 2016, 5:50:41 AM10/28/16
to std-pr...@isocpp.org
On 10/28/16 02:48, Miro Knejp wrote:
> Am 28.10.2016 um 01:06 schrieb Andrey Semashev:
>> On Fri, Oct 28, 2016 at 1:17 AM, Nevin Liber <ne...@eviloverlord.com>
>> wrote:
>>> 3. The OS doesn't know if the files have the same content. I'm
>>> pretty sure
>>> this would require an extra pass to compute the fingerprint hash, as you
>>> shouldn't be processing the file until you are sure it is unique.
>>> You still
>>> have to scan for include guards as that is just normal macro processing.
>> As I shown earlier, basing the decision on the hash can result in
>> incorrect result. IMO, content equivalence is not the right criteria
>> for skipping file inclusion.
>
> Your example doesn't work precisely *because* the behavior of #pragma
> once isn't specified. Anyone can come up with a contrived example that
> breaks a feature which hasn't been defined yet, implying the feature
> must be impossible because it cannot solve an artificial use case it
> probably isn't even designed to solve.

That example breaks assuming #once relies on hashing the header, which
is what you suggested above.

> If such a utility is added (whatever it ends up being called) and it has
> clearly defined rules then this "trick" only works if it conforms to the
> rules of the new feature. If it doesn't then you have to simply resolve
> to include guards for such preprocessor voodoo. But at that point you as
> the author of the project know whether you can use this feature or not
> because it has (hopefully) clearly specified behavior and as such you
> should then know whether or not you can setup your files the way described.
>>
>> I think a proposal similar to the previously mentioned "#once
>> guard_phrase" has more potential. It does give the user a way to
>> indicate file equivalence (through the guard name) and also is
>> arguably less expensive than computing a hash value.
>>
> It unfortunately doesn't solve the problem of having to come up wth a
> guard that is unique within the multiverse. For example boost_date_time
> has an include guard called ISO_FORMAT_HPP___. That's not exactly the
> most unique guard in the world. The less this relies on human
> (non-)creativity or discipline the better.

The important benefit of that approach is that the user is still in
control to solve the issue - he can simply modify the guard to be more
unique. You could argue that the user could modify any part of the
header to change its hash, and that's true. But as a user of the
library, I would be much more confident modifying the guard - the
element that is precisely aimed at solving the problem - than any
unrelated part of the header, even a comment.

> Like many modern C++ improvements it's trying to create a sensible
> default. If that default doesn't work for (hopefully rare) use cases
> then you can always revert to the previous method.

The problem with the "if it doesn't work for you, use the traditional
include guards" argument is that it doesn't work for library developers
because they generally don't know how their library will be used and
whether #once can fail in those conditions. If #once doesn't offer the
same degree of portability as include guards, the latter are always
preferable and there simply is no reason to use #once.

Miro Knejp

unread,
Oct 28, 2016, 6:53:34 AM10/28/16
to std-pr...@isocpp.org
On 28 Oct 2016, at 11:50 , Andrey Semashev <andrey....@gmail.com> wrote:

On 10/28/16 02:48, Miro Knejp wrote:
Am 28.10.2016 um 01:06 schrieb Andrey Semashev:
On Fri, Oct 28, 2016 at 1:17 AM, Nevin Liber <ne...@eviloverlord.com>
wrote:
3. The OS doesn't know if the files have the same content.  I'm
pretty sure
this would require an extra pass to compute the fingerprint hash, as you
shouldn't be processing the file until you are sure it is unique.
You still
have to scan for include guards as that is just normal macro processing.
As I shown earlier, basing the decision on the hash can result in
incorrect result. IMO, content equivalence is not the right criteria
for skipping file inclusion.
>
Your example doesn't work precisely *because* the behavior of #pragma
once isn't specified. Anyone can come up with a contrived example that
breaks a feature which hasn't been defined yet, implying the feature
must be impossible because it cannot solve an artificial use case it
probably isn't even designed to solve.

That example breaks assuming #once relies on hashing the header, which is what you suggested above.
That really depends on how your example came into existence. The way I understood is that “a" and “b” are both part of the same project and therefore the project author is in control and responsible for how the files are organized and therefore wouldn’t use #once in the first place. Otherwise if “a” and “b” are external libraries why would someone symlink two headers of two libraries into one? That just seems like a really bad idea to me.

Everyone can come up with infinite examples where every new proposed feature breaks. The question is always: does it aim at solving them all? I say no.


If such a utility is added (whatever it ends up being called) and it has
clearly defined rules then this "trick" only works if it conforms to the
rules of the new feature. If it doesn't then you have to simply resolve
to include guards for such preprocessor voodoo. But at that point you as
the author of the project know whether you can use this feature or not
because it has (hopefully) clearly specified behavior and as such you
should then know whether or not you can setup your files the way described.

I think a proposal similar to the previously mentioned "#once
guard_phrase" has more potential. It does give the user a way to
indicate file equivalence (through the guard name) and also is
arguably less expensive than computing a hash value.

It unfortunately doesn't solve the problem of having to come up wth a
guard that is unique within the multiverse. For example boost_date_time
has an include guard called ISO_FORMAT_HPP___. That's not exactly the
most unique guard in the world. The less this relies on human
(non-)creativity or discipline the better.

The important benefit of that approach is that the user is still in control to solve the issue - he can simply modify the guard to be more unique. You could argue that the user could modify any part of the header to change its hash, and that's true. But as a user of the library, I would be much more confident modifying the guard - the element that is precisely aimed at solving the problem - than any unrelated part of the header, even a comment.

Like many modern C++ improvements it's trying to create a sensible
default. If that default doesn't work for (hopefully rare) use cases
then you can always revert to the previous method.

The problem with the "if it doesn't work for you, use the traditional include guards" argument is that it doesn't work for library developers because they generally don't know how their library will be used and whether #once can fail in those conditions. If #once doesn't offer the same degree of portability as include guards, the latter are always preferable and there simply is no reason to use #once.
If I publish a library I can at least expect that users don’t symlink one of my headers into the header of a different library just because they can. Unless this is an exclusively announced and agreed on  compatibility feature with the another library author in which case I wouldn’t use #once for the file.

Even though I do not know how someone is going to use my library I can expect a certain amount of common sense. If they *do* break their code because of symlink shenanigans between libraries then, to be frank, that’s *their* problem and I’m not gonna lift a finger to fix it for them.

Viacheslav Usov

unread,
Oct 28, 2016, 6:58:40 AM10/28/16
to ISO C++ Standard - Future Proposals
We're talking about #pragma once and include guards because #include, well, includes. Its core function is extremely simple: just full textual inclusion.

But (arguably) most of the time it is used to implement import of definitions. So perhaps what we need is another processor directive, say #import, that includes exactly once.

Now, much has been said about the difficulty of achieving this "exactly once", but that difficulty has been discussed in the context of #include. Perhaps we could define #import so as to lessen the difficulty?

I know that #import looks like the import keyword of the modules proposal, and this is not entirely unintentional. As mentioned by others, modules are the way to go, but we do not know when we get them.

Cheers,
V.

Miro Knejp

unread,
Oct 28, 2016, 7:13:07 AM10/28/16
to std-pr...@isocpp.org
For what it’s worth Objective-C(++) already has #import doing exactly that and people don’t seem to complain about it.

Viacheslav Usov

unread,
Oct 28, 2016, 8:14:16 AM10/28/16
to ISO C++ Standard - Future Proposals
On Fri, Oct 28, 2016 at 1:13 PM, Miro Knejp <miro....@gmail.com> wrote:

> For what it’s worth Objective-C(++) already has #import doing exactly that and people don’t seem to complain about it.

Here is what GCC says about it [1]:

CPP supports a variant of ‘#include’ called ‘#import’ which includes a file, but does so at most once. If you use ‘#import’ instead of ‘#include’, then you don't need the conditionals inside the header file to prevent multiple inclusion of the contents. ‘#import’ is standard in Objective-C, but is considered a deprecated extension in C and C++.

‘#import’ is not a well designed feature. It requires the users of a header file to know that it should only be included once. It is much better for the header file's implementor to write the file so that users don't need to know this. Using a wrapper ‘#ifndef’ accomplishes this goal.

In the present implementation, a single use of ‘#import’ will prevent the file from ever being read again, by either ‘#import’ or ‘#include’. You should not rely on this; do not use both ‘#import’ and ‘#include’ to refer to the same header file.

(end)

What "once" really means, for example, with respect to hard/symbolic links, is not explained there. That is also true of #pragma once, also mentioned on the same page. If that was documented there (or elsewhere), it could be a good starting point to "standardise existing practice".

Cheers,
V.

Andrey Semashev

unread,
Oct 28, 2016, 9:22:14 AM10/28/16
to std-pr...@isocpp.org
On 10/28/16 13:53, Miro Knejp wrote:
>
>> On 28 Oct 2016, at 11:50 , Andrey Semashev <andrey....@gmail.com
>> <mailto:andrey....@gmail.com>> wrote:
>>
>> The problem with the "if it doesn't work for you, use the traditional
>> include guards" argument is that it doesn't work for library
>> developers because they generally don't know how their library will be
>> used and whether #once can fail in those conditions. If #once doesn't
>> offer the same degree of portability as include guards, the latter are
>> always preferable and there simply is no reason to use #once.
>
> If I publish a library I can at least expect that users don’t symlink
> one of my headers into the header of a different library just because
> they can. Unless this is an exclusively announced and agreed on
> compatibility feature with the another library author in which case I
> wouldn’t use #once for the file.

The user doesn't have to symlink the particular header, symlinking the
containing directory is enough. And this is a rather common practice in
order to maintain headers of different versions of the same library on
the system (with one default version indicated by the symlink). My
earlier example with a and b could easily demonstrate that, too.

Matthew Woehlke

unread,
Oct 28, 2016, 10:11:48 AM10/28/16
to std-pr...@isocpp.org
On 2016-10-27 17:02, Thiago Macieira wrote:
> So, yes, there is a benefit of #pragma once with a guard symbol that you define,
> but it's very minimal because it doesn't solve the real problem that guard
> macros have, namely that of clashes between libraries. It only saves you some
> typing.

This is why, in my proposal, the symbol is a *qualified* name. The idea
is to make it obvious that you should, well, *qualify* the guard. That
way, guard collisions should be about as likely as class name collisions.

(Also, my proposal gives the compiler a chance to yell at you if you
copy the guard between headers and forget to change it, which IME is
*the* most common mistake. I don't know that I've ever actually seen two
headers from different packages using the same guard.)

--
Matthew

Matthew Woehlke

unread,
Oct 28, 2016, 10:19:14 AM10/28/16
to std-pr...@isocpp.org
On 2016-10-27 19:06, Andrey Semashev wrote:
> I think a proposal similar to the previously mentioned "#once
> guard_phrase" has more potential. It does give the user a way to
> indicate file equivalence (through the guard name) and also is
> arguably less expensive than computing a hash value.

Perhaps more importantly, it doesn't require parsing the file in two
passes. Since it is ill-formed for `#once` to appear after meaningful
content, if the compiler is in doubt it can just process the file
normally until `#once`, and then (potentially) stop.

--
Matthew

Matthew Woehlke

unread,
Oct 28, 2016, 10:36:42 AM10/28/16
to std-pr...@isocpp.org
On 2016-10-27 19:48, Miro Knejp wrote:
> It unfortunately doesn't solve the problem of having to come up wth a
> guard that is unique within the multiverse. For example boost_date_time
> has an include guard called ISO_FORMAT_HPP___. That's not exactly the
> most unique guard in the world. The less this relies on human
> (non-)creativity or discipline the better.

Um...

#once boost::date_time::iso_format

QED.

I also suggested that compilers *might* be able to go as far as warning
if the namespace of the guard "looks suspicious". It *will* squawk at
you if you get the `iso_format` part wrong.

This requires no more creativity than class naming, and as mentioned in
another message, if you got that wrong, you have bigger name-collision
problems than just the #once guard.

The additional creative burden is therefore near zero, and the
additional discipline burden is only trivially higher than using
`#pragma once`. In both cases, you have to remember to add *some* guard,
and the compiler will keep you honest about using the *right* guard, at
least for the unqualified part.

(Huh. And I didn't even consider the possibility of the guard being
something like `package::__BASENAME__`...)

--
Matthew

Miro Knejp

unread,
Oct 28, 2016, 11:02:51 AM10/28/16
to std-pr...@isocpp.org

> On 28 Oct 2016, at 16:36 , Matthew Woehlke <mwoehlk...@gmail.com> wrote:
>
> On 2016-10-27 19:48, Miro Knejp wrote:
>> It unfortunately doesn't solve the problem of having to come up wth a
>> guard that is unique within the multiverse. For example boost_date_time
>> has an include guard called ISO_FORMAT_HPP___. That's not exactly the
>> most unique guard in the world. The less this relies on human
>> (non-)creativity or discipline the better.
>
> Um...
>
> #once boost::date_time::iso_format
>
> QED.
>
> I also suggested that compilers *might* be able to go as far as warning
> if the namespace of the guard "looks suspicious". It *will* squawk at
> you if you get the `iso_format` part wrong.
If you can specify a behavior that is consistent and won’t annoy anyone, sure. But we all know what happens when compilers start issuing warnings developers think are annoying…
The author of boost_date_time obviously didn’t consider it necessary to prefix the guard with BOOST_whatever so who knows what the associated #once guard would have looked like.

Furthermore if you have to specify an identifier that the compiler should somehow associate with the file and its content then that means the compiler actually has to fully parse and analyze the content instead of just preprocessing the header guard with some heuristics.

Miro Knejp

unread,
Oct 28, 2016, 11:07:46 AM10/28/16
to std-pr...@isocpp.org
So you risk ending up using two versions of the same library in one executable? Yeah, right. That’s not going to cause problems at all. I’m sorry but if one file includes "lib/1.1/lib.h" and another “lib/latest/lib.h” you really aren’t far away from shooting yourself in the foot.

Thiago Macieira

unread,
Oct 28, 2016, 11:47:46 AM10/28/16
to std-pr...@isocpp.org
Em sexta-feira, 28 de outubro de 2016, às 11:04:53 PDT, Viacheslav Usov
escreveu:
Oh, no doubt. But I didn't say the problem was non-existent. I said it was
unlikely because of Copy & Paste and because the developer who made the
mistake will most likely find out about it anyway.

Matthew Woehlke

unread,
Oct 28, 2016, 11:51:07 AM10/28/16
to std-pr...@isocpp.org
On 2016-10-28 11:07, Miro Knejp wrote:
> So you risk ending up using two versions of the same library in one
> executable? Yeah, right. That’s not going to cause problems at all.
> I’m sorry but if one file includes "lib/1.1/lib.h" and another
> “lib/latest/lib.h” you really aren’t far away from shooting yourself
> in the foot.

`#pragma once` will "stop" this by giving you strange compile failures
from including the "same" header twice. Traditional guards may or may
not stop this; the guard will silently prevent inclusion of the second
version of the header, which may or may not cause strange breakage at
compile time or run time.

My proposed `#once` will stop this because the second copy of the header
will have a different version, and you will get a nice, easily
understandable error message that you tried to include two different
versions of a header with the same guard.

(Arf. But this means the compiler needs to "ignore" #once to the extent
of recursively validating the `#once` in `#include`d headers; at least
for the version validation to work as I'd intended. Will have to update
the proposal to mention that.)

--
Matthew

Thiago Macieira

unread,
Oct 28, 2016, 11:51:47 AM10/28/16
to std-pr...@isocpp.org
Em sexta-feira, 28 de outubro de 2016, às 17:07:42 PDT, Miro Knejp escreveu:
> So you risk ending up using two versions of the same library in one
> executable? Yeah, right. That’s not going to cause problems at all. I’m
> sorry but if one file includes "lib/1.1/lib.h" and another
> “lib/latest/lib.h” you really aren’t far away from shooting yourself in the
> foot.

It can happen if the buildsystem does realname() and inserts the necessary -I
switches.

Moreover, remember that the problem happens at the time of the compilation of
the application on the user's machine, not on the application developer's
machine. You don't know what crazy things the user may do.

Andrey Semashev

unread,
Oct 28, 2016, 11:54:30 AM10/28/16
to std-pr...@isocpp.org
I think #once shouldn't be required to appear before any (meaningful?)
content. The common practice is to include a config header that detects
the features supported by compiler (that would include support for
#once) and then do any other stuff. For instance, in Boost "#pragma
once" is often used like this:

#include <boost/config.hpp>

#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif

If the compiler is not able to reliably prove that the header has
already been included, it would have to parse the header again. The
optimization you mention (only parse until #once) is still possible,
although less beneficial. I think, that is a fair trade for improved
usability.

Andrey Semashev

unread,
Oct 28, 2016, 12:02:22 PM10/28/16
to std-pr...@isocpp.org
That depends on what you're trying to do. It may be very reasonable to
want to use different versions of the same library in the project (like,
for compatibility between different libraries your application happens
to use). Not that I would like to find myself in such situation, but I
can see it happen in practice.

Matthew Woehlke

unread,
Oct 28, 2016, 12:05:55 PM10/28/16
to std-pr...@isocpp.org
On 2016-10-28 11:02, Miro Knejp wrote:
> On 28 Oct 2016, at 16:36 , Matthew Woehlke wrote:
>> On 2016-10-27 19:48, Miro Knejp wrote:
>>> It unfortunately doesn't solve the problem of having to come up wth a
>>> guard that is unique within the multiverse. For example boost_date_time
>>> has an include guard called ISO_FORMAT_HPP___. That's not exactly the
>>> most unique guard in the world. The less this relies on human
>>> (non-)creativity or discipline the better.
>>
>> Um...
>>
>> #once boost::date_time::iso_format
>>
>> QED.
>>
>> I also suggested that compilers *might* be able to go as far as warning
>> if the namespace of the guard "looks suspicious". It *will* squawk at
>> you if you get the `iso_format` part wrong.
>
> If you can specify a behavior that is consistent and won’t annoy
> anyone, sure.

The unqualified name shall match the basename of the included file.
(There are some corner cases, but since this is a non-mandated
diagnostic, we can figure them out as they come. If the include is via a
pipe or something strange, this check would likely be skipped, which is
somewhat unfortunate, but no worse than the current situation.)

> The author of boost_date_time obviously didn’t consider it necessary
> to prefix the guard with BOOST_whatever so who knows what the
> associated #once guard would have looked like.

I believe much of why traditional guards are ugly and inconsistent is...
because they're preprocessor symbols. Programmers have ingrained notions
about how those should look.

#once is explicitly *not* a preprocessor symbol; it is a qualified C++
identifier (albeit one that exists in a namespace that is unrelated to
any actual C++ code). By changing the paradigm, the hope is that people
would get in the habit of using proper names, the same as for e.g. class
names.

Besides, it would be easy to warn if you didn't use *any* namespace,
which would at least discourage simple laziness / ignorance.

And, really, this doesn't need to be hard. The obvious intent is that
the guard should have a namespace that is consistent with the namespace
of the stuff declared in the header. Or, if you don't namespace your
declarations, at least have a namespace that is e.g. the name of your
library or project.

> Furthermore if you have to specify an identifier that the compiler
> should somehow associate with the file and its content then that
> means the compiler actually has to fully parse and analyze the
> content instead of just preprocessing the header guard with some
> heuristics.

This is QoI and not required by the proposal. Also, the intent is that
the warning would be generated *after* the file is parsed (i.e. at EOF).

Trying to guess if you used an appropriate namespace is nice, but if we
can't have it, it's not a terrible loss. After all, *any* level of
checking is better than we have today.

--
Matthew

Matthew Woehlke

unread,
Oct 28, 2016, 12:14:26 PM10/28/16
to std-pr...@isocpp.org
On 2016-10-28 11:54, Andrey Semashev wrote:
> On 10/28/16 17:19, Matthew Woehlke wrote:
>> On 2016-10-27 19:06, Andrey Semashev wrote:
>>> I think a proposal similar to the previously mentioned "#once
>>> guard_phrase" has more potential. It does give the user a way to
>>> indicate file equivalence (through the guard name) and also is
>>> arguably less expensive than computing a hash value.
>>
>> Perhaps more importantly, it doesn't require parsing the file in two
>> passes. Since it is ill-formed for `#once` to appear after meaningful
>> content, if the compiler is in doubt it can just process the file
>> normally until `#once`, and then (potentially) stop.
>
> I think #once shouldn't be required to appear before any (meaningful?)
> content. The common practice is to include a config header that detects
> the features supported by compiler (that would include support for
> #once) and then do any other stuff. For instance, in Boost "#pragma
> once" is often used like this:
>
> #include <boost/config.hpp>
>
> #if defined(BOOST_HAS_PRAGMA_ONCE)
> #pragma once
> #endif

You're *partly* right. Really, it *should* not follow any content that
could potentially change the meaning of the program depending on whether
the header is processed more than once. In your example, assuming that
<boost/config.hpp> is also guarded, you'd be okay.

The trouble is you lose the optimization chance to not read the file at
all the second time. However, it is plausible that compilers could
detect if a second inclusion, even in a case like above, would be a
no-op as a matter of QoI.

Since this would also allow a better fix for ensuring that version
checks happen (when only one header has a version check, e.g. the above
<boost/config.hpp>), I think I am convinced :-). Thanks!

(Of course, this means any content before `#once` is parsed twice...
which may even be a good thing in some corner cases! Anyway, we still an
extra parsing pass to compute a hash.)

--
Matthew

Matthew Woehlke

unread,
Oct 28, 2016, 12:16:53 PM10/28/16
to std-pr...@isocpp.org
On 2016-10-28 12:02, Andrey Semashev wrote:
>> So you risk ending up using two versions of the same library in one
>> executable? Yeah, right. That’s not going to cause problems at all.
>> I’m sorry but if one file includes "lib/1.1/lib.h" and another
>> “lib/latest/lib.h” you really aren’t far away from shooting yourself
>> in the foot.
>
> That depends on what you're trying to do. It may be very reasonable to
> want to use different versions of the same library in the project (like,
> for compatibility between different libraries your application happens
> to use). Not that I would like to find myself in such situation, but I
> can see it happen in practice.

Yes, but hopefully not in the same TU. If you are trying to include
different versions of the "same" header in the same TU, that's likely
going to end badly unless the library has specific provisions to allow
it. (And if it does, it probably includes using versioned include guards.)

--
Matthew

inkwizyt...@gmail.com

unread,
Oct 28, 2016, 1:33:17 PM10/28/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com

Will this discussion become irrelevant when this line become standard?
import std.vector;
I except this could reduce by order of magnitude number of times you need write new include guards.

 

Matthew Woehlke

unread,
Oct 28, 2016, 1:36:07 PM10/28/16
to std-pr...@isocpp.org
On 2016-10-28 13:33, inkwizyt...@gmail.com wrote:
> Will this discussion become irrelevant when this line become standard?
> import std.vector;
> I except this could reduce by order of magnitude number of times you need
> write new include guards.

Ahem:

Won't modules make this irrelevant?
-----------------------------------

It is possible that modules will significantly reduce the need for
this feature, but modules aren't here yet, and it is likely that we
will continue to have traditional headers for a long time. Since this
feature happens entirely at the preprocessor level, it is our sincere
hope that compilers will choose to implement the feature early, and
enable it regardless of the language level requested. This means that
existing software may be able to take advantage of the feature much
sooner than such software can be ported to modules (which will
involve a much more invasive change).

--
Matthew

Miro Knejp

unread,
Oct 28, 2016, 2:55:49 PM10/28/16
to std-pr...@isocpp.org
Am 28.10.2016 um 17:51 schrieb Thiago Macieira:
> Em sexta-feira, 28 de outubro de 2016, às 17:07:42 PDT, Miro Knejp escreveu:
>> So you risk ending up using two versions of the same library in one
>> executable? Yeah, right. That’s not going to cause problems at all. I’m
>> sorry but if one file includes "lib/1.1/lib.h" and another
>> “lib/latest/lib.h” you really aren’t far away from shooting yourself in the
>> foot.
> It can happen if the buildsystem does realname() and inserts the necessary -I
> switches.
>
> Moreover, remember that the problem happens at the time of the compilation of
> the application on the user's machine, not on the application developer's
> machine. You don't know what crazy things the user may do.
>
Honestly there is only a limited set of "crazy things" I'm willing to
prepare for when writing a library. Just because people can do some
things doesn't mean they should. There are limits to everything.

inkwizyt...@gmail.com

unread,
Oct 28, 2016, 3:20:36 PM10/28/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com


From that approach discussing pragma made more sens. However from your working paper you mentioned that writing new guards is biggest problem.
This mean that current software not need much pragma because they have already working guards, only when it's rapidity growing we have problems.
However if this they case then using modules is more appropriate because you already changing/adding lot of code.
Another thing is that both modules and pragma (in your version) need new compilers and is possible that both will be include in standard in same time frame (around C++20).
Right now only GCC from three major compilers do not have module support for c++ and this is very possible it will change in this 4 years.

Bo Persson

unread,
Oct 29, 2016, 7:08:50 AM10/29/16
to std-pr...@isocpp.org
On 2016-10-28 18:05, Matthew Woehlke wrote:
> On 2016-10-28 11:02, Miro Knejp wrote:
>> On 28 Oct 2016, at 16:36 , Matthew Woehlke wrote:
>>> On 2016-10-27 19:48, Miro Knejp wrote:
>>>> It unfortunately doesn't solve the problem of having to come up wth a
>>>> guard that is unique within the multiverse. For example boost_date_time
>>>> has an include guard called ISO_FORMAT_HPP___. That's not exactly the
>>>> most unique guard in the world. The less this relies on human
>>>> (non-)creativity or discipline the better.
>>>
>>> Um...
>>>
>>> #once boost::date_time::iso_format
>>>
>>> QED.
>>>
>>> I also suggested that compilers *might* be able to go as far as warning
>>> if the namespace of the guard "looks suspicious". It *will* squawk at
>>> you if you get the `iso_format` part wrong.
>>
>> If you can specify a behavior that is consistent and won’t annoy
>> anyone, sure.
>
> The unqualified name shall match the basename of the included file.
> (There are some corner cases, but since this is a non-mandated
> diagnostic, we can figure them out as they come. If the include is via a
> pipe or something strange, this check would likely be skipped, which is
> somewhat unfortunate, but no worse than the current situation.)
>

Some of the corner cases are that the mapping from the physical source
file to the source character set, as well as the mapping from
#include-names to file names, are both implementation-defined. One or
more of those could be EBCDIC, for example.

Also, the base name "iso_format" might no be allowed as a file name
because of OS restrictions (length and/or allowed characters).


Bo Persson


Thiago Macieira

unread,
Oct 29, 2016, 12:24:59 PM10/29/16
to std-pr...@isocpp.org
On sexta-feira, 28 de outubro de 2016 20:57:33 PDT Miro Knejp wrote:
> > Moreover, remember that the problem happens at the time of the compilation
> > of the application on the user's machine, not on the application
> > developer's machine. You don't know what crazy things the user may do.
>
> Honestly there is only a limited set of "crazy things" I'm willing to
> prepare for when writing a library. Just because people can do some
> things doesn't mean they should. There are limits to everything.

True. We often have that problem with include guards anyway: if an older copy
of the header in question is found in a standard include path for which no -I
is necessary (e.g., /usr/include), most complex libraries fail to build.

Still, the question is that of debugging the issue, the kinds of failure modes
that are possible, and when they happen.

Nevin Liber

unread,
Oct 29, 2016, 2:09:00 PM10/29/16
to std-pr...@isocpp.org
On Thu, Oct 27, 2016 at 6:06 PM, Andrey Semashev <andrey....@gmail.com> wrote:
On Fri, Oct 28, 2016 at 1:17 AM, Nevin Liber <ne...@eviloverlord.com> wrote:
>
> There are three possibilities:
>
> 1. It is the same file according to the OS (e.g.: inode and device match).
> 2. It must have different contents according to the OS (e.g. the size doesn't
> match).

I think, the above two bullets cannot be implemented reliably. Event
the size may not be available if the source is read from a pipe.

Honestly, I don't particularly care about insane development environments.  I also don't know of any requirement that compilers must be single pass.  And even if so, caching should be able to address it.  Do you have a real world situation involving #pragma once and pipes instead of regular files?
 
I think a proposal similar to the previously mentioned "#once
guard_phrase" has more potential.

Given that people use #pragma once so that they *don't* have to come up with a unique guard name, I don't see the minor benefit of not having to copy the macro name one line later or a slightly nicer syntax than macros as being worth it, as people will *still* continue to use #pragma once.

And because of modules, I'm not even sure pursing a #once solution (without specifying an identifier) is worth it.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Andrey Semashev

unread,
Oct 29, 2016, 2:50:37 PM10/29/16
to std-pr...@isocpp.org
On 10/29/16 21:08, Nevin Liber wrote:
> On Thu, Oct 27, 2016 at 6:06 PM, Andrey Semashev
> <andrey....@gmail.com <mailto:andrey....@gmail.com>> wrote:
>
> On Fri, Oct 28, 2016 at 1:17 AM, Nevin Liber <ne...@eviloverlord.com
> <mailto:ne...@eviloverlord.com>> wrote:
> >
> > There are three possibilities:
> >
> > 1. It is the same file according to the OS (e.g.: inode and device
> match).
> > 2. It must have different contents according to the OS (e.g. the
> size doesn't
> > match).
>
> I think, the above two bullets cannot be implemented reliably. Event
> the size may not be available if the source is read from a pipe.
>
>
> Honestly, I don't particularly care about insane development
> environments.

You should, if you plan to make a proposal.

> I also don't know of any requirement that compilers must
> be single pass. And even if so, caching should be able to address it.
> Do you have a real world situation involving #pragma once and pipes
> instead of regular files?

I don't. But compilers do support reading input from a pipe, and nothing
prevents #once to appear in that input. Compiler vendors have to
implement #once in those circumstances as well, with behavior equivalent
to the proposal.

> And because of modules, I'm not even sure pursing a #once solution
> (without specifying an identifier) is worth it.

I won't argue with this.

Nevin ":-)" Liber <Nevin :-) Liber >

unread,
Oct 29, 2016, 3:36:02 PM10/29/16
to std-pr...@isocpp.org
On Oct 29, 2016, at 1:50 PM, Andrey Semashev <andrey....@gmail.com> wrote:
>
>> On 10/29/16 21:08, Nevin Liber wrote:
>> On Thu, Oct 27, 2016 at 6:06 PM, Andrey Semashev
>> <andrey....@gmail.com <mailto:andrey....@gmail.com>> wrote:
>>
>> On Fri, Oct 28, 2016 at 1:17 AM, Nevin Liber <ne...@eviloverlord.com
>> <mailto:ne...@eviloverlord.com>> wrote:
>> >
>> > There are three possibilities:
>> >
>> > 1. It is the same file according to the OS (e.g.: inode and device
>> match).
>> > 2. It must have different contents according to the OS (e.g. the
>> size doesn't
>> > match).
>>
>> I think, the above two bullets cannot be implemented reliably. Event
>> the size may not be available if the source is read from a pipe.

What makes you believe that include guards work correctly with files that are pipes?

Thiago Macieira

unread,
Oct 29, 2016, 4:04:48 PM10/29/16
to std-pr...@isocpp.org
On sábado, 29 de outubro de 2016 14:35:56 PDT wrote:
> What makes you believe that include guards work correctly with files that
> are pipes?

So long as the writing end uses the same guard twice, it has to work.

Andrey Semashev

unread,
Oct 29, 2016, 4:11:13 PM10/29/16
to std-pr...@isocpp.org
Why wouldn't they? They can be processed normally with preprocessor.

Matthew Woehlke

unread,
Oct 31, 2016, 9:34:17 AM10/31/16
to std-pr...@isocpp.org
On 2016-10-26 11:28, Hans Guijt wrote:
> On Wednesday, October 26, 2016 at 4:52:45 PM UTC+2, Matthew Woehlke wrote:
>> Please do your due diligence before making claims like this, or, for
>> that matter, asking for "obvious" features. I can assure you, this is
>> not the first time someone has asked to standardize #pragma once.
>
> So you are saying this is a common request from the community, which is not
> being answered by the standards committee because ...?

...for the reasons I listed. (Which BTW are from memory. I apologize for
any fudging of the reasons, but *you* are the one trying to propose the
feature; I expect you to do the work identifying and addressing the
reasons why this exact request has historically been rejected.)

>> What should happen if you include the same header (same inode) via
>> different names? What about if the compiler sees two identical include
>> directives (same path, same <> vs. "") that resolve to different files
>> with different contents? What about platforms with esoteric file
>> systems, or *no* file system?
>
> This is an attempt at complication of a painfully simple feature.

No. These are questions that *need to be answered*.

> You don't want it, so you just ask meaningless questions that have
> trivial answers in the hope of convincing others it is all incredibly
> complicated, while it really isn't.

I am probably neutral. I am not personally opposed to it (other than my
awareness that it has problems), but I don't feel a particular need for
it, either. Rather, I am trying to point out the reasons *given by
others* that have previously caused this proposal to fail.

What I see here is that you a) haven't done your homework, b) refuse to
do your homework when others point this out, and c) resort to ad hominem
attacks on said others instead.

This strikes me as a good way to get people to ignore you entirely.

> I don't see a problem with including a file twice if a symlink
> exists; after all, you could just copy it and include it twice that
> way as well.

<irony>If you don't see a problem including a header twice, then
obviously we don't need #pragma once.</irony>

(Also see example below.)

> I have some trouble imagining how the same header
> name could resolve to two different files during the same compilation.

// foo/foo.cpp
#include <cow.h> // /usr/include/cow/cow.h
#include <pig.h> // /usr/include/pork/pig.h
#include <bar/bar.h>
#include "config.h" // foo/config.h

// bar/bar.h
#include "thirdparty/cow.h" // bar/thirdparty/cow/include/cow.h
#include <hog.h> // -> /usr/include/pork/pig.h
#include "config.h" // bar/config.h

// foo/config.h
...

// bar/config.h
...totally different content from foo/config.h...

I've shown three corner cases here:

- Different files, different include paths; content may or may not be
the same
- Different files, same include paths, different content
- Same files, different include paths

The first two are not especially contrived, and this is not necessarily
an exhaustive list. It is well specified how traditional include guards
will handle these cases. What about #pragma once?

Hand-waving is not a sufficient answer.

> The whole thing is completely trivial

It isn't. Please *do your homework* (and don't expect us to do it for
you), identify the issues that have previously prevented this from being
standardized, and come back when you have solutions to those problems.
Otherwise you are just wasting everyone's time rehashing an old proposal
that has already been rejected.

--
Matthew

Peter Koch Larsen

unread,
Oct 31, 2016, 9:53:39 AM10/31/16
to std-pr...@isocpp.org
To me, this is quite simple and not that complicated. The facts are that

1) include guards are well-known and rather simple to make to work.
Even the rare cases with clashes (which should not occur if you spend
just a little time creating your guard-name) can normally be solved
reasonably easy.
2) It is non-trivial to make #pragma once work unless you resort to
hashing and other time consuming algorithms.
3) For many of us, #pragma once works well due to our environment.

So my proposal would be to not standardise #pragma once. Those who use
it can continue to do so and hope it will continue to work. The ones
with the really complicated environments with many libraries of which
they have no control should continue to use the old include guards,
living with the fact that they will have to spend in the order of one
minute more for each new header-file they create. Asking for a
standardisation of #pragma once is to ask the standard committee to
spend a lot of time on work with little benefit to anyone.

/Peter
> --
> You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.
> To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/nuqlkr%24sg7%241%40blaine.gmane.org.

Ville Voutilainen

unread,
Oct 31, 2016, 10:01:30 AM10/31/16
to ISO C++ Standard - Future Proposals
On 31 October 2016 at 15:53, Peter Koch Larsen
<peter.ko...@gmail.com> wrote:
> minute more for each new header-file they create. Asking for a
> standardisation of #pragma once is to ask the standard committee to
> spend a lot of time on work with little benefit to anyone.


Hallelujah, amen.

kiru...@gmail.com

unread,
Nov 30, 2017, 9:33:24 AM11/30/17
to ISO C++ Standard - Future Proposals, sergeik...@gmail.com
+1 : #pragma once should be standardized.
When working on projects with many classes, it's common to copy paste a module (header/cpp) and to forget modifying the include guard.
Then you have strange errors because the same include guard is defined by different files.
Pragra once doesn't have this problem.

Bo Persson

unread,
Nov 30, 2017, 9:51:47 AM11/30/17
to std-pr...@isocpp.org
Pragma once has other problems, like nobody has cared to specify what it
means on systems other than Linux and Windows.

And should the compiler writer be required to test the feature on every
other file system that can possibly be mounted on your development system?


The copy-paste error with the include guard has the advantage that you
can fix it yourself.


Bo Persson

Jake Arkinstall

unread,
Nov 30, 2017, 10:57:02 AM11/30/17
to std-pr...@isocpp.org
I generally agree. Standardizing pragma once makes sense. In fact, it makes so much sense that I think it's worthwhile putting in the elbow grease and solving the issues that arise as part of the "spring cleaning"

On 30 Nov 2017 14:51, "Bo Persson" <b...@gmb.dk> wrote:
Pragma once has other problems, like nobody has cared to specify what it means on systems other than Linux and Windows.

How it works on other systems is a tough one, as is usually the case, but in general if you have the ability to include a file and parse it, I can't see why you won't also have the ability to hash it (perhaps with two different salts, to give two hashes, to significantly reduce accidental collisions between two different but well-formed files) and compare with already-included files.

That isn't to say you would have the ability, but that I can't see why not - perhaps someone can educate me on the matter. If it's a particularly limited (rather than just "different") system, isn't it advisable that the compilation side of things be done on a more capable system?

And should the compiler writer be required to test the feature on every other file system that can possibly be mounted on your development system?

Do compiler writers normally neglect testing on a variety of filesystems as part of their usual process? I mean, surely this is vital regardless? If I'm using a filesystem that hasn't been tested, and I compile something on It, I expect the developer to at least give me the courtesy of a warning at compile time.

Compilers do seem to have a fairly nice property, in that general compilers have large enough communities surrounding them to have them tested on a plethora of setups, and very specific compilers that are finely tuned for specific setups only need support those setups. Perhaps I'm being naive here, but it appears that the compiler writers have a pretty good balance going on. Sure, this proposal would shift more weight onto their shoulders, but that is generally what a progressive standardisation process should be doing. At the end of the day, a programming language benefits from usability. Just look at how beautiful code has become since "auto" gained superpowers, as an example.

The copy-paste error with the include guard has the advantage that you can fix it yourself.

I agree on this point. The compiler shouldn't have to navigate a dev's laziness, and a dev can gain a lot of knowledge from solving such issues themselves.

That being said, I do believe a dev shouldn't have to manually create and name a set of distinct constants simply for a compiler to know if it has seen a given file before. This is something that has long been solved with many other languages. It's almost an XY problem: the underlying problem is that the compiler is unaware of where it is, and a fudge of constants that may cause a bad compilation is actually more of a symptom than a problem in itself.

I'd personally go as far as saying that I wish the single inclusion could be the DEFAULT and for something like "#pragma many" to be used when a developer wants to have the ability to include a file multiple times. That's the rarer and more obscure case (and arguably a very hacky one that should be discouraged, and for a smoother approach to be created), so should be treated as the exception, not the rule, IMO. However, this being a breaking change, it can safely be designated to the "dream world" bin along with dogs that pick up their own mess, and a virtual reality equivalent of Vim so we can program in 3D with gesture commands and infinite screen space.

Jake

Nicol Bolas

unread,
Nov 30, 2017, 11:07:54 AM11/30/17
to ISO C++ Standard - Future Proposals
On Thursday, November 30, 2017 at 10:57:02 AM UTC-5, Jake Arkinstall wrote:
I generally agree. Standardizing pragma once makes sense. In fact, it makes so much sense that I think it's worthwhile putting in the elbow grease and solving the issues that arise as part of the "spring cleaning"

Ignoring the issues with #pragma once, modules is moving forward. Which means that, sooner or later (or much later), we won't be using #include very much.

What's the point of spending time on solving a problem that will become increasingly less relevant? Especially since we already have a solution that, while not perfect, is still entirely adequate.

Jake Arkinstall

unread,
Nov 30, 2017, 11:22:42 AM11/30/17
to std-pr...@isocpp.org
Point well made.

What's the point of spending time on solving a problem that will become increasingly less relevant? Especially since we already have a solution that, while not perfect, is still entirely adequate.

I think that it will still be relevant for quite some time yet - especially for newcomers, who I think would benefit the most from #pragma once. At least, I can't imagine that they'll be using modules.

That being said, there is a high chance that a beginner will be using a compiler that supports #pragma once anyway. Though whether or not we should be encouraging people to adopt non-standard code is another matter.

Nicol Bolas

unread,
Nov 30, 2017, 12:07:45 PM11/30/17
to ISO C++ Standard - Future Proposals
On Thursday, November 30, 2017 at 11:22:42 AM UTC-5, Jake Arkinstall wrote:
Point well made.

What's the point of spending time on solving a problem that will become increasingly less relevant? Especially since we already have a solution that, while not perfect, is still entirely adequate.

I think that it will still be relevant for quite some time yet - especially for newcomers, who I think would benefit the most from #pragma once. At least, I can't imagine that they'll be using modules.

It's people with large codebases who will be the last to adopt modules. Newcomers are the ones who will be more able to use them, since they have no inertia and little need for macros.

Bo Persson

unread,
Nov 30, 2017, 6:55:01 PM11/30/17
to std-pr...@isocpp.org
On 2017-11-30 16:56, Jake Arkinstall wrote:
> I generally agree. Standardizing pragma once makes sense. In fact, it
> makes so much sense that I think it's worthwhile putting in the elbow
> grease and solving the issues that arise as part of the "spring cleaning"
>
> On 30 Nov 2017 14:51, "Bo Persson" <b...@gmb.dk <mailto:b...@gmb.dk>> wrote:
>
>
> And should the compiler writer be required to test the feature on
> every other file system that can possibly be mounted on your
> development system?
>
>
> Do compiler writers normally neglect testing on a variety of filesystems
> as part of their usual process? I mean, surely this is vital regardless?
> If I'm using a filesystem that hasn't been tested, and I compile
> something on It, I expect the developer to at least give me the courtesy
> of a warning at compile time.
>
> Compilers do seem to have a fairly nice property, in that general
> compilers have large enough communities surrounding them to have them
> tested on a plethora of setups, and very specific compilers that are
> finely tuned for specific setups only need support those setups. Perhaps
> I'm being naive here, but it appears that the compiler writers have a
> pretty good balance going on. Sure, this proposal would shift more
> weight onto their shoulders, but that is generally what a progressive
> standardisation process should be doing. At the end of the day, a
> programming language benefits from usability. Just look at how beautiful
> code has become since "auto" gained superpowers, as an example.
>

To be more specfic:

At work I have a Windows 7 machine with mounts to a z/OS mainframe,
ClearCase on a Unix(?) server, serveral departmental servers (probably
Windows 2012), and half a dozen NAS devices that I don't know either
what they are or where they are located.

How is a compiler writer supposed to test that their compiler resolves a
#pragma once properly on that configuration?


You cannot standardize "sometimes works".


Bo Persson

Matthew Woehlke

unread,
Dec 1, 2017, 7:54:09 PM12/1/17
to std-pr...@isocpp.org
On 2017-11-30 10:56, Jake Arkinstall wrote:
> How it works on other systems is a tough one, as is usually the case, but
> in general if you have the ability to include a file and parse it, I can't
> see why you won't also have the ability to hash it (perhaps with two
> different salts, to give two hashes, to significantly reduce accidental
> collisions between two different but well-formed files) and compare with
> already-included files.

So... you would make it *content*-based, rather than *name*-based? While
I agree that is probably more useful, I believe it is a departure from
current practice. (It's also potentially slow and problematic, since I
believe current compilers can work on streams as well as files.)

I tried something like that with http://wg21.link/p0538. The committee
is basically uninterested in doing anything in this area. (Modules, as
mentioned, is a major contributor to this disinterest.)

--
Matthew

Tom Honermann

unread,
Dec 2, 2017, 10:22:12 PM12/2/17
to std-pr...@isocpp.org, Matthew Woehlke
On 11/30/2017 12:04 PM, Matthew Woehlke wrote:
> On 2017-11-30 10:56, Jake Arkinstall wrote:
>> How it works on other systems is a tough one, as is usually the case, but
>> in general if you have the ability to include a file and parse it, I can't
>> see why you won't also have the ability to hash it (perhaps with two
>> different salts, to give two hashes, to significantly reduce accidental
>> collisions between two different but well-formed files) and compare with
>> already-included files.
> So... you would make it *content*-based, rather than *name*-based? While
> I agree that is probably more useful, I believe it is a departure from
> current practice. (It's also potentially slow and problematic, since I
> believe current compilers can work on streams as well as files.)

From what I recall from debugging problems with #pragma once in the
past, current practice differs across implementations.  I think gcc uses
content based matching, Clang uses inode based matching, and MSVC uses
path matching.

Tom.
Reply all
Reply to author
Forward
0 new messages