Jake Warmerdam C++ Style Guide
Background
This is a simple coding standard used for my personal projects. It is not a list of C++ best practices; rather it is a set of naming and formatting conventions intended to produce code that is more readable, maintainable, and attractive.
Naming
Prefixes
Globals, constants, and types use specific prefixes:
- c_ - class
- s_ - struct
- e_ - enumeration
- g_ - global
- k_ - constants
- h_ - handle
- m_ - member
General Naming Rules
- Function names, variable names, and file names should be clear and descriptive.
- Names should be all lowercase, with words separated by underscores:
int example_variable;
- Simple loop indices can use i,j,k. However, prefer iterator loops to indexed loops.
- Common abbreviations are also acceptable as part of a name, such as id, ptr, min, max, func.
- Do not make up new abbreviations.
- Class members must be prefixed with m_, but this is not required for structs.
- Use structs to hold simple data. When in doubt, use a class.
- Some specific standards:
- Variables and functions dealing with numbers of items in a list should use the word count:
int m_player_count;
- Getters and setters should be prefixed with
get_
andset_
. - Functions that do non-trivial work before returning should start with
compute_
. - Functions that may return
nullptr
should start withtry_to_
. - Enumeration values should begin with an underscore,
with the exception of values that are meant to be markers,
in which case they should be named like constants:
k_
.
- Variables and functions dealing with numbers of items in a list should use the word count:
static const int k_max_monster_count = 100; bool g_test_mini_monsters = true; enum e_monster_flavor { _monster_flavor_a, _monster_flavor_b, }; class c_monster { public: e_monster_flavor get_flavor() const { return m_flavor; } float compute_mass() const; const c_monster* try_to_find_enemy() const; private: e_monster_flavor m_flavor; h_monster m_spouse; int m_eyeball_count; }; struct s_health_data { float current_health; float max_health; };
File Names
- File names for class implementations should use the same name as the class, minus any prefix.
- Other files should follow the naming convention for variables: lowercase, words spaced with underscores.
- C++ files should end in
.cpp
and header files should end in.h
.
Formatting and Style
Braces and Whitespace
- Beginning braces occupy their own line. Brace pairs line up vertically.
else
andelse if
occupy their own line.- Use a space after the keywords
if
,for
,while
, andswitch
. - Do not include any extra whitespace between parentheses and the code inside.
- Always leave a space after a comma, and after semi-colons in
for
conditions. - Use a space before and after the equals sign in assignment.
- Use a single space around most operators, unless there is a compelling reason not to.
- Leave a blank line after all code blocks, unless the following line is an ending brace.
- Leave a blank line after variable declaration groups.
- Always use braced blocks, even for blocks with a single line.
- In pointer declarations, the
*
goes next to the type, not the name:int* my_int;
- Declare a single variable per line.
int compute_range_sum(int first, int last) { int result = 0; if (last < first) { std::swap(first, last); } for (int i = first; i <= last; ++i) { result += i; } return result; }
case:
blocks should always use braces, and use the following style:
switch (...) { case 0: { //... break; } default: { break; } }
(condition) ? result_a : result_b;
Clarity
- If there could be any doubt about precedence, use parentheses to clarify. Be liberal, but do not obfuscate the code.
- Functions should usually have a single return statement.
- In complex boolean conditionals, prefer defining named variables for each part:
const bool is_alive = (health > 0); const bool is_angry = (get_mood() == _mood_angry); if (is_alive && is_angry) { //... }
- Variables that are only assigned to once should be marked
const
. - Avoid default arguments.
- Function overloading is fine if the specific overload being called will be clear at the call site.
- If a
case:
block must fall through to the nextcase:
, this must be decorated withfallthrough
. - Never use the ternary operator twice in one expression.
- Avoid deep nesting.
- Avoid "clever" code, which is usually written in the name of efficiency or brevity, at the expense of clarity (and, often, correctness).
- Always use C++ casts, such as
static_cast<>()
. - Use prefix form (
++i
) of the increment and decrement operators with iterators and other template objects. Avoid all use of postfix form. - Use
const
liberally to clarify intent behind variable, parameter, and function usage. Viral nature ofconst
is considered a good thing. - In variable and parameter declarations, place
const
before the type. - Avoid complicated template meta-programming.
Brevity
- Clarity and brevity usually go hand in hand, but err on the side of clarity.
- Brevity is for the sake of readability, not reducing typing. Most programming time is not spent typing!
- Prefer concise focused functions.
- Functions returning
void
should omit the unnecessary finalreturn;
- In loops,
break;
andcontinue;
are allowed, but discouraged. - Use
auto
to avoid type names that are just clutter. Use type declarations when it helps readability. - Use lambda expressions where appropriate.
- Write all captures explicitly.
- Be very cautious with macros. Prefer inline functions, enums, and
const
variables to macros. - Try to not
#define
macros in a.h
file. #define
macros just before use, and#undef
them after.
Local Variables
- Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
int i; i = f(); // Bad -- initialization separate from declaration.
const int j = g(); // Good -- declaration has initialization.
vector<int> v; v.push_back(1); // Prefer initializing using brace initialization. v.push_back(2);
vector<int> v = {1, 2}; // Good -- v starts initialized.
Variables needed for if
, while
,
and for
statements should normally be declared
within those statements, so that such variables are confined
to those scopes:
while (const char* p = strchr(str, '/')) { str = p + 1; }
if (const c_thing* c = some_func()) { // ... use c; }
Function Parameters
- When defining a function, parameter order is: inputs, then outputs.
- Input parameters are usually values or
const
references, while output and input/output parameters will be non-const
references. - Avoid the use of out and in/out parameters when possible. Return a structure instead.
Header Files and Includes
- In general, every
.cpp
file should have an
associated .h
file.
- Header files should be self-contained and end in
.h
. Files that
are meant for textual inclusion, but are not headers, should end in
.inc
.
- All header files should be self-contained. No special
conditions should be required in order to include the header.
- Use
#pragma once
in header files to
prevent multiple inclusion.
- Order for includes: related header, external headers, project headers.
- Do not use
./
or ../
in
include paths.
- Within each section the includes should be ordered
alphabetically.
- In general, every
.cpp
file should have an associated.h
file. - Header files should be self-contained and end in
.h
. Files that are meant for textual inclusion, but are not headers, should end in.inc
. - All header files should be self-contained. No special conditions should be required in order to include the header.
- Use
#pragma once
in header files to prevent multiple inclusion. - Order for includes: related header, external headers, project headers.
- Do not use
./
or../
in include paths. - Within each section the includes should be ordered alphabetically.
Revision 1.0