[Development] Using '#pragma once' instead of include guards?

Marc Mutz marc.mutz at qt.io
Thu Feb 29 11:02:29 CET 2024


Hi,

DL;DR: Use #pragma once in all non-installed headers

The question recently came up "what is a private header". And the answer 
isn't just "_p.h, of course". We have tons of headers that are "private" 
without being marked as such with _p.h and "We mean it." comment.

The first realization is that there are degrees of privateness: We have 
the installed private headers, and then we have non-installed/able 
headers, e.g. in plugins, or tools.

So we have
- public installed headers (subject to SC and BC, syncqt and 
headerscheck runs on them)
- semi-public installed headers (like above, but not subject to SC (but 
BC) (_impl.h, stuff in QtPrivate namespaces, qNN, ...)
- private installed headers (not subject to SC/BC/headersclean, but 
syncqt runs on them, must have "We mean it." comment)
- private non-installed headers (not subject to any constraint, not even 
syncqt runs on them)

We can now look at what signs we currently have available that guide a 
reader to learn which kind of header he's looking at.

For the first, we have only location in $SRCDIR.

For the second, we have _impl.h and/or "We mean it." comment.

For the third, which is easiest to distinguish, we have _p.h and "We 
mean it." comment. This is enforced by syncqt, which is why we can rely 
on it 100%.

For the last one, we again have just the location in $SRCDIR.

The problem is, obviously, that the first and last cases are nearly 
indistinguishable and require non-local reasoning to answer.

I think we have improve on this.

With Volker's email we gave ourselves permission to use #pragma once for 
"non-SDK" (= non-installed) headers, and banned it for installed 
headers. So if we could make syncqt complain if a processed (= 
installable) header contains #praga once, we could then flip the coin 
and use an actual #pragma once as a static assertion that the header is 
not installed/able.

If we do this going forward, we can then easily distinguish the four 
header kinds:

- public installed headers have a traditional header guard
- semi-public installed headers ditto, except that have _impl.h suffix 
or "We mean it" comment
- private installed headers ditto, _p.h suffix and "We mean it" comment
- non-installed/able headers have #pragma once

I've implemented the check in syncqt.cpp and ported xcb over, see 
https://codereview.qt-project.org/q/topic:pragma-once

I'm not suggesting to do such a port for all plugins. XCB is just a test 
balloon, but we might want to apply the #pragma once trick for new code 
going forward.

Thanks,
Marc

On 12.10.22 12:35, Volker Hilsheimer via Development wrote:
> 
>> On 11 Oct 2022, at 22:11, Thiago Macieira <thiago.macieira at intel.com> wrote:
>>
>> On Tuesday, 11 October 2022 12:25:13 PDT Kyle Edwards via Development wrote:
>>> Speaking as co-maintainer of CMake, we have effectively required #pragma
>>> once to build CMake itself since August 2017, we officially codified
>>> this as policy in September 2020, and we will soon be writing a
>>> clang-tidy plugin to enforce this in our CI. We have not received any
>>> complaints about it. Just my $0.02.
>>
>> Thanks for the information. This confirms what we already knew that all systems
>> and compilers where Qt would be compiled do support it.
>>
>> However, neither Qt Creator nor CMake are libraries. They are not comparable.
> 
> 
> Thanks all for sharing your insights and digging up the previous discussions as well.
> 
> The summary of all this then seems to be:
> 
> - ok to use '#pragma once’ in headers that are not designed to be included by Qt users, i.e. in tools, applications, examples and demos, tests
> - for everything else, in particular for public and, for consistency’s sake - private headers in Qt, we continue to use conventional include guards
> 
> Rationale: #pragma once is not well enough defined and not part of the standard, and we cannot make any assumptions about how Qt is installed, used as part of a larger SDK etc. So best to stay conservative.
> 
> If that’s not entirely off, then I’d like to put this into https://wiki.qt.io/Coding_Conventions [1], preempting perhaps a new thread on this topic in a few years.
> 
> Volker
> 
> [1]: And since that page seems rather outdated - e.g. we do use dynamic_cast in Qt today, and the suggestion to normalize signals and slots should rather suggest to make connections via PMF syntax - perhaps it’s time to move this to a QUIP where we can discuss and review such changes in gerrit. I won’t have time to do that for a while (perhaps ditto for https://wiki.qt.io/Qt_Coding_Style), but perhaps someone else wants to give this a shot.
> 
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> https://lists.qt-project.org/listinfo/development
-- 
Marc Mutz <marc.mutz at qt.io>
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io

Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B



More information about the Development mailing list