Skip to content
Dane Springmeyer edited this page Dec 21, 2016 · 13 revisions

Introduction

This document attempts to describe a few C++ coding standards that are being used. Although no coding standards should be regarded as absolute requirements to be followed in all instances, coding standards are particularly important for large-scale code bases.

There are some conventions that are not uniformly followed in the code base (e.g. the naming convention). This is because they are relatively new, and a lot of code was written before they were put in place. Our long term goal is for the entire codebase to follow the convention, but we explicitly do not want patches that do large-scale reformating of existing code. On the other hand, it is reasonable to rename the methods of a class if you’re about to change it in some other way. Just do the reformating as a separate commit from the functionality change.

The ultimate goal of these guidelines is the increase readability and maintainability of our common source base.

C++ Source Code Formatting

Comments are one critical part of readability and maintainability. Everyone knows they should comment their code, and so should you. When writing comments, write them as English prose, which means they should use proper capitalization, punctuation, etc. Aim to describe what the code is trying to do and why, not how it does it at a micro level. Here are a few critical things to document:

File Headers

Every source file should have a header on it that recites the license and describes the basic purpose of the file. The standard header looks like this:

/*
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

Every non-trivial class is expected to have a description. Same holds for methods. Methods defined in a class (as well as any global functions) should also be documented properly. A quick note about what it does and a description of the borderline behaviour is all that is necessary here (unless something particularly tricky or insidious is going on). The hope is that people can figure out how to use your interfaces without reading the code itself.

Comment Formatting

In general, prefer C++ style (//) comments. To comment out a large block of code, use #if 0 and #endif. These nest properly and are better behaved in general than C style comments.

#include ordering

Immediately after the header file comment (and include guards if working on a header file), the minimal list of #includes required by the file should be listed. We prefer these #includes to be listed in this order:

1. Module Header
2. Local/Private Headers
3. ../...
4. System #includes

and each category should be sorted lexicographically by the full path.

The Main Module Header file applies to .cpp files which implement an interface defined by a .h file. This #include should always be included first regardless of where it lives on the file system. By including a header file first in the .cpp files that implement the interfaces, we ensure that the header does not have any hidden dependencies which are not explicitly included in the header, but should be. It is also a form of documentation in the .cpp file to indicate where the interfaces it implements are defined.

Source Code Format, Indentation

We use clang-format during the ongoing development. The develop branch contains a .clang-format style file. Changes shall be formatted using this tool. That being said, existing code shall not be touched for the time being. The change to the new style will be made gradually.

Indentation

In all cases, indent C++ code by four spaces.

Compiler Issues

Treat Compiler Warning as Errors!

If your code has compiler warnings in it, something is wrong — you aren’t casting values correctly, you have “questionable” constructs in your code, or you are doing something legitimately wrong. Compiler warnings can cover up legitimate errors in output and make dealing with a translation unit difficult.

Write Portable Code

In almost all cases, it is possible and within reason to write completely portable code. If there are cases where it isn’t possible to write portable code, isolate it behind a well defined (and well documented) interface.

In practice, this means that you shouldn’t assume much about the host compiler.

Do not use RTTI and Refrain from Exceptions Unless Needed.

In an effort to reduce code and executable size, do not use RTTI (e.g. dynamic_cast<>;) or exceptions. These two language features violate the general C++ principle of “you only pay for what you use”, causing executable bloat even if exceptions are never used in the code base, or if RTTI is never used for a class. Because of this, we turn them off globally in the code.

Exceptions are allowed in the case of IO operations.

Naming Conventions

Poorly-chosen names can mislead the reader and cause bugs. We cannot stress enough how important it is to use descriptive names. Pick names that match the semantics and role of the underlying entities, within reason. Avoid abbreviations unless they are well known. After picking a good name, make sure to use consistent capitalization for the name, as inconsistency requires clients to either memorize the APIs or to look it up to find the exact spelling.

In general, names should be in camel case (e.g. TextFileReader and isLValue()). Different kinds of declarations have different rules:

  • Type names (including classes, structs, enums, typedefs, etc) should be nouns and start with an upper-case letter (e.g. TextFileReader).
  • Variable names should be nouns (as they represent state). The name should be made up of lower-case words separated by underscores. Private class member start with 'm_'.
  • Function names should be verb phrases (as they represent actions), and command-like function should be imperative. The name should be camel case, and preferably start with a lower case letter (e.g. openFile() or OpenFile() are OK).
  • Enum declarations (e.g. enum Foo {...}) are types, so they should follow the naming conventions for types. A common use for enums is as a discriminator for a union, or an indicator of a subclass. When an enum is used for something like this, it should have a Kind suffix (e.g. ValueKind).
  • Enumerators (e.g. enum { Foo, Bar }) and public member variables should start with an upper-case letter, just like types. Unless the enumerators are defined in their own small namespace or inside a class, enumerators should have a prefix corresponding to the enum declaration name. For example, enum ValueKind { ... }; may contain enumerators like VK_Argument, VK_BasicBlock, etc. Enumerators that are just convenience constants are exempt from the requirement for a prefix. For instance:
   enum {
      MaxSize = 42,
      Density = 12
    };

As an exception, classes that mimic STL classes can have member names in STL’s style of lower-case words separated by underscores (e.g. begin(), push_back(), and empty()).

Assert Liberally

Use the “assert” macro to its fullest. Check all of your preconditions and assumptions, you never know when a bug (not necessarily even yours) might be caught early by an assertion, which reduces debugging time dramatically. The “” header file is probably already included by the header files you are using, so it doesn’t cost anything to use it.

Do not use using namespace ...

We prefer to explicitly prefix all identifiers from the standard namespace with an “std::” prefix, rather than rely on “using namespace std;”.

In header files, adding a 'using namespace XXX' directive pollutes the namespace of any source file that #includes the header. This is clearly a bad thing.

Integer types

Of the built-in C++ integer types, the only one used is int, i.e. 32 bits wide. If you need a variable of a different size, use a precise-width integer type from <boost/integer.hpp> to ensure maximum compatibility across different platforms and compilers, such as int16_t. If your variable represents a value that could ever be greater than or equal to 2^31 (2GiB), use a 64-bit type such as int64_t.


Note: This is partially copied from the page of LLVM Coding Standards. Thanks, LLVM!

Clone this wiki locally