C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Revising #pragma once

From: Tiago Freire <tmiguelf_at_[hidden]>
Date: Mon, 2 Sep 2024 11:58:18 +0000
Again, “pragma once” is a construct that works for cross-platform projects right now.
No change necessary.

Boths these statements must be reconcilable for even to make sense of reality.
I think you guys need to spend sometime learning how this actually works, or at least listen to someone who deploys this effectively.


Let’s say you have project A and B like so:

$(Project_A_Path)/include/ProjA/A.h
$(Project_A_Path)/include/ProjA/Sub/B.h

$(Project_B_Path)/src/c.cpp

Let’s say that to compile c.cpp you have to add the additional include path -I $(Project_A_Path)/include
c.cpp looks as follows:
#include <ProjA/A.h>

And A.h looks as follows:
#<paceholder implementation defined guard>
#include "Sub/B.h"


When parsing #include <ProjA/A.h>, the compiler will search for this file and hit “$(Project_A_Path)/include/ProjA/A.h” which is at
“$(Project_A_Path)/include/ProjA/”
When parsing A.h and hits #include "Sub/B.h", the "" include could be a local file
A.h is at “$(Project_A_Path)/include/ProjA/Sub/B.h” (i.e. it will find "Sub/B.h" from “$(Project_A_Path)/include/ProjA/”)

Which exactly the same thing as if c.cpp had done:
#include <ProjA/B.h>
#include <ProjA/A.h>

Which would have resolved #include <ProjA/B.h> as “$(Project_A_Path)/include/ProjA/Sub/B.h”
It’s the exact same thing.

---- Ex. 2

Even if an extra -I $(Project_A_Path)/include/ProjA/Sub
And A.h:
#include <B.h>

And c.cpp:
#include <ProjA/B.h>
#include <ProjA/A.h>

B.h would still resolve too “$(Project_A_Path)/include/ProjA/Sub/B.h”, ALWAYS!

Remove the ability to duplicate content in the filesystem (ex. symlinks), and it is impossible for B.h to be resolved to anything else other than “$(Project_A_Path)/include/ProjA/Sub/B.h”

I dare you try it! You won’t be able to do it!


---- Ex. 3
From this we can conclude content duplication is required, either by actually making a physical copy, or creating a separate link (whatever form it might take)

How can we possibly get 2 distinct paths to resolve to “B.h”

Let’s keep with the previous example, let’s imagine that $(Project_A_Path) is a symbolic link.
Guess what? B.h still resolves to “$(Project_A_Path)/include/ProjA/Sub/B.h”, and all of the equivalence still old true, because the only way to see “B.h” is to go trough “$(Project_A_Path)” my compiler is unaware that there are any other paths outside of this.
So, this by itself isn’t enough.

---- Ex. 4
We have the following project

$(common_path)/Project_A/include/ProjA/A.h
$(common_path)/Project_A/include/ProjA/Sub/B.h
$(common_path)/Project_C/include/ProjC/C.h
$(Project_B_Path)/src/c.cpp

Link $(Project_A_Path) = $(common_path)/Project_A
$(Project_C_Path) = $(common_path)/Project_C (not a link)


C.h:
#include "../../../Project_A/include/ProjA/Sub/B.h"


c.cpp:
#include <ProjC/C.h>
#include <ProjA/A.h>


Compile with -I $(Project_A_Path)/include -I $(Project_C_Path)/include

Only now we start getting into trouble, because we started to see
“C.h” from “$(common_path)/Project_C/include/ProjC/C” which then resolves B.h as
“$(common_path)/Project_C/include/ProjC/C” + "../../../Project_A/include/ProjA/Sub/B.h" = “$(common_path)/Project_A/include/ProjA/Sub/B.h”
While #include <ProjA/A.h> would resolve “B.h” as “$(Project_A_Path)/include/ProjA/Sub/B.h”

I.e. “$(Project_A_Path)/include/ProjA/Sub/B.h” != “$(common_path)/Project_A/include/ProjA/Sub/B.h”


But look at what I had to do to reach here:
* I need a link
* I needed a second include path that goes trough a different link
* “C.h” had to find an alternative way to reach across the other header file trough that different link "../../../Project_A/include/ProjA/Sub/B.h"

Had “C.h” simply done “#include <ProjA/Sub/B.h>", the only way it would have been able to find “B.h” was trough -I $(Project_A_Path)/include i.e. “$(Project_A_Path)/include/ProjA/Sub/B.h”

If you are just going to reach into neighboring projects trough relative paths going outside of the confines of the project assuming that the relative paths of other dependencies remain the same.
Then congratulations you have shot yourself in the foot.
Don’t do this is considered bad practice! Never reach out outside the project like this. #include "../" is considered bad practice.


And look how many hoops I had to jump through to get to the point where I could even shoot myself in the foot!

If you think you are not shooting yourself in the foot. Please give me an example of what exactly is your build setup, where your links are, where I can find different paths to the same file, what include paths you are making visible, and what include directives does it have to go trough to even get to such a collision.

Otherwise, my only answer is stop wearing those gun shoes.





From: Std-Proposals <std-proposals-bounces_at_[hidden]> On Behalf Of Jonathan Wakely via Std-Proposals
Sent: Monday, September 2, 2024 11:21 AM
To: std-proposals_at_[hidden]rg
Cc: Jonathan Wakely <cxx_at_[hidden]>
Subject: Re: [std-proposals] Revising #pragma once



On Mon, 2 Sept 2024 at 09:53, Sebastian Wittmeier via Std-Proposals <std-proposals_at_[hidden]<mailto:std-proposals_at_[hidden]>> wrote:

Hi Gašper,



perhaps my answer was too fast.



I was trying to go a third way:

Neither content nor local file identity, but using the path given in the #include files.



Difficulties arise from the facts

 - that libraries have several entry points for their includes, possibly in subdirectories.

 - that -I include directories may point to one or several libraries

 - that some libraries would need several -I parameters, e.g. for sub-components or plugins



Perhaps a working solution would be to create a #once only working for #include "".

The behaviour of #include "" is implementation-defined. The common "relative to the file doing the including" behaviour is not required by the standard.


After including a library with a search path with <>, all subsequent includes with "" are seen relative to the current file.



(Some compilers provide more complicated options to also extend a search path for "" or do so with the paths of all previously included files, but those extended lookup paths are not standard.)



With those files at least it should be possible to rely solely on the paths provided in the #include "" directives.





What to do with



main.cpp

#include <mylib/librarydefs.h>

#include <mylib/constants.h>

librarydefs.h:

#include "constants.h"



librarytemplates.h:

#include "constants.h"



constants.h:
#once





We would have to know that both includes in main point to the same library.



That could get more difficult with



-I /includes/alllibs -I /specialincludes/mylibplugins



On the other hand, #include "" probably would not work now either between those subcomponents of the library.



So we could limit #once to work only within one -I path (or of course in the cpp file, as long as #include "" is used locally)







Not sure, if it is worth it, as it is only a partly solution (only #include "") and is different from all the existing #pragma once implementations. And perhaps I have not seen some of the possible pitfalls.




Received on 2024-09-02 11:58:22