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
nullptrshould 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
.cppand header files should end in.h.
Formatting and Style
Braces and Whitespace
- Beginning braces occupy their own line. Brace pairs line up vertically.
elseandelse ifoccupy 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
forconditions. - 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
constliberally to clarify intent behind variable, parameter, and function usage. Viral nature ofconstis considered a good thing. - In variable and parameter declarations, place
constbefore 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
voidshould omit the unnecessary finalreturn; - In loops,
break;andcontinue;are allowed, but discouraged. - Use
autoto 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
constvariables to macros. - Try to not
#definemacros in a.hfile. #definemacros just before use, and#undefthem 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
constreferences, while output and input/output parameters will be non-constreferences. - 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
.cppfile should have an associated.hfile. - 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 oncein 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