Discussion:
[Development] Using #pragma once
Lars Knoll
2018-10-07 08:56:47 UTC
Permalink
Hi,

Just a quick question: Does anybody have any good arguments against us starting to use #pragma once instead of header guards throughout our code base?

I’ve started using it implicitly when updating 3rd party code (the macro assembler) in qtdeclarative without any problems (so I’d supported by all our compilers). IMO #pragma once is both safer and nicer to use than classic header guards.

Cheers,
Lars
André Pönitz
2018-10-07 09:06:05 UTC
Permalink
On Sun, Oct 07, 2018 at 08:56:47AM +0000, Lars Knoll wrote:
> Hi,
>
> Just a quick question: Does anybody have any good arguments against us
> starting to use #pragma once instead of header guards throughout our
> code base?

Not me.

> I’ve started using it implicitly when updating 3rd party code (the
> macro assembler) in qtdeclarative without any problems (so I’d
> supported by all our compilers). IMO #pragma once is both safer and
> nicer to use than classic header guards.

Qt Creator uses it since more than two years without problems.

For a potential conversion https://github.com/cgmb/guardonce
may help.

Andre'
Gunnar Roth
2018-10-07 12:30:52 UTC
Permalink
Hi Lars,
I do not really object exclusive use of pragma once, without header
guards ( I use it myself), I just want to tell about my experience on
Debian Stretch with gcc 6.3

1. Using precompiled header, you can run into trouble, if you use
forward header , like qt does, and these do not contain pragma once too.
I got double definition errors then, because pragam once was ignored
somehow.

2. There is a , still unfixed, gcc bug since gcc 4.6.3 , where pragma
once is ignored for files which start with a Utf8 BOM, when using
precompiled headers. see
https://gcc.gnu.org/bugzilla/show_bug.cgi?format=multiple&id=56549

3. #pragma once makes gcc much slower according to Bug 58770 - GCC very
slow compiling with #pragma once
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58770


Regards,
Gunnar Roth



------ Original Message ------
From: "Lars Knoll" <***@qt.io>
To: "Qt development mailing list" <***@qt-project.org>
Sent: 07/10/2018 10:56:47
Subject: [Development] Using #pragma once

>Hi,
>
>Just a quick question: Does anybody have any good arguments against us
>starting to use #pragma once instead of header guards throughout our
>code base?
>
>I’ve started using it implicitly when updating 3rd party code (the
>macro assembler) in qtdeclarative without any problems (so I’d
>supported by all our compilers). IMO #pragma once is both safer and
>nicer to use than classic header guards.
>
>Cheers,
>Lars
>
>_______________________________________________
>Development mailing list
>***@qt-project.org
>http://lists.qt-project.org/mailman/listinfo/development
Lars Knoll
2018-10-07 14:37:19 UTC
Permalink
> On 7 Oct 2018, at 15:18, Sérgio Martins <***@kdab.com> wrote:
>
> On 2018-10-07 09:56, Lars Knoll wrote:
>> Hi,
>> Just a quick question: Does anybody have any good arguments against us
>> starting to use #pragma once instead of header guards throughout our
>> code base?
>
> Hi Lars,
>
>
> This was already discussed back in January: https://lists.qt-project.org/pipermail/development/2018-January/031966.html
>
> The conclusion was to keep using header guards.

Thanks. I must have missed that thread (or forgotten about it) :)

Cheers,
Lars
Thiago Macieira
2018-10-07 18:39:22 UTC
Permalink
On Sunday, 7 October 2018 01:56:47 PDT Lars Knoll wrote:
> Hi,
>
> Just a quick question: Does anybody have any good arguments against us
> starting to use #pragma once instead of header guards throughout our code
> base?

Yes, two:

a) not supported everywhere
b) not well-defined behaviour when it comes to anything but the simplest
organisation

For example, I have ~/src as a bind-mount to ~/dev/src. That means
~/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
~/dev/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
are actually the same file, but they aren't for the effects of #pragma once
because the paths differ.

Another problem is qcompilerdetection.h, qprocessordetection.h,
qsystemdetection.h, qtypeinfo.h, etc. which depend on the header guards to
break the include cycle. Ditto for qlist.h + qstringlist.h and
qbytearraylist.h

> I’ve started using it implicitly when updating 3rd party code (the macro
> assembler) in qtdeclarative without any problems (so I’d supported by all
> our compilers). IMO #pragma once is both safer and nicer to use than
> classic header guards.

On all the compilers that compile that macro assembler. We still have
architectures where they don't.

Another aspect is that the macro assembler headers don't get installed and
aren't subject to syncqt.pl, so their hierarchy is a lot simpler than for Qt's
own headers.

I recommend against changing Qt.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Henry Skoglund
2018-10-07 22:17:30 UTC
Permalink
On 2018-10-07 20:39, Thiago Macieira wrote:
> On Sunday, 7 October 2018 01:56:47 PDT Lars Knoll wrote:
>> Hi,
>>
>> Just a quick question: Does anybody have any good arguments against us
>> starting to use #pragma once instead of header guards throughout our code
>> base?
>
> Yes, two:
>
> a) not supported everywhere
> b) not well-defined behaviour when it comes to anything but the simplest
> organisation
>
> For example, I have ~/src as a bind-mount to ~/dev/src. That means
> ~/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
> ~/dev/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
> are actually the same file, but they aren't for the effects of #pragma once
> because the paths differ.
>
> Another problem is qcompilerdetection.h, qprocessordetection.h,
> qsystemdetection.h, qtypeinfo.h, etc. which depend on the header guards to
> break the include cycle. Ditto for qlist.h + qstringlist.h and
> qbytearraylist.h
>
...
...
>
> I recommend against changing Qt.
>

Hi, but isn't C++17's __has_include preprocessor cmd an implicit
endorsement of #pragma once? I mean, they both assume that the file
namespace is stable and idempotent.

The example with ~/src as a bind-mount to ~/dev/src above, reminds me of
the old pointer aliasing problems in C++. But cannot it be solved by
requiring #pragma once to always call realpath() first, i.e. always test
on "the canonicalized absolute pathname" that realpath() returns?

Rgrds Henry
Thiago Macieira
2018-10-08 05:13:16 UTC
Permalink
On Sunday, 7 October 2018 15:17:30 PDT Henry Skoglund wrote:
> > I recommend against changing Qt.
>
> Hi, but isn't C++17's __has_include preprocessor cmd an implicit
> endorsement of #pragma once? I mean, they both assume that the file
> namespace is stable and idempotent.

No, I don't see how one thing has to do with the other.

__has_include imples that the #include will not fail with "No such file or
directory" and hopefully won't fail either with "Is a directory". Other checks
are welcome, but I wouldn't be too picky if they didn't get done.

#pragma once only applies after the #include and changes nothing about whether
the #include succeeds or not.

> The example with ~/src as a bind-mount to ~/dev/src above, reminds me of
> the old pointer aliasing problems in C++. But cannot it be solved by
> requiring #pragma once to always call realpath() first, i.e. always test
> on "the canonicalized absolute pathname" that realpath() returns?

No.

$ realpath ~/dev/src/qt/ ~/src/qt
/home/tjmaciei/dev/src/qt
/home/tjmaciei/src/qt

Bind-mounts are not symlinks. For all intents and purposes they are different
filesystems: you can't link() from one to the other, for example. You may be
able to tell that they are the same because the dev_t is the same:

$ stat -c '%D %i %n' ~/dev/src ~/src
fe03 4456449 /home/tjmaciei/dev/src
fe03 4456449 /home/tjmaciei/src

But to tell that the file is the same you'd need to compare inodes and those
are not guaranteed on network mounts.

Another option is to use name_to_handle_at(), a Linux-specific function, but
that will also fail with bind mounts and multiple mounts of the same
filesystem. The manpage suggests resolving mount IDs to actual disks by
looking for them in /dev/disks/by-uuid.

So you see why this is not a very easy solution.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Henry Skoglund
2018-10-08 06:23:39 UTC
Permalink
On 2018-10-08 07:13, Thiago Macieira wrote:
> On Sunday, 7 October 2018 15:17:30 PDT Henry Skoglund wrote:
>>> I recommend against changing Qt.
>>
>> Hi, but isn't C++17's __has_include preprocessor cmd an implicit
>> endorsement of #pragma once? I mean, they both assume that the file
>> namespace is stable and idempotent.
>
> No, I don't see how one thing has to do with the other.
>
> __has_include imples that the #include will not fail with "No such file or
> directory" and hopefully won't fail either with "Is a directory". Other checks
> are welcome, but I wouldn't be too picky if they didn't get done.
>
> #pragma once only applies after the #include and changes nothing about whether
> the #include succeeds or not.
>
>> The example with ~/src as a bind-mount to ~/dev/src above, reminds me of
>> the old pointer aliasing problems in C++. But cannot it be solved by
>> requiring #pragma once to always call realpath() first, i.e. always test
>> on "the canonicalized absolute pathname" that realpath() returns?
>
> No.
>
> $ realpath ~/dev/src/qt/ ~/src/qt
> /home/tjmaciei/dev/src/qt
> /home/tjmaciei/src/qt
>
> Bind-mounts are not symlinks. For all intents and purposes they are different
> filesystems: you can't link() from one to the other, for example. You may be
> able to tell that they are the same because the dev_t is the same:
>
> $ stat -c '%D %i %n' ~/dev/src ~/src
> fe03 4456449 /home/tjmaciei/dev/src
> fe03 4456449 /home/tjmaciei/src
>
> But to tell that the file is the same you'd need to compare inodes and those
> are not guaranteed on network mounts.
>
> Another option is to use name_to_handle_at(), a Linux-specific function, but
> that will also fail with bind mounts and multiple mounts of the same
> filesystem. The manpage suggests resolving mount IDs to actual disks by
> looking for them in /dev/disks/by-uuid.
>
> So you see why this is not a very easy solution.
>

Yes, I see.

When #pragma once was introduced 20 years ago by Microsoft I thought it
was the greatest thing since sliced bread, but maybe it's time to
reconsider.

So, what about a new preprocessor command:

__has_same_md6_digest

It would require the compiler to store an md6 hash/checksum of the
contents of each of the #include files seen during compilation, but if
most of the compiler's time is spent waiting for file i/o, then
computing those md6 sums wouldn't be so costly.

It would work the same way as the traditional header guards, but more
automated, example:

#if !__has_same_md6_digest
...
...
...
#endif

Rgrds Henry
Matthew Woehlke
2018-10-08 16:12:26 UTC
Permalink
On 08/10/2018 02.23, Henry Skoglund wrote:
> So, what about a new preprocessor command:
>
> __has_same_md6_digest

See also http://wg21.link/p0538 and note that EWG rejected it. The
general consensus, AFAICT, is that modules is expected to make all this
stuff irrelevant, and therefore EWG does not want to invest in fiddling
with the preprocessor for a problem that is becoming irrelevant.

--
Matthew
Henry Skoglund
2018-10-08 16:40:52 UTC
Permalink
On 2018-10-08 18:12, Matthew Woehlke wrote:
> On 08/10/2018 02.23, Henry Skoglund wrote:
>> So, what about a new preprocessor command:
>>
>> __has_same_md6_digest
>
> See also http://wg21.link/p0538 and note that EWG rejected it. The
> general consensus, AFAICT, is that modules is expected to make all this
> stuff irrelevant, and therefore EWG does not want to invest in fiddling
> with the preprocessor for a problem that is becoming irrelevant.
>

Aha, didn't know about your proposal, looks good.

I forgot about modules, and even if they don't make it into C++20, I
agree that most likely they are the future/best way to solve this problem.

Rgrds Henry
Thiago Macieira
2018-10-08 17:06:37 UTC
Permalink
On Monday, 8 October 2018 09:12:26 PDT Matthew Woehlke wrote:
> On 08/10/2018 02.23, Henry Skoglund wrote:
> > So, what about a new preprocessor command:
> >
> > __has_same_md6_digest
>
> See also http://wg21.link/p0538 and note that EWG rejected it. The
> general consensus, AFAICT, is that modules is expected to make all this
> stuff irrelevant, and therefore EWG does not want to invest in fiddling
> with the preprocessor for a problem that is becoming irrelevant.

Comparing hashes has another problem: what happens if you have /usr/include/
QtCore/qglobal.h but it's slightly different (an older version)? In that case,
it could get included by #pragma once but wouldn't be for regular include
guards.

Again, this is a mistake in setting things up, but it happens often enough.
And the problem is one of figuring out what's wrong.

Not that it would stop *another* header from the older installation from
getting included, though.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Edward Welbourne
2018-10-08 08:50:04 UTC
Permalink
On Sunday, 7 October 2018 01:56:47 PDT Lars Knoll wrote:
>> Just a quick question: Does anybody have any good arguments against
>> us starting to use #pragma once instead of header guards throughout
>> our code base?

Thiago Macieira (7 October 2018 20:39) wrote:
> For example, I have ~/src as a bind-mount to ~/dev/src. That means
> ~/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
> ~/dev/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
> are actually the same file, but they aren't for the effects of #pragma
> once because the paths differ.

How could the compiler end up trying to #include both of those files ?
Wherever they appear in its include-path for a given compilation unit,
one of them must appear before the other and thus be used consistently.
I suppose you could put one directory in the sys-include path for
#include <qglobal.h> and the other in the source-include path for
#include "qglobal.h" ... but this seems like a strangely contrived thing
to do and I'd be inclined to say "well don't do that, then" to anyone
who has problems with it. What am I missing ?

The other reasons make more sense to me (and I'm "not a fan" of #pragma
in general),

Eddy.
Thiago Macieira
2018-10-08 15:33:00 UTC
Permalink
On Monday, 8 October 2018 01:50:04 PDT Edward Welbourne wrote:
> On Sunday, 7 October 2018 01:56:47 PDT Lars Knoll wrote:
> >> Just a quick question: Does anybody have any good arguments against
> >> us starting to use #pragma once instead of header guards throughout
> >> our code base?
>
> Thiago Macieira (7 October 2018 20:39) wrote:
> > For example, I have ~/src as a bind-mount to ~/dev/src. That means
> >
> > ~/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
> > ~/dev/src/qt/qt5/qtbase/src/corelib/global/qglobal.h
> >
> > are actually the same file, but they aren't for the effects of #pragma
> > once because the paths differ.
>
> How could the compiler end up trying to #include both of those files ?

THAT is the million dollar question.

Of course it shouldn't, but it could happen in an improperly-configured build,
with stale files. And now instead of no error due to same include guard, you
get C++ error which make no sense.

> Wherever they appear in its include-path for a given compilation unit,
> one of them must appear before the other and thus be used consistently.
> I suppose you could put one directory in the sys-include path for
> #include <qglobal.h> and the other in the source-include path for
> #include "qglobal.h" ... but this seems like a strangely contrived thing
> to do and I'd be inclined to say "well don't do that, then" to anyone
> who has problems with it. What am I missing ?

The ability to debug and who has to do it.

If this problem happens, it happens for someone *using* Qt, not those of us
developing it. And figuring out what went wrong is extremely difficult.

> The other reasons make more sense to me (and I'm "not a fan" of #pragma
> in general),

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Loading...