-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SF.12: Prefer the quoted form of #include
for files relative to the…
#1596
Conversation
… including file The current guidance on SF.12 can be over-applied and devolves into "always use <>" because all compilers support adding include directories to the <> search. In this case, even the current directory may be added and so it is always possible to use <> for every header. Applying the guidance then devolves into an undesirable state where <> is always used and include"" is never used. Instead, the proposed guidance leverages and encourages the distinction between <> and "" to create an easy-to-understand rule that the original guidance hints at and that most developers already follow and understand: "" is for local headers and <> is for library and external headers.
I like it. |
…clude` for files relative Updated wording, adjusted example, and provided a more verbose example of what can go wrong if using the wrong form.
If you're writing a library, you must use the angle-bracket form:
This line will work no matter whether |
Correct; I didn't mention it in SF.12 because it's not an exception to the rule: including "mine/detail.h" is not an option. But there should be another guideline that says as a library creator, you should place your headers along a unique path (author/component/header.h) and include your own headers the same way your users will (e.g. prefer <quux/mine/detail.h> vs "detail.h"). Checking the GSL I can't find it such a guidance, so I created an issue for us to go create one: |
@Quuxplusone can you describe in detail more what you meant? I think I misinterpreted it when I created: I mistakenly thought using "detail.h" from mine.h would not be found when included from user code, but I'm reminded that the include is relative from the including file (and not just the final cpp file). In this case, as @MikeGitb suggests, I think it's reasonable for library writers to also use the #include "detail.h" form since this will almost always locate the same detail.h as #include <mine/detail.h>. If we also exactly follow the guideline of this PR for SF.12, then it would also suggest that the latter form is preferred (e.g. detail.h will always be at a local relative path to mine.h, so #include "detail.h" should be preferred) |
Hmm. Bloomberg BDE guideline 4.3.4: "All include directives must use angle brackets (i.e., JSF AV Rule 33: "The Google C++ style guide doesn't directly address it, but shows a mix of Facebook HHVM guidelines: "Use double quotes for Folly and HHVM headers, and angle brackets for all others." So I guess the trouble with There may be a distinction to be made (even though none of the guidelines above explicitly make this distinction) between "public-facing headers" and "private implementation headers." For example, if your project contains
then it might be reasonable for "src/base/foo.cpp" to contain the lines
However, I hope we all agree that the public-facing header "foo.h" should never ever contain
and if the public-facing header "foo.h" needs to consume one of its public-facing siblings, it should do it via its "public-facing identifier"
I believe Bloomberg gets by with the simpler guideline (which is also what I was pushing for) simply because they never write "private implementation headers." |
It's quite simple: On clang, gcc, msvc and a few other compilers that I don't remember,
will first look relative to the current directory (the directory in which the file currently being processed resides) and second relative to whatever include paths are built-in to the compiler (/usr/include) or have been specified on the command line (
Will only look relative to the paths built-in or specified on the command line. |
This is surprising. My conclusion was precisely the opposite last time I thought about this. If the library uses angle brackets, then it relies on compiler configuration (include paths) be passed. After all, the user code could be doing:
That won't work unless I add If the library uses quotes (which I believe to mean "relative includes"), then there is no extra configuration needed. As soon as the user code can include the |
I disagree and would like to know what your reasoning is. Consider contributing to the discussion here. (To clarify, I agree that the choice to include -- at all -- this particular file looks highly dubious, but the general belief that |
make it more clear that using <> for external projects is just a typical example (and not the only use case for <> outside of standard headers)
.... if a bit more monotonous, but that's ok.
Just a note. The c++ stanard draft actually says: use <> for standard library and "" for anything else:
#include <stdio.h>
#include <unistd.h>
#include "usefullib.h"
#include "myprog.h" http://eel.is/c++draft/cpp.include#8.note-1 this is a note however, and it might be an outdated one. |
I think it completely ignores the reality, but it is an interesting point. |
Editors call: We agree the existing guideline is somewhat vague, but the quoted form is less portable and there is no consensus at this time for an improvement in the wording. We agree with the feedback mentioned in the discussion thread, including that if writing a library one should prefer the angle form so that you can have a more predictable search algorithm. |
This is a much better version than the original version that was submitted. However, I still see some issues. When it says "exist at a relative path", is that an invitation to scrutinize the installation location and decide based on that whether to use the quoted form, or is that an invitation to exploit the internal coherence of a component? That is, if my library's header is going to be installed in I agree that terms such as "project" are too ambiguous. |
the latter but not the former: the library author can't know where the library will be installed or what headers will be co-located with it, outside of the headers it provides. |
Fantastic! The wording isn't reflecting that though since it talks about "exist at a relative path": once the library header is installed, it will exist at a relative path. So we need to clarify that. How about "use the quoted form if the header being included is part of the same component therefore is expected to exist with the including header as a coherent whole"? |
it should be covered under the same rule, but with the implication being "at the current location of authoring the file". I can make that explicit if need be? (but I do feel it's pretty clear that "the header" here refers to the one being authored and not any future copy of itself placed into the compiler install location, since no one could reason about the location or neighbors of such a file or hope to edit it ... except if doing something unorthodox like authoring a header library directly in /usr/include!) added the following blurb: |
explicitly note that "file" refers to as it exists in the location of it being authored/modified. this is to address any confusion about #including "bar.h" from foo.h based on whether bar.h is relative to foo.h at the time and location foo.h is being modified (e.g. under mylib/foo.h) versus at the time/location from which foo.h is installed to the system and included by the user (e.g. from /usr/include).
That makes clearer your intent; thanks! The rule as written is still approaching the issue from an "implementation" perspective ("at it exists in the location where it is typically authored/edited") versus expressing clearly the desired structure. The desired structure is invariable independently of where the header ended up being found by the compiler or a reader -- either during build time of the component, or after installation. The reason I am emphasizing this aspect is because people will copy the structure they see when they are consulting/reading. This is one of the reasons why in my previous message I talked about component. The header file could have been generated on the fly -- that shouldn't matter. What matters is the final structure and what form of includes is used to express intent. If you can adjust the text to express that, that would be awesome. Thanks! |
I want to avoid referring to a component or project because of the case where headers are included from separate components along the relative path: mylib/foo.h includes mylib/deps/otherlib/bar.h. In that case, one might conclude the guidance doesn't apply because "otherlib" is a different component. but I suppose if entire dependent projects or headers are placed within the project (as opposed to consuming them from an external location) then, like it or not, those separate projects have now become an inseparable part of the project. clarified this as: |
... files that exist at a relative path to the file containing the `#include` statement (from within the same component or project)
Thanks @gdr-at-ms. This section is improved now. Unfortunately, @apenn-msft , the merged wording suffers from exactly the ambiguity I mentioned here. These lines reintroduce use of problematic term, 'project'. Whether or not an included file is from the same project is not sufficient to determine whether |
@johnmcfarlane I also wanted to avoid the use of terms like 'project' or 'component', but the statement is qualified as "locally relative": If a header search path (<>) was used to locate a file at the same path where it exists relatively then the guidance applies, and "" should be used instead. Example: In this case, the guidance is recommending foo.h include bar.h using "", this seems correct to me and in the spirit of the guidance. While myproject could be on the header search path, the guidance makes it more portable: using "" to access bar.h relatively will work in either case (whether it's on the search path or not) and more concisely names bar.h as "the bar.h that is co-existent with foo.h" increasing likelihood that the correct bar.h is selected. Note that in a case like: Whether "" or <> is used (in context of this guideline) does depend on whether the reader considers foo.h to be relatively reachable from bar.h and that depends on their policy of using ".." in include paths. That decision is intentionally left up to the reader. |
Couldn't you remove the words "project" and "component", then?
|
@Quuxplusone, they were originally removed in response to @johnmcfarlane original comment; but see @gdr-at-ms comments:
where the worry was a possible interpretation that leaves readers coding against the header where it resides rather than the header from where it is authored (e.g. the difference between /usr/include/foo.h and myproj/foo.h). I felt I was able to satisfy both gdr's comment by naming the structure ("project") and john's comment by stating that even within a "project" local relative-ness must apply if using "" (which I think addresses the issue with the original reason to remove it which is where someone might interpret needing to use "" anywhere within the project). |
The examples in SF.12 are likely to encourage readers to always use the `""` form of `'#include` when including headers from the same project ([discussion](isocpp#1596 (comment))). However, in larger projects this may not always be appropriate; `<>` should be used for includes located via a header search path. This proposed solution adds an example of the later, i.e. where `<>` is used to include a header from the same project.
@apenn-msft I fully understand. This isn't the first PR on this topic which was pulled in competing directions! I've opened another PR with an additional example: #include <component_b/bar.h> // A file in the same project located via a search path, use the <> form I'd appreciate any feedback as this is perhaps the hardest example for many readers to comprehend. |
I agree with @Quuxplusone and @johnmcfarlane about the use of the term "project". I continue to believe "component" is more appropriate than "project". Talk of "relative path" sounds too low level to provide an effective guidelines, at least what we want to convey -- which is why I asked the question earlier. Also, talk of "where the header is authored" fails to capture the common scenario that headers can be generated at build time. @Quuxplusone - would you mind preparing a PR that reflects your suggested edit? |
"component" is not a sufficient enough level to reason over because it is many-to-one to a "project". A project or "library" can consist of multiple "component"s; see @johnmcfarlane's example in #1664 which implies this interpretation as well. Borrowing from the example in #1664, if you have structure:
The syntax for including componentB/bar.h from componentA/foo.h is impacted by this guidance; if reasoning at the "project" level (e.g. considering relativity from anywhere within some_library), you would apply the guidance as using the "" form. This is the better outcome since it increases the likelihood of locating the correct header (see my earlier example on when <> and "" can locate different headers even when used within the same project). |
Opened #1666. |
The examples in SF.12 are likely to encourage readers to always use the `""` form of `'#include` when including headers from the same project ([discussion](isocpp#1596 (comment))). However, in larger projects this may not always be appropriate; `<>` should be used for includes located via a header search path. This proposed solution adds an example of the later, i.e. where `<>` is used to include a header from the same project.
Sorry, but it's not clear which form of Other practical matters aside, I find #include <mylib/foo.hpp> to be cleaner than #include <../include/mylib/foo.hpp> while considering the common case where *.cpp files live in a different directory than *.hpp files within the library project structure. |
I've grappled with this in my attempt of an answer at SO. Note that the question is specific to what library writers should do in their own library code. Comments are welcome. |
@ecorm the relevant example is here: I was able to land on the word 'project', and some confusion is merited here, because we did have the majority of feedback center around what to do for "library writers", and understandably, it is challenging to define what is a "library" or "project" in C++. We went with "project" because it's a bit more general than "library", but there really is no such concept in C++ for a good reason: the overarching principle is to not segment the language into "library mode" and "user mode". it's all the same code, we want the rules to apply uniformly. (even though yes, we tend to loosely think of "project" as a collection of highly cohesive code that is typically provided as a portable unit) In short that means the same rules for the library writer, as the user, as in the example above. |
that being said, it's also deliberately left up to the reader to interpret the degree to which "relative locality" applies, which speaks to your other point on SO re: at what point does one do "../../../unsightly/path/foo/foo.h" vs <foo/foo.h>. That also goes back to inability to say what constitutes a project (at which directory do you define the project split?) and intentionally leaves it up to the reader. |
@apenn-msft Thank for you the clarifications. Based on my current understanding, I'm now personally aiming to adopt the following convention:
The former makes it clear that I want to include a header file that's bundled with the same library as the file doing the including. The latter makes it possible for the library source (cpp) files to be compiled by being directly dropped into an application's build system, without having to maintain the same relative directory structure between the library's source and header files. It also avoids the When my library source files are being compiled using the library's CMake scripts (to generate a static/shared library), the CMake scripts are in control of the |
This could lead to usage of the #include directive that appears puzzlingly inconsistent. Imagine a library with headers having the following structure:
In combobox.hpp, you could have: #include "textbox.hpp"
#include <somelib/core/util.h> // as opposed to #include "../core/util.h" Both of those headers logically belong to the same "project" (i.e the "somelib" library), yet they are included via different forms. |
yep, consistency of intra-project include form is nice, but the implication of SF.12 is that it prioritizes portability and consistency of behavior across platforms where there is a trade-off. Where you can, use the more portable relative-local header look-up. If you can't (perhaps at the limit of unsightliness for ".."'s) then it's fine to fallback to using the <>-form. As you rightly point out, this may result in different include forms locating what should be the same header within the same project. SF.12 is saying it is more important to use the form that most uniquely locates a header, rather than the same form everywhere. |
… including file
The current guidance on SF.12 can be over-applied and devolves into "always use <>" because all compilers support adding include directories to the <> search. In this case, even the current directory may be added and so it is always possible to use <> for every header. Applying the guidance then devolves into an undesirable state where <> is always used and include"" is never used.
Instead, the proposed guidance leverages and encourages the distinction between <> and "" to create an easy-to-understand rule that the original guidance hints at and that most developers already follow and understand: "" is for local headers and <> is for library and external headers.