-
Notifications
You must be signed in to change notification settings - Fork 397
File and Module Structure
- C++ source file should be named
.cpp
or.cc
NOT.c
- C++ header files should be named
.hpp
or.hh
NOT.h
Header files must contain a distinctly-named "include guard" to avoid problems with including the same header multiple times or conflicting with other headers from other projects.
#ifndef MYPROJECT_MYMODULE_HPP
#define MYPROEJCT_MYMODULE_HPP
namespace MyProject {
class MyClass {
};
}
#endif // MYPROJECT_MYMODULE_HPP
It's good hygiene to add a comment at the end of #endif
lines to document which #ifdef
it is closing.
... <>
is reserved for system includes.
// Bad Idea. Requires extra -I directives to the compiler and goes against standards
#include <string>
#include <includes/MyHeader.hpp>
// Worse Idea
// requires potentially even more specific -I directives and makes code more difficult to package and distribute
#include <string>
#include <MyHeader.hpp>
// Good Idea
// requires no extra params and notifies the user that the file is a local file
#include <string>
#include "MyHeader.hpp"
[SM] The reason for <path/MyHeader.hpp>
is two-fold:
-
Search semantics for "MyHeader.hpp" are not fully specified in the language and thus create a potential portability problem. Search with
<>
is precisely specified. This is particularly important for cross-platform projects where you may want precise override control to pick up different headers depending on the platform/compiler (for a cleaner more maintainable approach than an#ifdef
mess). - Putting a namespace path in front of the header file name prevents header name collisions between the application and a library or 2 application namespaces.
-
These reasons are why projects like Boost use the
<boost/header.hpp>
style.
[JT] Having just grepped through the boost source: there is a surprising amount of inconsistency within boost. Practically speaking, I have never seen a portability issue with using ""
for includes, and have seen it solve far more problems then it has created. However if we do use the <>
includes as you suggest, we need to be consistent and use fully scoped paths, like boost does, (real world example) boost/log/expressions/predicates.hpp:#include <boost/log/expressions/predicates/channel_severity_filter.hpp>
Including header files from other header files increases compilation time and is considered bad practice. Better practice is to include header files in code files in "topological order" such that header files are always included after the header files that they themselves need.
There is another good way to minimize the inclusion of header files from other files. If you only need to reference a type that is defined in another header file, i.e., you are declaring a function that has a reference or pointer parameter of that type, then you can simply "forward declare" that type without including the header file itself. If you are only using a pointer or a reference to a type and never using it by value or looking into it, then the compiler doesn't actually need to know the definition of the object.
For example, EnergyPlus now passes a struct EnergyPlusData & state
parameter to essentially every function and method call. It would stand to reason therefore that every header file needs to include Data/EnergyPlusData.hh
where the struct EnergyPlusData
is defined. However, this is sufficient.
#ifndef MYPROJECT_MYMODULE_HPP
#define MYPROJECT_MYMODULE_HPP
namespace MYMODULE {
struct EnergyPlusData; // No need to include the header file that defines this if all you are doing is declaring it as a reference argument
void myFunction(struct EnergyPlusData & state);
Note, this does not work if you actually have to include an instance of EnergyPlusData
as a field in another type, if you had to create a variable of type EnergyPlusData
, or if EnergyPlusData
were a template. If either of these were the case, the compiler would have to know the size and structure of EnergyPlusData
and you would have to include the Data/EnergyPlusData.hh
header in this header.
There is almost never a reason to declare an identifier in the global namespaces. Instead, functions and classes should exist in an appropriately named namespaces or in a class inside of a namespace. Identifiers which are placed in the global namespace risk conflicting with identifiers from other (mostly C, which doesn't have namespaces) libraries.
This causes the name space you are using
to be pulled into the namespace of the header file.
This reduces the code readability.