We are using Modern C++ 11 and we are limiting use of C++ 14 to specific parts of the code for now. Smart pointers, Lamdas, and C++ 11 multithreading primitives are your friend.
The great thing about "standards" is that there are many to chose from: ISO, Sutter & Stroustrup, ROS, LINUX, Google's, Microsoft's, CERN's, GCC's, ARM's, LLVM's and probably thousands of others. Unfortunately most of these can't even agree on something as basic as how to name a class or a constant. This is probably due to the fact that these standards often carry lots of legacy issues due to supporting existing code bases. The intention behind this document is to create guidance that remains as close to ISO, Sutter & Stroustrup and ROS while resolving as many conflicts, disadvantages and inconsistencies as possible among them.
Avoid using any sort of Hungarian notation on names and "_ptr" on pointers.
Code Element | Style | Comment |
---|---|---|
Namespace | under_scored | Differentiate from class names |
Class name | CamelCase | To differentiate from STL types which ISO recommends (do not use "C" or "T" prefixes) |
Function name | camelCase | Lower case start is almost universal except for .Net world |
Parameters/Locals | under_scored | Vast majority of standards recommends this because _ is more readable to C++ crowd (although not much to Java/.Net crowd) |
Member variables | under_scored_with_ | The prefix _ is heavily discouraged as ISO has rules around reserving _identifiers, so we recommend suffix instead |
Enums and its members | CamelCase | Most except very old standards agree with this one |
Globals | g_under_scored | You shouldn't have these in first place! |
Constants | UPPER_CASE | Very contentious and we just have to pick one here, unless if is a private constant in class or method, then use naming for Members or Locals |
File names | Match case of class name in file | Lot of pro and cons either way but this removes inconsistency in auto generated code (important for ROS) |
Use a namespace qualified #ifdef to protect against multiple inclusion:
#ifndef msr_airsim_MyHeader_hpp
#define msr_airsim_MyHeader_hpp
//--your code
#endif
The reason we don't use #pragma once is because it's not supported if same header file exists at multiple places (which might be possible under ROS build system!).
Inside function or method body place curly bracket on same line. Outside that the Namespace, Class and methods levels use separate line. This is called K&R style and its variants are widely used in C++ vs other styles which are more popular in other languages. Notice that curlies are not required if you have single statement, but complex statements are easier to keep correct with the braces.
int main(int argc, char* argv[])
{
while (x == y) {
f0();
if (cont()) {
f1();
} else {
f2();
f3();
}
if (x > 100)
break;
}
}
Religiously review all non-scalar parameters you declare to be candidate for const and references. If you are coming from languages such as C#/Java/Python,
the most often mistake you would make is to pass parameters by value instead of const T&;
Especially most of the strings, vectors and maps you want to
pass as const T&;
(if they are readonly) or T&
(if they are writable). Also add const
suffix to methods as much as possible.
When overriding virtual method, use override suffix.
This is really about memory management. A simulator has much performance critical code, so we try and avoid overloading the memory manager with lots of calls to new/delete. We also want to avoid too much copying of things on the stack, so we pass things by reference when ever possible. But when the object really needs to live longer than the call stack you often need to allocate that object on the heap, and so you have a pointer. Now, if management of the lifetime of that object is going to be tricky we recommend using C++ 11 smart pointers. But smart pointers do have a cost, so don’t use them blindly everywhere. For private code where performance is paramount, raw pointers can be used. Raw pointers are also often needed when interfacing with legacy systems that only accept pointer types, for example, sockets API. But we try to wrap those legacy interfaces as much as possible and avoid that style of programming from leaking into the larger code base.
Religiously check if you can use const everywhere, for example, const float * const xP
. Avoid using prefix or suffix to indicate pointer types in variable names, i.e. use my_obj
instead of myobj_ptr
except in cases where it might make sense to differentiate variables better, for example, int mynum = 5; int* mynum_ptr = mynum;
Yes, and it's on purpose because no one likes to read 200 page coding guidelines. The goal here is to cover only most significant things which are already not covered by strict mode compilation in GCC and Level 4 warnings-as-errors in VC++. If you had like to know about how to write better code in C++, please see GotW and Effective Modern C++ book.