Mir C++ Style Guide

Revision 4.2

Tim Penhey
Neil J. Patel
Thomas Voss

Each style point has a summary for which additional information is available by toggling the accompanying arrow button that looks this way: . You may toggle all summaries with the big arrow button:

Toggle all summaries
Table of Contents

Important Note

Displaying Hidden Details in this Guide

link
This style guide contains many details that are initially hidden from view. They are marked by the triangle icon, which you see here on your left. Click it now. You should see "Hooray" appear below.

Background

As every C++ programmer knows, the language has many powerful features, but this power brings with it complexity, which in turn can make code more bug-prone and harder to read and maintain.

The goal of this guide is to manage this complexity by describing in detail the dos and don'ts of writing C++ code. These rules exist to keep the code base manageable while still allowing coders to use C++ language features productively.

Style, also known as readability, is what we call the conventions that govern our C++ code. The term Style is a bit of a misnomer, since these conventions cover far more than just source file formatting.

One way in which we keep the code base manageable is by enforcing consistency. It is very important that any programmer be able to look at another's code and quickly understand it. Maintaining a uniform style and following conventions means that we can more easily use "pattern-matching" to infer what various symbols are and what invariants are true about them. Creating common, required idioms and patterns makes code much easier to understand. In some cases there might be good arguments for changing certain style rules, but we nonetheless keep things as they are in order to preserve consistency.

Another issue this guide addresses is that of C++ feature bloat. C++ is a huge language with many advanced features. In some cases we constrain, or even ban, use of certain features. We do this to keep code simple and to avoid the various common errors and problems that these features can cause. This guide lists these features and explains why their use is restricted.

Note that this guide is not a C++ tutorial: we assume that the reader is familiar with the language.

Header Files

In general, every .cpp file should have an associated .h file. There are some common exceptions, such as unit tests and small .cpp files containing just a main() function.

Correct use of header files can make a huge difference to the readability, size and performance of your code.

The following rules will guide you through the various pitfalls of using header files.

The #define Guard

link
All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be <PROJECT>_<PATH>_<FILE>_H_.

Header File Dependencies

link
Don't use an #include when a forward declaration would suffice.

Inline Functions

link
Define functions inline only when they are small, say, 10 lines or less.

The -inl.h Files

link
You may use file names with a -inl.h suffix to define complex inline functions when needed.

Function Parameter Ordering

link
When defining a function, parameter order is: outputs, then inputs.

Names and Order of Includes

link
Use standard order for readability and to avoid hidden dependencies: your project's public .h, your project's private .h, other libraries' .h, .C library, C++ library,

Scoping

Namespaces

link
Unnamed namespaces in .cpp files are encouraged. With named namespaces, choose the name based on the project, and possibly its path. Do not use a using-directive in a header file.

Nested Classes

link
Although you may use public nested classes when they are part of an interface, consider a namespace to keep declarations out of the global scope.

Nonmember, Static Member, and Global Functions

link
Prefer nonmember functions within a namespace or static member functions to global functions; use completely global functions rarely.

Local Variables

link
Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.

Classes

Classes are the fundamental unit of code in C++. Naturally, we use them extensively. This section lists the main dos and don'ts you should follow when writing a class.

Constructors

link
The purpose of a constructor is to initialise a class so that its invariants hold. For value classes it is worth having a cheap default constructor.

Default Constructors

link
You must define a default constructor if your class defines member variables of POD types and has no other constructors. Otherwise the compiler will do it for you, badly.

Explicit Constructors

link
Use the C++ keyword explicit for constructors with one argument.

Copy Constructors

link
Provide a copy constructor and assignment operator only when necessary. Otherwise, disable them with the help of = delete;.

Structs vs. Classes

link
Use a struct only for passive objects that carry data; everything else is a class.

Inheritance

link
Composition is often more appropriate than inheritance. When using inheritance, make it public.

Multiple Inheritance

link
Only very rarely is multiple inheritance of implementation actually useful. We allow multiple inheritance only when at most one of the base classes has an implementation; all other base classes must be interface classes.

Interfaces

link
Classes that satisfy certain conditions are interfaces.

Operator Overloading

link
Overload operators where appropriate.

Access Control

link
Make data members private, and provide access to them through accessor functions as needed (for technical reasons, we allow data members of a test fixture class to be protected when using Google Test). Typically a variable would be called foo and the accessor function get_foo(). You may also want a mutator function set_foo(). Exception: static const data members need not be private.

Declaration Order

link
Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.

Write Short Functions

link
Prefer small and focused functions.

Other C++ Features

Reference Arguments

link
Most parameters passed by reference should be labeled const.

Function Overloading

link
Use overloaded functions (including constructors) only if a reader looking at a call site can get a good idea of what is happening without having to first figure out exactly which overload is being called.

Default Arguments

link
We do not allow default function parameters, except in a few uncommon situations explained below.

Variable-Length Arrays and alloca()

link
We do not allow variable-length arrays or alloca().

Friends

link
We allow use of friend classes and functions, within reason.

Casting

link
Use C++ casts like static_cast<>(). Do not use other cast formats like int y = (int)x; or int y = int(x);.

Streams

link
Use streams only for logging.

Preincrement and Predecrement

link
Use prefix form (++i) of the increment and decrement operators with iterators and other template objects.

Use of const

link
We strongly recommend that you use const whenever it makes sense to do so.

Integer Types

link
Use built-in C++ integer types, both signed and unsigned int. Use more specific types like size_t where appropriate. If a program needs a variable of a different size, use a precise-width integer type from <cstdint>, such as int16_t.

64-bit Portability

link
Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.

Preprocessor Macros

link
Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.

0 and NULL

link
Use 0 for integers, 0.0 for reals, nullptr for pointers, and '\0' for chars.

sizeof

link
Use sizeof(varname) instead of sizeof(type) whenever possible.

link
Use C++11 features wherever appropriate.

Naming

The most important consistency rules are those that govern naming. The style of a name immediately informs us what sort of thing the named entity is: a type, a variable, a function, a constant, a macro, etc., without requiring us to search for the declaration of that entity. The pattern-matching engine in our brains relies a great deal on these naming rules.

Naming rules are pretty arbitrary, but we feel that consistency is more important than individual preferences in this area, so regardless of whether you find them sensible or not, the rules are the rules.

General Naming Rules

link
Function names, variable names, and filenames should be descriptive; eschew abbreviation. Types and variables should be nouns, while functions should be "command" verbs.

File Names

link
Filenames should be all lowercase and can include underscores (_) or dashes (-). Follow the convention that your project uses. If there is no consistent local pattern to follow, prefer "_".

Type Names

link
Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum.

Variable Names

link
Variable names are all lowercase, with underscores between words. Class member variables follow this convention. For instance: my_exciting_local_variable, my_exciting_member_variable.

Constant Names

link
Name constants like other variables, using all lowercase, with underscores between words. default_width.

Function Names

link
Regular functions, accessors, and mutators are all lowercase, with underscores between words.

Namespace Names

link
Namespace names are all lower-case, and based on project names and possibly their directory structure: my_awesome_project.

Enumerator Names

link
Enumerators should be named like member variables: out_of_memory, enclosed within an enum class.

Macro Names

link
You're not really going to define a macro, are you? If you do, they're like this: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.

Exceptions to Naming Rules

link
If you are naming something that is analogous to an existing C or C++ entity then you can follow the existing naming convention scheme.

Comments

Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.

When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!

Comment Style

link
Use either the // or /* */ syntax, as long as you are consistent.

File Comments

link
Start each file with a copyright notice, followed by a description of the contents of the file.

Class Comments

link
Every class definition should have an accompanying comment that describes what it is for and how it should be used.

Function Comments

link
Declaration comments describe use of the function; comments at the definition of a function describe operation.

Variable Comments

link
In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.

Implementation Comments

link
In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.

Punctuation, Spelling and Grammar

link
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.

TODO Comments

link
Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.

Deprecation Comments

link
Mark deprecated interface points with DEPRECATED comments.

Formatting

Coding style and formatting are pretty arbitrary, but a project is much easier to follow if everyone uses the same style. Individuals may not agree with every aspect of the formatting rules, and some of the rules may take some getting used to, but it is important that all project contributors follow the style rules so that they can all read and understand everyone's code easily.

Line Length

link
Each line of text in your code should be at most 120 characters long.

Non-ASCII Characters

link
Non-ASCII characters should be rare, and must use UTF-8 formatting.

Spaces vs. Tabs

link
Use only spaces, and indent 4 spaces at a time.

Function Declarations and Definitions

link
void or auto on the same line as function name, parameters and return type on the same line if they fit.

Function Calls

link
On one line if it fits; otherwise, wrap arguments at the parenthesis.

Conditionals

link
Prefer no spaces inside parentheses. The else keyword belongs on a new line.

Loops and Switch Statements

link
Switch statements may use braces for blocks. Empty loop bodies should use {} or continue.

Pointer and Reference Expressions

link
No spaces around period or arrow. Pointer operators do not have trailing spaces.

Boolean Expressions

link
When you have a boolean expression that is longer than the standard line length, be consistent in how you break up the lines.

Return Values

link
Do not needlessly surround the return expression with parentheses.

Variable and Array Initialization

link
Use {}.

Preprocessor Directives

link
The hash mark that starts a preprocessor directive should always be at the beginning of the line.

Class Format

link
Sections in public, protected and private order.

Constructor Initializer Lists

link
Constructor initializer lists can be all on one line or with subsequent lines indented four spaces.

Namespace Formatting

link
The contents of namespaces are not indented.

Horizontal Whitespace

link
Use of horizontal whitespace depends on location. Never put trailing whitespace at the end of a line.

Vertical Whitespace

link
Minimize use of vertical whitespace but apply it to enhance readability.

Exceptions to the Rules

The coding conventions described above are mandatory. However, like all good rules, these sometimes have exceptions, which we discuss here.

Existing Non-conformant Code

link
You may diverge from the rules when dealing with code that does not conform to this style guide.

Parting Words

Use common sense and BE CONSISTENT.

If you are editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around their if clauses, you should, too. If their comments have little boxes of stars around them, make your comments have little boxes of stars around them too.

The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you are saying, rather than on how you are saying it. We present global style rules here so people know the vocabulary. But local style is also important. If code you add to a file looks drastically different from the existing code around it, the discontinuity throws readers out of their rhythm when they go to read it. Try to avoid this.

OK, enough writing about writing code; the code itself is much more interesting. Have fun!