This guide documents the style conventions used in C/C++ repositories by @igkiou. Style in this case refers to both source file formatting and coding and design practices.
The adopted style conventions are strongly influenced by the Google C++ Style Guide and the styling conventions of the Mitsuba Physically-Based Renderer. With respect to the Google C++ Style Guide in particular, it is by default assumed that it applies, unless explicitly contradicted by a convention described below. For this reason, the emphasis of this (evolving) guide is on differences the Google C++ Style Guide. In addition to describing the adopted conventions, some effort has been put into also explaining the rationale behind them.
To help enforce these conventions, the guide is accompanied by an Eclipse CDT-importable template that partially implements it. Furthermore, given the large overlap with the Google C++ Style Guide, cpplint can be used to identify violations of this guide's conventions. Due to differences with the Google C++ Style Guide, it is recommended that cpplint
be called with the following arguments,
./cpplint.py –filter=-whitespace/tab,-whitespace/labels
Enumeration types have a second to last length entry that is set equal to the number of valid entries (without the length entry), and a last invalid entry that is set equal to -1
.
Only scoped enums are used. The practice of using length and invalid entries (see previous) results in name collisions when unscoped enum are used.
Boost libraries should be used whenever this helps save time or write more compact code.
When it is necessary to indicate that some names should not be used outside a header file, the corresponding definitions or declarations are placed inside a detail
namespace. In most cases, they are instead placed in an implementation file inside an unnamed namespace, but a detail
namespace may be necessary in some cases (e.g., in header-only implementations). Unnamed namespaces are not used in header files.
When it is necessary to provide a destructor, a destructor defaulted using = default
is preferred to an empty destructor.
TODO.
Indentation is done using tabs, which expand to four spaces. One "indent" is one tab.
When it is necessary to wrap a line starting a new block scope (e.g., a for
statement), any subsequent lines have at least two additional indents relative to the first line. This is to distinguish them from the code block that will follow, which has only one additional indent.
When it is necessary to wrap a function's parameters or arguments, they are wrapped at the maximum number of indents that is no more that one character to the right of the first character of the parameter or argument list. If this is impractical, they are wrapped at two additional indents.
When it is necessary to wrap initializer lists, they are formatted the same as parameter and argument lists. The colon :
starting the initializer list should be in the same line as the end of the constructor parameter name.
The asterisk and ampersand are placed adjacent to the type.
The public
, protected
, and private
keywords are at the same indentation level as the class
keyword.
Header files end in .h
, and C/C++ implementation files in .cpp
.
Names are always written in camel-case, sometimes with the addition of a prefix consisting of lower-case characters followed by an underscore, and with the exception for macros. These are described in detail below.
Classes and structures use camel-case starting with an upper-case letter.
Type names use camel-case starting with an upper-case letter.
Variables use camel-case starting with a lower-case letter.
Functions use camel-case starting with a lower-case letter.
Macros (which should be used scarcely) use upper-case letters with underscores separating words.
Enumeration types and entries start with an upper-case E (E-type
). The length entry has the name ELength
, and the invalid entry has the name EInvalid
. Variables that are of a type of an enumeration start with e (e-type
). This is to make these types and variables easier to recognize.
Functions returning bool
and variables of type bool
have names corresponding to the binary (true/false) question their value answers. Most of the time, this means starting the name with is
(b-type
).
Compile-time constants start with k (k-type
). This is to enhance recognizability of these variables.
Type names that are defined as pointers to some other type start with an upper-case P (P-type
). Variables that are of type pointer to some type start with p (p-type
). This is to enhance recognizability of these types and variables. Note that a variable that is of a P-type
type is not a p-type
variable.
Attributes of classes and structs have the prefix m_
. This is to enhance recognizability, but also to facilitate maintenance, by making exploring the attributes of a class straightforward (e.g., type m_
, and auto-complete).
Accessor ("getter") and mutator ("setter") member functions of a class are named by concatenating the prefix get_
and set_
, respectively, and the part of the name of the attribute they provide access to after its m_
prefix. This is to enhance recognizability, but also to facilitate maintenance, by making exploring functions that provide access to attributes of a class straightforward (e.g., type get_
or set_
, and auto-complete). In mutators, the parameter is named after the attribute it is assigned to, without the m_
prefix.
In copy and move constructors, the parameter is named other
. This is to make facilitate reuse of standard forms of these constuctors (e.g., copy-and-swap idiom) and refactoring. In other constructors, function parameters are named after the attributes they are assigned to, without the m_
prefix.
In copy and move operators, the parameter is named other
.
When extending swap
, the two parameters are named first
and second
. Functions with functionality analogous to swap
have similarly named parameters. When defining functions with cloning or copying functionality (e.g., private implementations of copy constructors), the parameter that is cloned is named other
.
When a variable is declared both static
and const
, the static
keyword is the leftmost one.
Below is an example demonstrating many of the above conventions.
// Bad practice, used only for demonstration of macro naming convention.
#define NUM_DAYS_OF_YEAR 360
enum class EMonth {
EJanuary = 0,
EFebrurary,
/* ... */
EDecember,
ELength,
EInvalid = -1
};
static const int kNumOfDays = 30;
struct Date {
public:
int m_day;
EMonth m_eMonth;
int m_year;
};
bool isValidDate(const Date& query);
typedef Date* PDate;
class LogEntry {
public:
LogEntry(const PDate& entryDate, double* pEntryData,
const std::string& entryText) :
m_entryDate(entryDate),
m_pEntryData(pEntryData),
m_entryText(entryText) {}
LogEntry(const LogEntry& other);
LogEntry& operator=(const LogEntry& other);
inline const PDate& get_entryDate() const {
return m_entryDate;
}
inline const double* get_pEntryData() const {
return m_pEntryData;
}
inline void set_entryDate(const PDate& entryDate) {
m_entryDate = entryDate;
}
inline void set_pEntryData(double* pEntryData) {
m_pEntryData = pEntryData;
}
void printLogEntry() const;
private:
PDate m_entryDate;
double* m_pEntryData;
std::string m_entryText;
};