I'm currently facing the same decision with a library I'm working on, which is intended to be consumed by projects other than my own. None of the other answers here address the practicality of quoted vs angular form of includes within libraries themselves, but merely explain the technical differences between the two forms.
There seems to be a divide into which include form is best for library sources/headers including headers from the same library. I don't think I'm qualified to give the Ultimate Answer. What I'm going to do here instead is attempt to summarize the pros and cons of each approach as best as I can, and link to some resources I found online.
The C++ Core Guidelines [1] has this to say under SF.12:
Library creators should put their headers in a folder and have clients include those files using the relative path #include <some_library/common.h>
The advantages of the angular form listed below assume that a library's headers are put into such a "root" folder named after the library (I hate it when libraries don't do that).
Unfortunately, the C++ Core Guidelines do not explicitly state what library creators should do when including their own headers from within their own library source/header files. However, a contributor kindly clarified to me in [4] that in their example:
#include "foo_utils/utils.h"
// A file locally relative to foo.cpp in the same project, use the "" form
the term project also applies to libraries. That contributor also clarified that "locally relative" was left up to the reader so that instances of #include "../../include/somelib/foo.hpp"
could be interpreted as non-locally relative so that #include <somelib/foo.hpp>
may be used instead.
Note that the C++ Core Guidelines, co-authored by Stroustrup, somewhat contradicts the AV Rule 33 of Stroutrup's earlier JSF coding standards [2] when it comes to the preferred #include
form. Perhaps he changed his views regarding this.
Advantages of the #include <somelib/foo.hpp>
approach:
- As discussed in [3], it allows the user to supply their own patched library header files by putting them in a directory that's searched earlier, via
-I
or -isystem
compiler flags. Patching via this method is probably only viable for header-only libraries.
- If one of your library's headers is named the same as one of the system's headers, then it's less confusing which one you're referring to. e.g.:
#include <somelib/float.h>
vs #include "float.h"
, where float.h happens to be a C standard library header.
- Avoids relying on the inconsistent behavior of the quoted form among compilers, and conforms to AV Rule 33 of Stroutrup's JSF coding standards [2]. This may not be a problem if the library is not aiming to be portable across many compilers.
- It avoids ugliness when the included file lives in a parent or sibling directory of the including file. E.g.
#include <somelib/foo/bar.hpp>
vs #include "../foo/bar.hpp"
. Also consider the case where the source and header files live in different directories of the library project: #include <somelib/foo/bar.hpp>
vs #include "../include/foo/bar.hpp"
when including from a library cpp file.
- When used in the library's cpp files, it allows the user to simply embed the library's cpp files into the application's build system, and not have to maintain the same relative directory structure between the library's source and header files. This may be desirable if the library consists of only one (or a few) cpp files, and the application project doesn't want to use the build method provided by the library. For example, an application project uses bazel, but the library only provides a CMake build.
Advantages of the #include "foo.hpp"
approach:
- Makes it clear to the reader that the file to be included is part of the same "project" as the including file.
- If a user has both locally-installed and system-installed versions of the library, it can avoid the system-installed header being accidentally included if the user neglected to set the proper
-I
or -isystem
flags.
- If a beginner user doesn't know how to set up
-I
or -isystem
flags properly to point to a local version of the library, they can just do #include "../dependencies/somelib/include/somelib/foo.hpp"
(which I find terribly ugly - perhaps irrationally).
Based on the above research and on the kind help from the C++ Core Guidelines contributor, I'm now personally aiming to adopt the following convention:
- When a library header file includes another header of the same library: Use
#include "foo.h"
, #include "bar/foo.hpp"
, or #include "../bar/foo.hpp"
, depending on the relative location of the other header file.
- When a library source (cpp) file includes one of the library's own headers: Use
#include <somelib/foo.hpp>
or #include <somelib/bar/foo.hpp>
.
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 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 #include "../include/somelib/foo.hpp"
ugliness in favor of the cleaner #include <somelib/foo.hpp>
.
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 -I
and -isystem
flags and can thus ensure that the correct library headers path will be given top search priority.
[1] https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rs-incform
[2] https://www.stroustrup.com/JSF-AV-rules.pdf
[3] https://lists.boost.org/Archives/boost//2008/09/142030.php
[4] https://github.com/isocpp/CppCoreGuidelines/pull/1596#issuecomment-1113901271