Skip to content
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

library writers should include their own headers as users of their library would #1609

Closed
apenn-msft opened this issue Apr 18, 2020 · 5 comments

Comments

@apenn-msft
Copy link
Contributor

apenn-msft commented Apr 18, 2020

Edit:
This is a follow up from a comment on e47463c

Where library writers are including their own headers, they should prefer to do so the same way they would expect their users to. So for example, if you are creating foo.h and bar.h as part of some_lib, even though you could include bar.h locally as #include "bar.h" your users will not have bar.h at this local path. Instead, if you would expect your users to consume foo.h as "#include <author/library/foo.h>" then from foo.h, you should include bar.h as "#include <author/library/bar.h>"

Taking this guidance (that we should add to the GSL) combined with my PR to SF.12 this then translates succinctly to "if you're writing a library, you must use the angle-bracket form" because it encapsulates that if you're using the <> form it implies you are not including via the local relative path. From the original post:

If you're writing a library, you must use the angle-bracket form:

// inside mine/mine.h
#include <mine/detail.h>

This line will work no matter whether mine/mine.h is located in ~/sourcetree/src/ or in /usr/include — basically, if anybody ever includes this file via #include <mine/mine.h>, then we automatically know that the exact same include-search-path will be able to find <mine/detail.h>. On the other hand, doing #include "detail.h" might or might not work (depending on compiler options that ought to be irrelevant to us as library writers).

Originally posted by @Quuxplusone in #1596 (comment)

@apenn-msft
Copy link
Contributor Author

I opened this issue from feedback on SF.12:
#1596 (comment)

We need a new guideline to cover how library creators should manage their headers and includes:

  1. library headers should exist under a unique path like author/library/header.h
  2. library headers should reference other library headers using the path in Integrate the parameter passing guidance #1, just as the library users would, even if those same headers can be accessed locally for the library creator (because they won't exist locally for their users).

I did not add it to SF.12 because SF.12 tells you what to use if you're accessing a file at a local path. it does not tell you whether or not you should access a file from the local path or <> search path (typically there's not ambiguity and there is only one file that exists, but if there is multiple, the user must decide what they want; however this is not the case for library creators who reference their own headers... they must choose to reference them from the same path their users will)

@MikeGitb
Copy link

(because they won't exist locally for their users).

So what? Even installed, the relative paths between the headers inside a library usually remain the same, so there is no reason why a library header must use the angle bracket form.

Personally I don't have a strong feeling one way or the other and never had to decide this convention for any project of significant size. However I will say, that with the #include <same_lib/foo.h> style I always was a little bit afraid that headers of different versions could mix (e.g. the header of a local installation of libFoo happens to include the system installed version or vice versa.

@apenn-msft
Copy link
Contributor Author

apenn-msft commented Apr 18, 2020

I should clarify that this stems as a corollary from
https://github.com/isocpp/CppCoreGuidelines/pull/1596/commits

Without that PR, the title could be more along the lines of "the library writer must include their headers along the user-defined search path and not the local search available to the library writer"

the relative paths between the headers inside a library usually remain the same

the relative paths may remain the same, but those relative paths are not available to the user:
the library writer has:
some_lib/foo.h
some_lib/bar.h

the writer can include bar.h from foo.h as #include "bar.h". but then it will break user code:
usercode.cpp:
#include <some_lib/foo.h>

because when user code sees the statement #include "bar.h" from foo.h, it has no such include "bar.h". Note, the user should not modify the include search path so that #include "bar.h" actually resolves because if they did that, then all libraries providing "bar.h" would collide. (instead, the recommendation as in the above example is to modify the search path so that the header is found uniquely under a namespace like "author/library/header.h")

Instead, the library writer must include bar.h as the user would:
foo.h:
#include <some_lib/bar.h>

This way, when user code includes foo.h the include of bar.h will be found in the same way the user found foo.h.

My proposed changes to SF.12 are to use <> for user includes and "" for local includes because this resolves disambiguity of what users intended (did you mean to include from the header search path, or locally?)

Taking both guidelines together (this issue and the proposed SF.12 PR), you get "library writers should always include their headers using <>"

@apenn-msft apenn-msft changed the title If you're writing a library, you *must* use the angle-bracket form: library writers should include their own headers as users of their library would Apr 18, 2020
@MikeGitb
Copy link

the relative paths may remain the same, but those relative paths are not available to the user:
the library writer has:
some_lib/foo.h
some_lib/bar.h

the writer can include bar.h from foo.h as #include "bar.h". but then it will break user code:
usercode.cpp:
#include <some_lib/foo.h>

Have you actually verified your claim? Either I completely misunderstand you or this is plainly not true. Relative include statements are resolved always relative to the header in which they are written. Doesn't matter how that particular header was found in the first place.

@apenn-msft
Copy link
Contributor Author

Yes, you're right. I'm way off here and I'll need follow up with the original poster (Quux) on what they meant. I may have been misinterpreting this behavior from that. or I may have been thinking of an unrelated common practice like:
some_lib structure:
src/foo.cpp
inc/author/some_lib/ifoo.h
foo.cpp should consume ifoo.h as #include <author/some_lib/ifoo.h>

but that neither has impact on SF.12 or this issue at hand.
while I've seen libraries do this, and I do find it to be good practice (serves as a good example on how to use the library headers), I wouldn't say either practice is good or bad or that either one would be a guideline.

I can see your point for doing it the other way -- at least if foo.h includes "bar.h" it can be assured it is the bar collocated with foo.h versus picking up whatever bar.h happens to live at <author/lib/bar.h> (although this would very likely be the same bar.h)

Thanks for making me work the example. I'm going to close this issue and circle back with the original poster.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants