Style guide should be followed for all C/C++ code developed for WARG. Generally, we want to follow the guide set by BAARS C or MISRA C, although generally our style follows that of the google c++ guide. https://google.github.io/styleguide/cppguide.html. A .clang-format will be uploaded soon.
Table of Contents |
---|
minLevel | 1 |
---|
maxLevel | 7 |
---|
outline | true |
---|
type | flat |
---|
printable | false |
---|
|
File Naming
With the introduction of ZP and LOS, there’s an increased pressure to name well. Please refer to the ZP / LOS architecture spec for directory structure.
Zeropilot
Zeropilot file names should be prefixed by their corresponding category, and then be in Pascal Case. I.E:
Code Block |
---|
AM_Interface.cpp // Attitude Manager Interface
SF_MahonyFilter.hpp // Sensor Fusion Mahony Filter |
There should be very few exceptions to this rule. Try to make names descriptive and follow our design pattern.
Laminar OS
LaminarOS is a bit more difficult. For interface layers, the name of the file should be prefixed only by LOS_
:
Code Block |
---|
LOS_Link.cpp // RC-Link interface
LOS_Pos.cpp // LOS Position interface
LOS_Telem.hpp // LOS Telemetry interface |
LOS Core and LOS Driver layers should be prefixed by LOS_C_
and LOS_D_
respectively. Los CORE generally will have names of protocols, please follow the naming conventions for those protocols, otherwise use all uppercase. Drivers should be in all lowercase (since a lot of these will also be names like PPM or CRSF, but using all caps makes it easier to confuse with core)
Code Block |
---|
LOS_C_I2C.Xpp
LOS_C_GPIO.Xpp
LOS_C_UART.Xpp
LOS_C_TIM.Xpp
LOS_D_imu.Xpp
LOS_D_ppm.Xpp
LOS_D_xbees.Xpp |
Formatting
Spacing
We use 4 spaces instead of tabs here.
Hard line limit of 80 characters.
Variables
Note: For class
and struct
member conventions, refer to the Data Structures
section.
Generally, variables should follow snake case.
Code Block |
---|
|
int you_are_reading_this = 0;
int you_are_reading_this{0};
bool nice_thanks_for_reading = false;
bool nice_thanks_for_reading{false};
double this_is_super_fun = 0.0f; |
As a rule of thumb, all variables should be initialized upon declaration to prevent undefined behaviour.
Static variables
Static variables should be appended with an underscore
Code Block |
---|
|
static int oops_{69};
std::string openhd_status_ = "broken like always"; |
Constants
Constants (const
and constexpr
) should be named using screaming case, never lead with an underscore
Code Block |
---|
const int YOU_ARE_READING_THIS{0}
constexpr bool K_NICE_THANKS_FOR_READING{false}; |
Pointers
The* should be attached to the variable:
Code Block |
---|
|
int *integer_pointer = nullptr; |
As a rule of thumb, all pointers must be initialized on declaration. If you are not assigning it a value, then set it to nullptr
like the example above.
Declaring Variables on the Same Line
Declaring and initializing multiple variables of the same type can be done on the same line. However, the number of variables on the line should not exceed 5:
Code Block |
---|
int a = 0, b = 1, c = 2, d = 3;
int a = 0, b = 1, c = 2, d = 3, e = 4, f = 6; // Not good, six variables on same line |
Structs and Classes
When declaring structs, classes, or any object for that matter, we will prefer to use C++'s implementation of value initialization so all parameters are default initialized (are given their default value).
Code Block |
---|
|
struct A {
...
};
int main() {
A hello {}; // The curly brackets trigger value initialiation
} |
Mathematics
Mathematical Operations
There should always be spaces around all mathematical operators. There should always be spaces between equal signs.
Code Block |
---|
|
int b = (1 + 2 - 3) * random_variable / 5.0 + random_variable_two % 7; |
Selection
Conditions
Operations
There must be a space around all operations (==
, >=
, &&
, etc.)
Code Block |
---|
|
if (1 < 2 || (1 <= 3 && 2 >= 1) || 1 == 1 || 1 > 0) { ... } |
Additionally, if a condition list has more than one condition, any condition containing mathematical operations must be surrounded in brackets:
Code Block |
---|
|
if (1 + 1 > 0) { ... } // Only one condition
if ((1 + 1 > 0) && 4 == 4) { ... } // More than one condition |
Order of Terms
When comparing the value of a variable, it is best practice to put the value before the variable identifier. This prevents runtime errors from occuring in case you use =
instead of ==
.
Code Block |
---|
if (1 == a) { ... } |
If Statements
Structure
There must be a space around the if
and else
keywords, and there must be a space separating the condition list and the opening curlly-bracket. Curly-brackets must be on the same line as the condition list.
Code Block |
---|
if (a == b) {
} else if (b == c) {
} else {
} |
Always use curly brackets, even if the code within the if statement is one line.
Ternary Expressions
Pretty standard.
Code Block |
---|
bool b = true;
int a = b ? 1 : 0; |
Switch Statements
Switch statements are pretty standard too, just need to get the indentation right. Also, ensure you put break
s in every condition unless you cannot put it:
Code Block |
---|
|
int a = 1;
switch(a) {
case 1: // Indent the cases
...
break;
// Keep a space between the end of one case and the condition of the next
case 2:
case 3:
...
break;
default: // Always include a default case
break;
} |
Loops
Curly Brackets
Should be on the same line as the do
, for
, and while
keyword:
Code Block |
---|
for ( ... ) {
}
do {
} while ( ... );
while {
} |
Also keep a space between the do
, for
, and while
keyword; conditions, and open curly-bracket.
Conditions follow same rules as if statements.
Breaks
We try to avoid using the break
keyword when exiting loops. Instead, use a boolean flag to track if the loop is done executing.
Code Block |
---|
bool isDone{false};
for (int i = 0; i < 10 && !isDone; i++) {
if (6 == i) {
isDone = true;
}
} |
continue
statements will be allowed, however they should only be used if it will simplify the code by a significant amount.
Functions
Naming and Brackets
...
Code Block |
---|
int hewwoThereFwend() {
return 0;
} |
Parameters
Parameter names
Parameter names should follow the same conventions as written in the Variables
section.
Pointers
The* in pointers must be separated on both sides by one space.
Code Block |
---|
|
void hewwoThereFwend(string * fwendNamefwend_name) { ... } |
NOTE: Moving forward, we will prefer to use reference parameters over pointers when passing by reference. It is a more C++ style of programming.
...
Code Block |
---|
void hewwoThereFwend(string& fwendNamefwend_name) { ... } |
We will prefer to use this C++ feature when passing values by reference from now on. To learn more about reference parameters, you can refer to the following links:
More than one Parameter
Parameters in a list must be separated by commas (obviously lol), but a space must separate a comma and a parameter:
Code Block |
---|
|
void hewwoThereFwendswitchFwendName(string& fwendNamefwend_name, string fwendTwoNamenew_fwend_name) { ... } |
If the parameter list is very long, creating a vertical list of parameters is acceptable too:
...
. Just ensure each parameter starts in the same column so as to ensure readability.
Code Block |
---|
|
void hewwoThereFwend(string fwendOnefwend_one,
string fwendOnefwend_two,
string fwendOnefwend_three,
string fwendOne, ) |
Mathematics
Mathematical Operations
There should always be spaces around all mathematical operators. There should always be spaces between equal signs.
Code Block |
---|
|
int b = (1 + 2 - 3) * 4/5.0 + 6 % 7;fwend_four,
string fwend_five) { ... } |
All function declarations must have a comment above describing its purpose, parameters, and returned value. Pay notice to the spacing on the* and the /**
that opens the comment
Code Block |
---|
|
/**
* This function switches the friend's name with a new name
*
* @param fwend_name -> reference pointing to the friend's name
* @param new_fwend_name -> value of the friend's new name
*
* @return none
*/
void switchFwendName(string& fwend_name, string new_fwend_name); |
Data Structures
Classes
Classes use PascalCase. Public member variables and methods will be declared first, followed by protected and then private:
Code Block |
---|
|
class ExampleClass {
public:
...
protected:
...
private:
...
}; |
Member Variables: follow variable naming
Code Block |
---|
|
class ExampleClass {
public:
private:
// Member variables should always be private. If you need access to them, provide getters and setters.
int hello_there;
const double GENERAL_KENOBI;
static int its_over_anakin_;
};
// Remember static variables must be redeclared outside of the class declaration
int ExampleClass::its_over_anakin_ {0}; |
Constructors
We prefer to use initializer lists when initializing member variables, however there are cases where we should stick to initializing member variables within the constructor.
Code Block |
---|
|
class ExampleClass {
public:
ExampleClass();
ExampleClass(int num_apple_pies);
private:
int apple_pies, banana_pies;
double slices_left;
}
ExampleClass::ExampleClass() : apple_pie {0}, banana_pies {0}, slices_left {0.0f} {}
// You can stack your initializing list. Just make sure they start in the same column
ExampleClass::ExampleClass(int num_apple_pies) : apple_pies {num_apple_pies},
banana_pies {0} {
slices_left = 6* num_apple_pies; // If an operation is required, preferred if initialization is within constructor
} |
Initializing Classes
When statically declaring a struct, you should initialize it immediately. If a default constructor exists, call that, else call another constructor.
Code Block |
---|
|
class ExampleClass {
public:
ExampleClass();
ExampleClass(int num_apple_pies);
private:
int apple_pies, banana_pies;
double slices_left;
}
int main() {
ExampleClass default_class {}; // Preferred over `ExampleClass class_with_apples()`
ExampleClass class_with_apples {1}; // Preferred over `ExampleClass class_with_apples(1)`
} |
If declaring a class pointer, initialize to nullptr
if no other options exist:
Code Block |
---|
|
class ExampleClass {
...
};
int main() {
ExampleClass* example_pointer = nullptr; // Good!
ExampleClass statically_declared_class {};
ExampleClass* other_pointer = &statically_declared_class; // Also good!
ExampleClass* one_more_pointer = functionThatReturnsAClassPointer(); // Good too!
} |
Structs
Structs should only be used to store Plain Old Data (POD). That means no methods should ever be declared in a struct. If you need method, use a class :))
Structs will use PascalCase for their names. We will use the following syntax:
Code Block |
---|
|
struct StructName {
...
}; |
If you are making a typedef, append a _t.
Code Block |
---|
|
TypeDef struct NewType_t{...} |
Initializing Structs
When statically declaring a struct variable, always initialize upon declaration:
Code Block |
---|
|
StructName struct_variable {}; // Value initialization |
When declaring a struct variable pointer, initialize it to nullptr
if no other options exist:
Code Block |
---|
|
StructName* struct_variable = nullptr;
StructName* struct_variable_two = functionThatReturnsAStructNamePointer(); // Valid too! |
Enums
We use PascalCase for enunms, and the constants will use all caps with underscores separating words. Additionally, the first constant should have a = 0
appended to it.
Code Block |
---|
|
enum EnumOne {HEWWO = 0, THERE, MY, FWEND};
// If there are a lot of terms, you can split them on multiple lines with one constant per line
enum EnumTwo {
HEWWO = 0,
THERE,
MY,
FWEND,
YOU,
AWRE,
AMAZING
}; |
Further reading
https://barrgroup.com/sites/default/files/barr_c_coding_standard_2018.pdf