[Assume C++ 17 or newer]
This article is my C++ version of My Python Coding Style and Principles. A coding guideline is a bare minimum for keeping the software developing under certain quality. As a C++ programmer for several years, this article is my note of C++ programming, so I can keep my C++ code consistent with readability and maintainability.
Naming
Type | Naming Convention |
---|---|
Header Files | CamelCase.hpp |
Implementation Files | CamelCase.cpp |
Classes | CamelCase |
Class Methods | camelCase |
Exceptions | CamelCase |
Functions | camelCase |
Variables | camelCase |
Function/Method Parameters | camelCase |
Aliased Name | using CamelCase = xxx |
Enumeration Type Names and Values | CamelCase |
Compile-Time Constants | CAPS_WITH_UNDERSCORE |
Namespace | CamelCase |
Pointers | camelCaseWith_p |
- Although classes and exceptions should use
CamelCase
, it is encouraged to use CAPS as a common term for readability. For example,
class MySQL
{
// ...
}
- Make names descriptive. Use complete words. Avoid abbreviation.
int n = 0; // Bad! Meaningless name.
int itm = 1; // Bad! Maybe it means "item" with "e" removed. Who knows?
int eid = 2; // Bad! Maybe some kind of id? Extent id? Who knows?
- Boolean functions describing a characteristic start with is or has.
bool isConnect()
{
// ...
}
bool hasMessage()
{
// ...
}
- Functions and variables denoting the number of elements should use count or length.
int messageCount()
{
// Number of messages
}
int queueLength()
{
// Number of entries in the queue
}
int queueSize()
{
// Avoid: Confusing.
}
- Use plural if an object indicates a collection or a list.
std::vector<int> objects;
Style Guideline
The coding style is adapted from LLVM with the following modifications (use clang-format)
- BasedOnStyle: LLVM
- BreakBeforeBraces: Allman
- ColumnLimit: 88
- IndentWidth: 4
- NamespaceIndentation: All
- TabWidth: 4
General Rule
- Header files end with .hpp; implementation files end with .cpp.
- The name of the file should be the same as the class name. For example, the header file for MyClass should be in the file MyClass.hpp, and its implementation file should be named MyClass.cpp.
- One class interface per header file
- Use pragma #once. All header files should use #pragma once as the header guard.
- Explicitly add #include when referencing external classes and data. Do not assume that another includes already includes the external references.
- Group related includes together with the following order: 1. C++ STL, 2. 3rd party libraries, 3. other files within the same project. Alphabetize the entries within each group.
- Use angle-brackets (<>) for STL and 3rd party libraries; use double-quotes (“”) for files in the same project.
#pragma once
#include <memory>
#include <string>
#include <boost/math/special_functions/bernoulli.hpp>
#include <boost/math/special_functions/detail/bernoulli.hpp>
#include <boost/math/special_functions/prime.hpp>
#include "MyClass1.hpp"
#include "MyClass2.hpp"
Line Length
Line Length
Line length should be 88 characters per line (the idea is borrowed from Python’s Black Line Length recommendation). Exceptions are
- URL
- Long include statements
- Long flags
- Long string without whitespace
- Linting disable comments
Access Modifier Order
The modifier order should be consistent with the order (from top to bottom).
- Public
- Protected
- Private
Coding Guideline
Scope
- Declare local variables as close to their use as possible.
- Always initialize local variables
- Use a project namespace. Each project should have its namespace, so all of its code can be placed in the namespace.
- Declare local variables and functions to a class in the anonymous namespace
- Static (and global) data should only be of POD (plain old data) types
- Do not use using namespace std. (Avoid blanket uses of namespaces)
- Avoid global data
Do
- Use smart pointers whenever and wherever is possible. Avoid raw pointers.
- Use RAII. Avoid using new and delete. When managing raw memory is necessary, use placement new and delete. Do not use malloc and free.
- Use default to explicitly define default constructors and destructors
- Keep functions short
- Use classes for type safety and readability
- Use explicit (compile time checking)
- Use constructor initializer lists
- Use const and constexpr
- Use override when a base method (compile time checking)
- Use static_assert (compile time checking)
- Make object immutable
- Use const whenever and wherever is possible
- Use inline functions in the interface when small (no more than two or three lines). Do not use the inline keyword. Pointless to use it.
- Input parameters go before output parameters
Don’t Do
- Avoid naked numeric types like int. For example,
int count = 0; // Avoid
uint32_t count = 0; // Preferred
- Avoid c-style casts (compile time checking)
- Avoid assert
- Avoid #define for compile-time constants (compile time checking)
- Do not call a virtual method in a class constructor or destructor
- Do not throw exceptions from a destructor or a function/method marked noexcept
- Do not return class members by non-const reference
- Do not re-define a method’s inherited default parameter value
- Do not use exceptions for error-scenarios that may be reasonably expected or occur frequently
- Do not use C++03 features that were obsoleted by C++ 11 or newer
- Do not write code that is not exception safe
- Do not write code that uses features of a specific compiler
- Do not use C library if there is a C++ version. For example,
#include <math.h> // Avoid
#include <cmath> // Preferred
Comments Documents
Use C++ style comment for license boilerplate, one line comment, and multiple line comments. For example,
// Copyright © 2021 by Shun Huang. All rights reserved.
// Licensed under MIT License.
// See LICENSE in the project root for license information.
void function()
{
// One line comment...
// Multiple line comments
// ...
// ...
}
Documenting the Code
Code documenting follows Doxygen style and Javadoc style.
- Basic comment block
/**
* ... text ...
*/
- Brief description
/*! \brief Brief description.
* Brief description continued.
*
* Detailed description starts here.
*/
- Document for members
uint32_t var; //!< Brief description after the member
- Inline document for parameters with in and out
void foo(int v /**< [in] docs for input parameter v. */);
A complete document example.
/**
* \brief An example class.
*
* A more elaborate class description.
*/
class Example
{
public:
/**
* \brief An enum.
* More detailed enum description.
*/
enum class EnumExample
{
Val1, //!< enum value Val1.
Val2 //!< enum value Val2.
};
/**
* A constructor.
* A more elaborate description of the constructor.
*/
Example();
/**
* A destructor.
* A more elaborate description of the destructor.
*/
~Example();
/**
* a normal member taking two arguments and returning an integer value.
* @param a an integer argument.
* @param str a constant string reference.
* @return The test results
*
* @see Example()
*/
uint32_t test(uint32_t a, const std::string &str);
};
Toolchain
The preferred tools for my C++ projects are the following:
- Compiler: GNU GCC (g++)
- Build Tool: CMake and Ninja
- Code Format: Clang-Format
- Code Linting: Clang-Tidy
- Document Generators: Sphinx and Doxygen
- Editor: VS Code with C++ Extension