> 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
>

Reply via email to