Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
301 views
in Technique[技术] by (71.8m points)

c preprocessor - C++ #include semantics

This is a multiple question for the same pre-processing instruction.

1 - <> or "" ?

Apart from the info found in the MSDN:

#include Directive (C-C++)

1.a: What are the differences between the two notations?
1.b: Do all compilers implement them the same way?
1.c: When would you use the <>, and when would you use the "" (i.e. what are the criteria you would use to use one or the other for a header include)?

2 - #include {TheProject/TheHeader.hpp} or {TheHeader.hpp} ?

I've seen at least two ways of writing includes of one's project headers. Considering that you have at least 4 types of headers, that is:

  • private headers of your project?
  • headers of your project, but which are exporting symbols (and thus, "public")
  • headers of another project your module links with
  • headers of a compiler or standard library

For each kind of headers:

2.a: Would you use <> or "" ?
2.b: Would you include with {TheProject/TheHeader.hpp}, or with {TheHeader.hpp} only?

3 - Bonus

3.a: Do you work on project with sources and/or headers within a tree-like organisation (i.e., directories inside directories, as opposed to "every file in one directory") and what are the pros/cons?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

After reading all answers, as well as compiler documentation, I decided I would follow the following standard.

For all files, be them project headers or external headers, always use the pattern:

#include <namespace/header.hpp>

The namespace being at least one directory deep, to avoid collision.

Of course, this means that the project directory where the project headers are should be added as "default include header" to the makefile, too.

The reason for this choice is that I found the following information:

1. The include "" pattern is compiler-dependent

I'll give the answers below

1.a The Standard

Source:

In the section 16.2 Source file inclusion, we can read that:

A preprocessing directive of the form

  #include <h-char-sequence> new-line

searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the < and > delimiters, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.

This means that #include <...> will search a file in an implementation defined manner.

Then, the next paragraph:

A preprocessing directive of the form

  #include "q-char-sequence" new-line

causes the replacement of that directive by the entire contents of the source file identified by the specified sequence between the " delimiters. The named source file is searched for in an implementation-defined manner. If this search is not supported, or if the search fails, the directive is reprocessed as if it read

  #include <h-char-sequence> new-line

with the identical contained sequence (including > characters, if any) from the original directive.

This means that #include "..." will search a file in an implementation defined manner and then, if the file is not found, will do another search as if it had been an #include <...>

The conclusion is that we have to read the compilers documentation.

Note that, for some reason, nowhere in the standards the difference is made between "system" or "library" headers or other headers. The only difference seem that #include <...> seems to target headers, while #include "..." seems to target source (at least, in the english wording).

1.b Visual C++:

Source:

#include "MyFile.hpp"

The preprocessor searches for include files in the following order:

  1. In the same directory as the file that contains the #include statement.
  2. In the directories of any previously opened include files in the reverse order in which they were opened. The search starts from the directory of the include file that was opened last and continues through the directory of the include file that was opened first.
  3. Along the path specified by each /I compiler option.
  4. (*) Along the paths specified by the INCLUDE environment variable or the development environment default includes.

#include <MyFile.hpp>

The preprocessor searches for include files in the following order:

  1. Along the path specified by each /I compiler option.
  2. (*) Along the paths specified by the INCLUDE environment variable or the development environment default includes.

Note about the last step

The document is not clear about the "Along the paths specified by the INCLUDE environment variable" part for both <...> and "..." includes. The following quote makes it stick with the standard:

For include files that are specified as #include "path-spec", directory searching begins with the directory of the parent file and then proceeds through the directories of any grandparent files. That is, searching begins relative to the directory that contains the source file that contains the #include directive that's being processed. If there is no grandparent file and the file has not been found, the search continues as if the file name were enclosed in angle brackets.

The last step (marked by an asterisk) is thus an interpretation from reading the whole document.

1.c g++

Source:

The following quote summarizes the process:

GCC [...] will look for headers requested with #include <file> in [system directories] [...] All the directories named by -I are searched, in left-to-right order, before the default directories

GCC looks for headers requested with #include "file" first in the directory containing the current file, then in the directories as specified by -iquote options, then in the same places it would have looked for a header requested with angle brackets.

#include "MyFile.hpp"

This variant is used for header files of your own program. The preprocessor searches for include files in the following order:

  1. In the same directory as the file that contains the #include statement.
  2. Along the path specified by each -iquote compiler option.
  3. As for the #include <MyFile.hpp>

#include <MyFile.hpp>

This variant is used for system header files. The preprocessor searches for include files in the following order:

  1. Along the path specified by each -I compiler option.
  2. Inside the system directories.

1.d Oracle/Sun Studio CC

Source:

Note that the text contradict itself somewhat (see the example to understand). The key phrase is: "The difference is that the current directory is searched only for header files whose names you have enclosed in quotation marks."

#include "MyFile.hpp"

This variant is used for header files of your own program. The preprocessor searches for include files in the following order:

  1. The current directory (that is, the directory containing the “including” file)
  2. The directories named with -I options, if any
  3. The system directory (e.g. the /usr/include directory)

#include <MyFile.hpp>

This variant is used for system header files. The preprocessor searches for include files in the following order:

  1. The directories named with -I options, if any
  2. The system directory (e.g. the /usr/include directory)

1.e XL C/C++ Compiler Reference - IBM/AIX

Source:

Both documents are titled "XL C/C++ Compiler Reference" The first document is older (8.0), but is easier to understand. The second is newer (12.1), but is a bit more difficult to decrypt.

#include "MyFile.hpp"

This variant is used for header files of your own program. The preprocessor searches for include files in the following order:

  1. The current directory (that is, the directory containing the “including” file)
  2. The directories named with -I options, if any
  3. The system directory (e.g. the /usr/vac[cpp]/include or /usr/include directories)

#include <MyFile.hpp>

This variant is used for system header files. The preprocessor searches for include files in the following order:

  1. The directories named with -I options, if any
  2. The system directory (e.g. the /usr/vac[cpp]/include or /usr/include directory)

1.e Conclusion

The pattern "" could lead to subtle compilation error across compilers, and as I currently work both on Windows Visual C++, Linux g++, Oracle/Solaris CC and AIX XL, this is not acceptable.

Anyway, the advantage of "" described features are far from interesting anyway, so...

2. Use the {namespace}/header.hpp pattern

I saw at work (i.e. this is not theory, this is real-life, painful professional experience) two headers with the same name, one in the local project directory, and the other in the global include.

As we were using the "" pattern, and that file was included both in local headers and global headers, there was no way to understand what was really going on, when strange errors appeared.

Using the directory in the include would have saved us time because the user would have had to either write:

#include <MyLocalProject/Header.hpp>

or

#include <GlobalInclude/Header.hpp>

You'll note that while

#include "Header.hpp"

would have compiled successfully, thus, still hiding the problem, whereas

#include <Header.hpp>

would not have compiled in normal circonstances.

Thus, sticking to the <> notation would have made mandatory for the developer the prefixing of the include with the right directory, another reason to prefer <> to "".

3. Conclusion

Using both the <> notation and namespaced notation together removes from the pre-compiler the possibility to guess for files, instead searching only the default include directories.

Of course, the standard libraries are still included as usual,


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...