Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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.

Can I propose a few changes (following what gordon said). Basically this changes variables to snake case and classes to PascalCase?

for example:

Variables

All variables should be initialized upon declaration. Most variables follow snake_case.

Code Block
languagecpp
int you_are_reading_this = 0;
int you_are_reading_things{0}; \\ what about like this
bool nice_thanks_for_reading = false;
bool nice_thank_you_for_reading{false}; \\ and like this
double this_is_super_fun = 0.0f;

Static variables in classes are prelined by an underscore before the first character.

Code Block
languagecpp
int _this_is_a_static_variable = 0;
bool _do_you_understand = false;
double _ok_keep_reading = 0.0f;

Variables

All variables must follow lower Camel Casehtml. A .clang-format will be uploaded soon.

Table of Contents
minLevel1
maxLevel7
outlinetrue
typeflat
printablefalse

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
languagecpp
int youAreReadingThisyou_are_reading_this = 0;
int you_are_reading_this{0};
bool niceThanksForReadingnice_thanks_for_reading = false;
bool nice_thanks_for_reading{false}; 
double thisIsSuperFunthis_is_super_fun = 0.0f;

As a rule of thumb, all variables should be initialized upon declaration to prevent undefined behaviour. Only exception to this is static variables in classes, which should have an underscore before the first character:

Static variables

Static variables should be appended with an underscore

Code Block
languagecpp
// These are static variables for a class
int _youAreReadingThis;
bool _niceThanksForReading;
double _thisIsSuperFunstatic 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 separated on both sides by a spaceattached to the variable:

Code Block
languagecpp
int *integer_pointer integerPointer = 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.

...

There should always be spaces around all mathematical operators (except division). There should always be spaces between equal signs.

Code Block
languagecpp
int b = (1 + 2 - 3) * random_variable randomVariable/ 5.0 + randomVariableTworandom_variable_two % 7;

Selection

Conditions

Operations

...

Code Block
languagecpp
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
languagecpp
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. To learn more about reference parameters, you can refer to the following links:

...

Code Block
languagecpp
void switchFwendName(string& fwendNamefwend_name, string newFwendNamenew_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
languagecpp
void hewwoThereFwend(string fwendOnefwend_one,
                     string fwendTwofwend_two,
                     string fwendTweefwend_three,
                     string fwendFourfwend_four,
                     string fwendFivefwend_five) { ... }

Commenting

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
languagecpp
/**
 * This function switches the friend's name with a new name
 *
 * @param fwendNamefwend_name -> reference pointing to the friend's name
 * @param newFwendNamenew_fwend_name -> value of the friend's new name
 *
 * @return none
 */
void switchFwendName(string& fwendNamefwend_name, string newFwendNamenew_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
languagecpp
class ExampleClass {
  public:
    ... 
  
  protected: 
    ...
    
  private:
    ...
};

Member Variables: follow variable naming

Code Block
languagecpp
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
languagecpp
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
languagecpp
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
languagecpp
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

...

Classes

...

will use PascalCase for their names. We will use the following syntax:

Code Block
languagecpp
struct StructName {
  ...
};

If you are making a typedef, append a _t.

Code Block
languagecpp
TypeDef struct NewType_t{...}

Initializing Structs

When statically declaring a struct variable, always initialize upon declaration:

Code Block
languagecpp
StructName struct_variable {}; // Value initialization

When declaring a struct variable pointer, initialize it to nullptr if no other options exist:

Code Block
languagecpp
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
languagecpp
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