> Could a "uses the relative search path" fact be used to mix into the > file's identity? This way the `once` key would see "this content looked > for things in directory `library_a`" and would see that > `library_b/library_main.hpp`, despite the same content (and mtime) is > actually a different context and actually perform the inclusions?
I think I see what you're getting at. I am having a hard time imagining an implementation that doesn't lead to a lot of complexity and I think handling hard links would also be out of the question. Jeremy On Sep 6 2024, at 8:26 am, Ben Boeckel <ben.boec...@kitware.com> wrote: > On Fri, Sep 06, 2024 at 00:03:23 -0500, Jeremy Rifkin wrote: >> Hello, >> >> I'm looking at #pragma once behavior among the major C/C++ compilers as >> part of a proposal paper for standardizing #pragma once. (This is >> apparently a very controversial topic) >> >> To put my question up-front: Would GCC ever be open to altering its >> #pragma once behavior to bring it more in-line with behavior from other >> compilers and possibly more in-line with what users expect? >> >> To elaborate more: >> >> Design decisions for #pragma once essentially boil down to a file-based >> definitions vs a content-based definition of "same file". >> >> A file-based definition is easier to reason about and more in-line with >> what users expect, however, distinct copies of headers can't be handled >> and multiple mount points are problematic. >> >> A content-based definition works for distinct copies, multiple mount >> points, and is completely sufficient 99% of the time, however, it could >> potentially break in hard-to-debug ways in a few notable cases (more >> information later). >> >> Currently the three major C/C++ compilers treat #pragma once very >> differently: >> - GCC uses file mtime + file contents >> - Clang uses inodes >> - MSVC uses file path >> >> None of the major compilers have documented their #pragma once semantics. >> >> In practice all three of these approaches work pretty well most of the >> time (which is why people feel comfortable using #pragma once). However, >> they can each break in their own ways. >> >> As mentioned earlier, clang and MSVC's file-based definitions of "same >> file" break for multiple mount points and multiple copies of the same >> header. MSVC's approach breaks for symbolic links and hard links. >> >> GCC's hybrid approach can break in surprising ways. I have three >> examples to share: >> >> Example 1: >> >> Consider a scenario such as: >> >> usr/ >> include/ >> library_a/ >> library_main.hpp >> foo.hpp >> library_b/ >> library_main.hpp >> foo.hpp >> src/ >> main.cpp >> >> main.cpp: >> #include "library_a/library_main.hpp" >> #include "library_b/library_main.hpp" >> >> And both library_main.hpp's have: >> #pragma once >> #include "foo.hpp" > > Could a "uses the relative search path" fact be used to mix into the > file's identity? This way the `once` key would see "this content looked > for things in directory `library_a`" and would see that > `library_b/library_main.hpp`, despite the same content (and mtime) is > actually a different context and actually perform the inclusions? > > Of course, this fails if `#include "../common/foo.hpp"` is used in each > location as that *would* then want to elide the second inclusion. I > don't know how this problem is avoided without actually reading the > contents again. But the "I read this file" can remember what relative > paths were searched (since the contents are the same at least). > >> Example 2: >> >> namespace v1 { >> #include "library_v1.hpp" >> } >> namespace v2 { >> #include "library_v2.hpp" >> } >> >> Where both library headers include their own copy of a shared header >> using #pragma once. > > Again, the context of the inclusion matters, so "is wrapped in a scope" > can modify the "onceness" (`extern "C"` is probably the more common > instance). > >> Example 3: >> >> usr/ >> include/ >> library/ >> library.hpp >> vendored-dependency.hpp >> src/ >> main.cpp >> vendored-dependency.hpp >> >> main.cpp: >> #include "vendored-dependency.hpp" >> #include <library/library.hpp> >> >> library.hpp: >> #pragma once >> #include "vendored-dependency.hpp" > > This is basically the same as Example 1 as far as context goes. > > Note that context cannot include `#define` state because `#once` is > defined to be the first thing in the file and a file that is intended to > be included multiple times (e.g., Boost.PP shenanigans) in different > states cannot, in good faith, use `#once`. > > Hrm…though if we are doing `otherdir/samecontent`, the different > preprocessor state *might* change that "what relative files did we look > for?" state… Nothing is easy :( . > > --Ben >