# C Style Guide and Rules

## Table of contents
- [General rules](#general-rules)
- [Comments](#comments)
- [Functions](#functions)
- [Structures, enumerations, typedefs](#structures-enumerations-typedefs)
- [Compound statements](#compound-statements)
- [Macros and preprocessor directives](#macros-and-preprocessor-directives)
- [Documentation](#documentation)
- [Header/source files](#headersource-files)

## Conventions used
MUST, MUST NOT, SHOULD, SHOULD NOT, MAY and OPTIONAL are used as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).

## General rules

- Do not use tabs, use spaces instead
- Use `4` spaces per indent level
- Use `1` space between keyword and opening bracket
```c
// OK 
if (condition)
while (condition)
for (init; condition; step)
do {} while (condition)

// Wrong 
if(condition)
while(condition)
for(init;condition;step)
do {} while(condition)
```

- Limit the line length to `120` characters, use `120` characters as a soft limit. 
- Do not use space between function name and opening bracket
```c
int32_t a = sum(4, 3);  // OK
int32_t a = sum (4, 3); // Wrong
```

- Use only lowercase characters for variables/functions/types with optional underscore `_` char
- Opening curly bracket is always at the same line as keyword (`for`, `while`, `do`, `switch`, `if`, ...)
```c
size_t i;
for (i = 0; i < 5; ++i) {   // OK
}
for (i = 0; i < 5; ++i){   // Wrong 
}
for (i = 0; i < 5; ++i)    // Wrong 
{
}
```

- Use single space before and after comparison and assignment operators
```c
int32_t a;
a = 3 + 4;              // OK
for (a = 0; a < 5; ++a) // OK
a=3+4;                  // Wrong
a = 3+4;                // Wrong
for (a=0;a<5;++a)       // Wrong
```

- Use single space after every comma
```c
func_name(5, 4);    // OK
func_name(4,3);     // Wrong
```

- Do not initialize `global` variables to any default value (or `NULL`), implement it in the dedicated `init` function (if REQUIRED).

```c
static int32_t a;       // OK
static int32_t b = 4;   // Wrong

void my_module_init(void) {
    a = 0;
    b = 4;
}
```

- Declare local variables in order
 1. Custom types, structures and enumerations
 2. Integer types
 3. Single/Double floating point
```c
int my_func(void) {
    // 1 
    my_struct_t my;
    my_struct_ptr_t* p;

    // 2
    uint32_t a;
    char h;

    // 3
    double d;
    float f;
}
```

- Always declare local variables at the beginning of the block, before the first executable statement
- Always add trailing comma in the last element of structure (or its children) initialization. Unless the structure is very simple and short
```c
typedef struct {
    int a, b;
} str_t;

str_t s = {
    .a = 1,
    .b = 2,   // Comma here 
};

// No trailing commas - only for small and simple structures
static const str_t y = {.a = 1, .b = 2};
```

- Declare counter variables in `for` loop
```c
// OK
for (size_t i = 0; i < 10; ++i)

// OK, if you need the counter variable later
size_t i;
for (i = 0; i < 10; ++i) {
    if (...) {
        break;
    }
}
if (i == 10) {

}

// Wrong
size_t i;
for (i = 0; i < 10; ++i) ...
```

- Except `char`, `float` or `double`, always use types declared in `stdint.h` library, eg. `uint8_t` for `unsigned 8-bit`, etc.
- Never compare against `true`, eg. `if (check_func() == 1)`, use `if (check_func()) { ... }`
- Always compare pointers against `NULL` value
```c
void* ptr;

// OK, compare against NULL
if (ptr == NULL || ptr != NULL) {

}

// Wrong
if (ptr || !ptr) {

}
```
- Always use `size_t` for length or size variables
- Always use `const` for a pointer if function should not modify memory pointed to by `pointer`
- Always use `const` for a function parameter or variable, if it should not be modified
```c

/** 
 * When d could be modified, data pointed to by d could not be modified
 */
void my_func(const void* d) {

}

/** 
 * When d and data pointed to by d both could not be modified
 */
void my_func(const void* const d) {

}

/** 
 * When d should not be modified inside function, only data pointed to by d could be modified 
 */
void my_func(void* const d) {

}
```

- When a function may accept a pointer of any type, always use `void *`, do not use `uint8_t *`
    - The function MUST take care of proper casting in implementation
- Always use brackets with `sizeof` operator
- Always compare variable against zero, except if it is treated as `boolean` type
- Never compare `boolean-treated` variables against zero or one. Use NOT (`!`) instead
```c
size_t length = 5;  // Counter variable
uint8_t is_ok = 0;  // Boolean-treated variable
if (length)         // Wrong, length is not treated as boolean
if (is_ok)          // OK, variable is treated as boolean
if (!is_ok)         // OK
if (is_ok == 1)     // Wrong, never compare boolean variable against 1!
if (is_ok == 0)     // Wrong, use ! for negative check
```

- Every function MUST include *doxygen-enabled* comment, even if function is `static`
- Use English names/text for functions, variables, comments
- Use *lowercase* characters for variables
- Use *underscore* if variable contains multiple names, eg. `force_redraw`. Do not use `forceRedraw`
- Always use `<` and `>` for C Standard Library include files, eg. `#include <stdlib.h>`
- Always use `""` for custom libraries, eg. `#include "my_library.h"`
- When casting to a pointer type, always align the asterisk to the type, eg. `uint8_t* t = (uint8_t*)var_width_diff_type`

## Comments

- Start every comment with capital letter, if possible
- Use `//` for single-line comments, start comment with single space
```c
// This is comment (ok)
/* This is comment (wrong) */
```

- For multi-line comments use `space+asterisk` for every line
```c
/*
 * This is multi-line comments,
 * written in 2 lines (ok)
 */

/**
 * Wrong, use double-asterisk only for doxygen documentation
 */

/*
* Single line comment without space before asterisk (wrong)
*/

/*
 * Single line comment in multi-line configuration (wrong)
 */
```

- Make comments aligned to 4-spaces indent from the beginning of line, and aligned with the rest of the block
```c
void my_func(void) {
    char a, b;
    
    a = call_func_returning_char_a(a);              // This is a comment
    b = call_func_returning_char_b_longer_func(a);  // This is a comment, aligned to 4-spaces indent
}
```

## Functions

- Every function which may have access from outside its module, must include function *prototype* (or *declaration*) in the header file
- Every function that is used only inside its module:
  - Must be declared as `static`
  - Must NOT include function prototype in the header file
  - Must include function prototype in the source file
- Function name MUST be lowercase, optionally separated with underscore `_` character
```c
// OK
void my_func(void);
void myfunc(void);

// Wrong
void MYFunc(void);
void myFunc();
```

- When function returns pointer, align the asterisk to the return type
```c
// OK
const char* my_func(void);
my_struct_t* my_func(int32_t a, int32_t b);

// Wrong
const char *my_func(void);
my_struct_t * my_func(void);
```

## Structures, enumerations, typedefs

- Structure or enumeration name MUST be lowercase with optional underscore `_` character between words
- Structure or enumeration may contain `typedef` keyword
- All structure members SHOULD be lowercase
- All enumeration members MUST be uppercase

When structure is declared, it may use one of `3` different options:

1. When structure is declared with *name only*, it *MUST not* contain `_t` suffix after its name.
```c
struct struct_name {
    char* a;
    char b;
};
```
2. When structure is declared with *typedef only*, it *has to* contain `_t` suffix after its name.
```c
typedef struct {
    char* a;
    char b;
} struct_name_t;
```
3. When structure is declared with *name and typedef*, it *MUST NOT* contain `_t` for basic name and it *MUST* contain `_t` suffix after its name for typedef part.
```c
typedef struct struct_name {
    char* a;
    char b;
    char c;
} struct_name_t;
```

- When new typedef is introduced for function handles, use `_fn` suffix
```c
typedef uint8_t (*my_func_typedef_fn)(uint8_t p1, const char* p2);
```

## Compound statements

- Every compound statement MUST include opening and closing curly bracket, even if it includes only `1` nested statement
- Every compound statement MUST include single indent; when nesting statements, include `1` indent size for each nest
```c
// OK
if (c) {
    do_a();
} else {
    do_b();
}

// Wrong
if (c)
    do_a();
else
    do_b();

// Wrong
if (c) do_a();
else do_b();
```

- In case of `if` or `if-else-if` statement, `else` MUST be in the same line as closing bracket of the first statement
```c
// OK
if (a) {

} else if (b) {

} else {

}

// Wrong
if (a) {

}
else {

}

// Wrong
if (a) {

}
else
{

}
```

- In case of `do-while` statement, `while` part MUST be in the same line as closing bracket of `do` part
```c
// OK
do {
    /* ... */
} while (check());

// Wrong
do
{
    /* ... */
} while (check());

// Wrong
do {
    /* ... */
}
while (check());
```

- Compound statement MUST include curly brackets, even in the case of a single statement.
- Avoid incrementing variables inside loop block if possible.

### Switch statement
- Always include `default` statement
- Add *single indent* for every `case` statement
```c
// Ok
switch (check()) {
    case 0:
        do_a();
        break;
    case 1:
        do_b();
        break;
    default:
        break;
}

// Wrong
switch (check()) {
case 0:         // Wrong, case indent missing
        do_a();
    break;      // Wrong, break MUST have indent as it is under case
    case 1:
    do_b();     // Wrong, indent under case is missing
    break;
    default:
        break;
}
```

## Macros and preprocessor directives

- Always use macros instead of literal constants, especially for numbers
- Always protect input parameters with parentheses
- All macros MUST be fully uppercase, with optional underscore `_` character between words
```c
// OK
#define SQUARE(x)   ((x) * (x))

// Wrong
#define square(x)   ((x) * (x))

// Wrong
#define MIN(x, y)           x < y ? x : y
```

- - Always document `if/elif/else/endif/ifdef/ifndef` statements
```c
#ifdef XYZ
/* do something */
#endif /* XYZ */
```

- Do not indent sub statements inside `#if` statement

## Documentation

- Use doxygen-enabled documentation style for `variables`, `functions` and `structures/enumerations`
- Always use `@` for doxygen, do not use `\`

```c
/**
 * @brief The main screen
 */
lv_obj_t * main_screen;
```

- Every structure/enumeration member MUST include documentation
```c
/**
 * @brief Struct containing information about a wifi network
 * @note The ssid is null terminated
 */
typedef struct {
    char ssid[33];
    int8_t rssi;
    ui_wifi_auth_mode_t auth_mode;
} ui_wifi_network_t;
```

- Documentation for functions used outside its module MUST be placed above function prototype in header file, that is, documentation that describes WHAT the function does. 
- Documentation that describes HOW the function does it be placed with the function implementation.
- For functions that have only internal use, documentation MUST be placed with the function implementation.
- All functions MUST include a `brief` description
- Every parameter MUST be noted if it is `in` or `out` for *input* and *output* respectively
- Function MUST include `return` parameter if it returns something. This does not apply for `void` functions
- Function can include other doxygen keywords, such as `note` or `warning`
- If function returns member of enumeration, use `ref` keyword to specify which one
```c
/**
 * @brief Parse and publish data from the meter data history endpoint
 *
 * @param[in] buf The buffer containing the JSON data
 * @param[in] len The length of the JSON data
 * @return @ref ESP_OK on success, member of @ref esp_err_t otherwise
 */
static esp_err_t parse_publish_meter_data_history(uint8_t *buf, uint32_t len) {
    /* ... */
}
```

## Header/source files

- Leave a single empty line at the end of the file
- Every file MUST include doxygen annotation for `file` and `brief` description followed by empty line
```c
/**
 * @file tsc2046.c
 * @brief TSC2046 touch screen controller driver
 */
                    // Empty line 
```

- Header file MUST include guard `#ifndef`
- Include external header files with STL C files first followed by application custom files
- Header file MUST include only every other header file to compile correctly, but not more (.c should include the rest if REQUIRED)
- Header file MUST only expose module public variables/types/functions
- Use `extern` for global module variables in header file, define them in source file later
```
/* file.h ... */
#ifndef ...

extern int32_t my_variable; // This is global variable declaration in header 

#endif

/* file.c ... */
int32_t my_variable;        // Actually defined in source 
```

- Never include `.c` files in another `.c` file
- Do not include module private declarations in header file

- Header file example
```c
/* License comes here */
#ifndef TEMPLATE_HDR_H
#define TEMPLATE_HDR_H

/* Include headers */
#include <stdint.h>
#include "my_custom_header.h"

/* File content here */

#endif /* TEMPLATE_HDR_H */
```