C++ Style Guide
September 19, 2019 ยท View on GitHub
In general, Google's coding standard is used, and we strongly encourage to read it.
Below are our specific (but not all!) exceptions to the Google's coding standard:
- All code should conform to the C++14 standard (not C++17 or higher).
- We use
.cppand.hppfiles, not.ccand.h(.cand.hare used for C code), in UTF-8 encoding. - File names are lowercase with underscores, like
file_reader.cpp. - We use
#pragma onceinstead of the#defineGuard in header files. - Includes are sorted and grouped by directory, there should be newlines between different directories.
- Order of directories in includes: "current_dir/current_file.hpp", includes from other dirs sorted by dependencies (e.g. indexer, then coding, then base), "defines.hpp", C++ standard library headers, boost headers, 3party.
- We ARE using C++ exceptions.
- We are using all features of C++11 (the only known exception is thread_local which is not fully supported on all platforms).
- We try to limit the usage of boost libraries which require linking (and prefer C++11 types over their boost counterparts).
Naming and formatting
-
We ALWAYS use two spaces indent and don't use tabs.
-
We don't have strict limits on line width, but keep it reasonable to fit on the screen. The advised width is that written in the .clang-format file (currently 100).
-
Doxygen-style comments can be used.
-
Underscores are allowed only in prefixes for member variables and namespace names, like
int m_countriesCount; namespace utf_parser. -
Use right-to-left order for variables/params:
string const & s(reference to the const string). -
In one line
if,for,whilewe do not use brackets. If one linefororwhileis combined with one lineif, do use brackets for cycle. -
Space after the keyword in conditions and loops. Space after
;inforloop. -
Space between binary operators:
x = y * y + z * z. -
Space after double dash.
-
We use
usingkeyword instead oftypedef. -
We do not use the Systems Hungarian Notation: do not add the "p" suffix to your pointer variable names and the "T" prefix or suffix to your type names.
-
Compile-time constants must be named in camelCase, starting with a lower-case
k, e.g.kCompileTimeConstantand marked asconstexprwhen possible. -
Values of enum classes must be named in CamelCase, e.g.
enum class Color { Red, Green, LightBlue };. -
Macros and C-style enums must be named in UPPER_CASE, and enum values must be prefixed with a capitalized enum name.
Note that macros complicate debugging, and old-style enums have dangerous implicit conversions to integers, and tend to clutter containing namespaces. Avoid them when possible - use
constorconstexprinstead of macros, and enum classes instead of enums.
We write code without warnings!
ClangFormat
Most of our coding style is specified in a configuration file for ClangFormat.
To automatically format a file, install clang-format and run:
clang-format -i file.cpp file.hpp other_file.cpp
Formatting Example/Guide/Reference
#pragma once
#include <math>
uint16_t constexpr kBufferSize = 255;
// C-style enums are ALL_CAPS. But remember that C++11 enum classes are preferred.
enum Type
{
TYPE_INTEGER,
TYPE_FLOAT,
TYPE_STRING
};
using TMyTypeStartsWithCapitalTLetter = double;
class ComplexClass
{
public:
Complex(double rePart, double imPart) : m_re(rePart), m_im(imPart) {}
double Modulus() const
{
double const rere = m_re * m_re;
double const imim = m_im * m_im;
return sqrt(rere + imim);
}
double OneLineMethod() const { return m_re; }
private:
// We use the "m_" prefix for member variables.
double m_re;
double m_im;
};
namespace
{
void CamelCaseFunctionName(int lowerCamelCaseVar)
{
static int counter = 0;
counter += lowerCamelCaseVar;
}
} // namespace
namespace lower_case
{
template <typename TypenameWithoutAffixes>
void SomeFoo(int a, int b,
TypenameWithoutAffixes /* We avoid compilation warnings. */)
{
for (int i = 0; i < a; ++i)
{
// IMPORTANT! We DON'T use one-liners for if statements for easier debugging.
// The following syntax is invalid: if (i < b) Bar(i);
if (i < b)
Bar(i);
else
{
Bar(i);
Bar(b);
// Commented out the call.
// Bar(c);
}
}
}
} // namespace lower_case
// Switch formatting.
int Foo(int a)
{
switch (a)
{
case 1:
Bar(1);
break;
case 2:
{
Bar(2);
break;
}
case 3:
default:
Bar(3);
break;
}
return 0;
}
// Loops formatting.
if (condition)
foo();
else
bar();
if (condition)
{
if (condition)
foo();
else
bar();
}
for (size_t i = 0; i < size; ++i)
foo(i);
while (true)
{
if (condition)
break;
}
// Space after the keyword.
if (condition)
{
}
for (size_t i = 0; i < 5; ++i)
{
}
while (condition)
{
}
switch (i)
{
}
// Space between operators, and don't use space between unary operator and expression.
x = 0;
x = -5;
++x;
x--;
x *= 5;
if (x && !y)
{
}
v = w * x + y / z;
v = w * (x + z);
// Space after double dash. And full sentences in comments.
Tips and Hints
- If you see outdated code which can be improved, DO IT NOW (but in a separate pull request)!
- Your code should work at least on [mac|linux|android][x86|x86_64], [ios|android][x86|armv7|arm64] architectures
- Your code should compile with gcc 6.0+ and clang 5.0+
- Try to avoid using any new 3party library if it is not fully tested and supported on all our platforms
- Cover your code with unit tests. See examples for existing libraries
- Check Base and Coding libraries for most of the basic functions
- Ask your team if you have any questions
- Use dev@maps.me mailing list to ask all developers and bugs@maps.me mailing list to post bugs
- Release builds contain debugging information (for profiling), production builds do not
- If you don't have enough time to make it right, leave a
// TODO(DeveloperName): need to fix itcomment
Some useful macros:
#ifdef DEBUG | RELEASE | OMIM_PRODUCTION#ifdef OMIM_OS_ANDROID | OMIM_OS_IPHONE | OMIM_OS_MAC(and some other useful OS-related macros, seestd/target_os.hpp)- Use
ASSERT(expression, (out message))andASSERT_XXXXXXmacros often to check code validity in DEBUG builds - Use
CHECK(expression, (out message))andCHECK_XXXXXXmacros to check code validity in all builds - Use
LOG(level, (message))for logging, below is more detailed description for level:LINFO- always prints log messageLDEBUG- logs only in DEBUGLWARNING- the same asLINFObut catches your attentionLERROR- the same asLWARNING, but crashes in DEBUG and works in RELEASELCRITICAL- the same asLERRORand ALWAYS crashes
- Need scope guard? Check
SCOPE_GUARD(name, func) - Use
std::array::size()to calculate plain C-array's size - Declare your own exceptions with
DECLARE_EXCEPTION(name, baseException), wherebaseExceptionis usuallyRootException - Throw exceptions with
MYTHROW(exceptionName, (message)) - A lot of useful string conversion utils are in
base/string_utils.hpp