This document describes C code style used by Tilen MAJERLE in his projects and libraries.
Let's start with the quote from GNOME developer site.
The single most important rule when writing code is this: check the surrounding code and try to imitate it.
As a maintainer it is dismaying to receive a patch that is obviously in a different coding style to the surrounding code. This is disrespectful, like someone tromping into a spotlessly-clean house with muddy shoes.
So, whatever this document recommends, if there is already written code and you are patching it, keep its current style consistent even if it is not your favorite style.
VScode comes with pre-installed clang-format
tool (part of LLVM package) that has been design to help developers with auto-format tool during code development.
As such, it allows users to format code on file change (and save).
When file is saved, vscode will try to invoke the clang-format and format the code. Rules to use are in .clang-format
file. If clang-format cannot find the rules in the path of current file, it will go all the way up to the root, until one is found. If still none is available, default rules are then being used.
This repository contains always up-to-date .clang-format
file with rules matching explained ones.
You can place the folder in the root or your project or even in the root of your software development projects -> use one file for all!
Some configurations shall be enabled:
The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174]
Here are listed most obvious and important general rules. Please check them carefully before you continue with other chapters.
clang-format
SHOULD be used with formatting file attached to this repository (version 15.x
is a minimum)C11
standard4
spaces per indent level1
space between keyword and opening bracket/* OK */
if (condition)
while (condition)
for (init; condition; step)
do {} while (condition)
/* Wrong */
if(condition)
while(condition)
for(init;condition;step)
do {} while(condition)
int32_t a = sum(4, 3); /* OK */
int32_t a = sum (4, 3); /* Wrong */
__
or _
prefix for variables/functions/macros/types. This is reserved for C language itself
prv_
name prefix for strictly module-private (static) functionslibname_int_
or libnamei_
prefix for library internal functions, that should not be used by the user application while they MUST be used across different library internal modules_
charfor
, while
, do
, switch
, if
, ...)size_t i;
for (i = 0; i < 5; ++i) { /* OK */
}
for (i = 0; i < 5; ++i){ /* Wrong */
}
for (i = 0; i < 5; ++i) /* Wrong */
{
}
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 */
func_name(5, 4); /* OK */
func_name(4,3); /* Wrong */
global
variables to any default value (or NULL
), implement it in the dedicated init
function (if REQUIRED).static int32_t a; /* Wrong */
static int32_t b = 4; /* Wrong */
static int32_t a = 0; /* Wrong */
In embedded systems, it is very common that RAM memories are scattered across different memory locations in the system. It quickly becomes tricky to handle all the cases, especially when user declares custom RAM sections. Startup script is in-charge to set default values (.data and .bss) while other custom sections may not be filled with default values, which leads to variables with init value won't have any effect.
To be independent of such problem, create init function for each module and use it to set default values for all of your variables, like so:
static int32_t a; /* OK */
static int32_t b = 4; /* Wrong - this value may not be set at zero
if linker script&startup files are not properly handled */
void
my_module_init(void) {
a = 0;
b = 4;
}
void
my_func(void) {
/* 1 */
char a; /* OK */
/* 2 */
char a, b; /* OK */
/* 3 */
char a;
char b; /* Wrong, variable with char type already exists */
}
int
my_func(void) {
/* 1 */
my_struct_t my; /* First custom structures */
my_struct_ptr_t* p; /* Pointers too */
/* 2 */
uint32_t a;
int32_t b;
uint16_t c;
int16_t g;
char h;
/* ... */
/* 3 */
double d;
float f;
}
typedef struct {
int a, b;
} str_t;
str_t s = {
.a = 1,
.b = 2, /* Comma here */
}
/* Examples of "complex" structure, with or with missing several trailing commas, after clang-format runs the formatting */
static const my_struct_t my_var_1 = {
.type = TYPE1,
.type_data =
{
.type1 =
{
.par1 = 0,
.par2 = 1, /* Trailing comma here */
}, /* Trailing comma here */
}, /* Trailing comma here */
};
static const my_struct_t my_var_2 = {.type = TYPE2,
.type_data = {
.type2 =
{
.par1 = 0,
.par2 = 1,
},
}}; /* Missing comma here */
static const my_struct_t my_var_3 = {.type = TYPE3,
.type_data = {.type3 = {
.par1 = 0,
.par2 = 1,
}}}; /* Missing 2 commas here */
/* No trailing commas - good only for small and simple structures */
static const my_struct_t my_var_4 = {.type = TYPE4, .type_data = {.type4 = {.par1 = 0, .par2 = 1}}};
for
loop/* OK */
for (size_t i = 0; i < 10; ++i)
/* OK, if you need 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) ...
void
a(void) {
/* Avoid function calls when declaring variable */
int32_t a, b = sum(1, 2);
/* Use this */
int32_t a, b;
b = sum(1, 2);
/* This is ok */
uint8_t a = 3, b = 4;
}
char
, float
or double
, always use types declared in stdint.h
library, eg. uint8_t
for unsigned 8-bit
, etc.stdbool.h
library. Use 1
or 0
for true
or false
respectively/* OK */
uint8_t status;
status = 0;
/* Wrong */
#include <stdbool.h>
bool status = true;
true
, eg. if (check_func() == 1)
, use if (check_func()) { ... }
NULL
valuevoid* ptr;
/* ... */
/* OK, compare against NULL */
if (ptr == NULL || ptr != NULL) {
}
/* Wrong */
if (ptr || !ptr) {
}
int32_t a = 0;
...
a++; /* Wrong */
++a; /* OK */
for (size_t j = 0; j < 10; ++j) {} /* OK */
size_t
for length or size variablesconst
for pointer if function should not modify memory pointed to by pointer
const
for function parameter or variable, if it should not be modified/* 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) {
}
/* Not REQUIRED, it is advised */
void
my_func(const size_t len) {
}
/* When d should not be modified inside function, only data pointed to by d could be modified */
void
my_func(void* const d) {
}
void *
, do not use uint8_t *
/*
* To send data, function should not modify memory pointed to by `data` variable
* thus `const` keyword is important
*
* To send generic data (or to write them to file)
* any type may be passed for data,
* thus use `void *`
*/
/* OK example */
void
send_data(const void* data, size_t len) { /* OK */
/* Do not cast `void *` or `const void *` */
const uint8_t* d = data;/* Function handles proper type for internal usage */
}
void
send_data(const void* data, int len) { /* Wrong, not not use int */
}
sizeof
operatormalloc
and free
functions or if library/project provides custom memory allocation, use its implementation
/* OK */
#include <stdlib.h>
void
my_func(size_t size) {
int32_t* arr;
arr = malloc(sizeof(*arr) * n); /* OK, Allocate memory */
arr = malloc(sizeof *arr * n); /* Wrong, brackets for sizeof operator are missing */
if (arr == NULL) {
/* FAIL, no memory */
}
free(arr); /* Free memory after usage */
}
/* Wrong */
void
my_func(size_t size) {
int32_t arr[size]; /* Wrong, do not use VLA */
}
boolean
typeboolean-treated
variables against zero or one. Use NOT (!
) insteadsize_t length = 5; /* Counter variable */
uint8_t is_ok = 0; /* Boolean-treated variable */
if (length) /* Wrong, length is not treated as boolean */
if (length > 0) /* OK, length is treated as counter variable containing multi values, not only 0 or 1 */
if (length == 0) /* OK, length is treated as counter variable containing multi values, not only 0 or 1 */
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 */
/* comment */
for comments, even for single-line commentC++
with extern
keyword in header filestatic
force_redraw
. Do not use forceRedraw
void *
, eg. uint8_t* ptr = (uint8_t *)func_returning_void_ptr();
as void *
is safely promoted to any other pointer type
uint8_t* ptr = func_returning_void_ptr();
instead<
and >
for C Standard Library include files, eg. #include <stdlib.h>
""
for custom libraries, eg. #include "my_library.h"
uint8_t* t = (uint8_t*)var_width_diff_type
//
are not allowed. Always use /* comment */
, even for single-line comment//This is comment (wrong)
/* This is comment (ok) */
space+asterisk
for every line/*
* 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)
*/
/* Single line comment (ok) */
12
indents (12 * 4
spaces) offset when commenting. If statement is larger than 12
indents, make comment 4-spaces
aligned (examples below) to next available indentvoid
my_func(void) {
char a, b;
a = call_func_returning_char_a(a); /* This is comment with 12*4 spaces indent from beginning of line */
b = call_func_returning_char_a_but_func_name_is_very_long(a); /* This is comment, aligned to 4-spaces indent */
}
_
character/* OK */
void my_func(void);
void myfunc(void);
/* Wrong */
void MYFunc(void);
void myFunc();
/* 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);
/* OK, function names aligned */
void set(int32_t a);
my_type_t get(void);
my_ptr_t* get_ptr(void);
/* Wrong */
void set(int32_t a);
const char * get(void);
/* OK */
int32_t
foo(void) {
return 0;
}
/* OK */
static const char*
get_string(void) {
return "Hello world!rn";
}
/* Wrong */
int32_t foo(void) {
return 0;
}
_
character/* OK */
int32_t a;
int32_t my_var;
int32_t myvar;
/* Wrong */
int32_t A;
int32_t myVar;
int32_t MYVar;
type
void
foo(void) {
int32_t a, b; /* OK */
char a;
char b; /* Wrong, char type already exists */
}
void
foo(void) {
int32_t a;
a = bar();
int32_t b; /* Wrong, there is already executable statement */
}
int32_t a, b;
a = foo();
if (a) {
int32_t c, d; /* OK, c and d are in if-statement scope */
c = foo();
int32_t e; /* Wrong, there was already executable statement inside block */
}
/* OK */
char* a;
/* Wrong */
char *a;
char * a;
/* OK */
char *p, *n;
_
character between wordstypedef
keywordWhen structure is declared, it may use one of 3
different options:
_t
suffix after its name.struct struct_name {
char* a;
char b;
};
_t
suffix after its name.typedef struct {
char* a;
char b;
} struct_name_t;
_t
for basic name and it MUST contain _t
suffix after its name for typedef part.typedef struct struct_name { /* No _t */
char* a;
char b;
char c;
} struct_name_t; /* _t */
Examples of bad declarations and their suggested corrections
/* a and b MUST be separated to 2 lines */
/* Name of structure with typedef MUST include _t suffix */
typedef struct {
int32_t a, b;
} a;
/* Corrected version */
typedef struct {
int32_t a;
int32_t b;
} a_t;
/* Wrong name, it MUST not include _t suffix */
struct name_t {
int32_t a;
int32_t b;
};
/* Wrong parameters, MUST be all uppercase */
typedef enum {
MY_ENUM_TESTA,
my_enum_testb,
} my_enum_t;
C99
initialization style/* OK */
a_t a = {
.a = 4,
.b = 5,
};
/* Wrong */
a_t a = {1, 2};
_fn
suffix/* Function accepts 2 parameters and returns uint8_t */
/* Name of typedef has `_fn` suffix */
typedef uint8_t (*my_func_typedef_fn)(uint8_t p1, const char* p2);
1
nested statement1
indent size for each nest/* OK */
if (c) {
do_a();
} else {
do_b();
}
/* Wrong */
if (c)
do_a();
else
do_b();
/* Wrong */
if (c) do_a();
else do_b();
if
or if-else-if
statement, else
MUST be in the same line as closing bracket of first statement/* OK */
if (a) {
} else if (b) {
} else {
}
/* Wrong */
if (a) {
}
else {
}
/* Wrong */
if (a) {
}
else
{
}
do-while
statement, while
part MUST be in the same line as closing bracket of do
part/* OK */
do {
int32_t a;
a = do_a();
do_b(a);
} while (check());
/* Wrong */
do
{
/* ... */
} while (check());
/* Wrong */
do {
/* ... */
}
while (check());
if (a) {
do_a();
} else {
do_b();
if (c) {
do_c();
}
}
if (a) do_b();
else do_c();
if (a) do_a(); else do_b();
while
, do-while
or for
loops MUST include brackets/* OK */
while (is_register_bit_set()) {}
/* Wrong */
while (is_register_bit_set());
while (is_register_bit_set()) { }
while (is_register_bit_set()) {
}
while
(or for
, do-while
, etc) is empty (it can be the case in embedded programming), use empty single-line brackets/* Wait for bit to be set in embedded hardware unit */
volatile uint32_t* addr = HW_PERIPH_REGISTER_ADDR;
/* Wait bit 13 to be ready */
while (*addr & (1 << 13)) {} /* OK, empty loop contains no spaces inside curly brackets */
while (*addr & (1 << 13)) { } /* Wrong */
while (*addr & (1 << 13)) { /* Wrong */
}
while (*addr & (1 << 13)); /* Wrong, curly brackets are missing. Can lead to compiler warnings or unintentional bugs */
for
, do-while
, while
/* Not recommended */
int32_t a = 0;
while (a < 10) {
.
..
...
++a;
}
/* Better */
for (size_t a = 0; a < 10; ++a) {
}
/* Better, if inc may not happen in every cycle */
for (size_t a = 0; a < 10; ) {
if (...) {
++a;
}
}
if
statement MAY be used only for assignment or function call operations/* OK */
int a = condition ? if_yes : if_no; /* Assignment */
func_call(condition ? if_yes : if_no); /* Function call */
switch (condition ? if_yes : if_no) {...} /* OK */
/* Wrong, this code is not well maintenable */
condition ? call_to_function_a() : call_to_function_b();
/* Rework to have better program flow */
if (condition) {
call_to_function_a();
} else {
call_to_function_b();
}
case
statementbreak
statement in each case
or default
statement/* OK, every case has single indent */
/* OK, every break has additional indent */
switch (check()) {
case 0:
do_a();
break;
case 1:
do_b();
break;
default:
break;
}
/* Wrong, case indent missing */
switch (check()) {
case 0:
do_a();
break;
case 1:
do_b();
break;
default:
break;
}
/* Wrong */
switch (check()) {
case 0:
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;
}
default
statement/* OK */
switch (var) {
case 0:
do_job();
break;
default:
break;
}
/* Wrong, default is missing */
switch (var) {
case 0:
do_job();
break;
}
break
statement inside.
case
statementswitch (a) {
/* OK */
case 0: {
int32_t a, b;
char c;
a = 5;
/* ... */
break;
}
/* Wrong */
case 1:
{
int32_t a;
break;
}
/* Wrong, break shall be inside */
case 2: {
int32_t a;
}
break;
}
_
character, except if they are clearly marked as function which may be in the future replaced with regular function syntax/* OK */
#define SQUARE(x) ((x) * (x))
/* Wrong */
#define square(x) ((x) * (x))
/* OK */
#define MIN(x, y) ((x) < (y) ? (x) : (y))
/* Wrong */
#define MIN(x, y) x < y ? x : y
/* Wrong */
#define MIN(x, y) (x) < (y) ? (x) : (y)
#define SUM(x, y) (x) + (y)
/* Imagine result of this equation using wrong SUM implementation */
int32_t x = 5 * SUM(3, 4); /* Expected result is 5 * 7 = 35 */
int32_t x = 5 * (3) + (4); /* It is evaluated to this, final result = 19 which is not what we expect */
/* Correct implementation */
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define SUM(x, y) ((x) + (y))
do {} while (0)
statementtypedef struct {
int32_t px, py;
} point_t;
point_t p; /* Define new point */
/* Wrong implementation */
/* Define macro to set point */
#define SET_POINT(p, x, y) (p)->px = (x); (p)->py = (y) /* 2 statements. Last one should not implement semicolon */
SET_POINT(&p, 3, 4); /* Set point to position 3, 4. This evaluates to... */
(&p)->px = (3); (&p)->py = (4); /* ... to this. In this example this is not a problem. */
/* Consider this ugly code, however it is valid by C standard (not recommended) */
if (a) /* If a is true */
if (b) /* If b is true */
SET_POINT(&p, 3, 4);/* Set point to x = 3, y = 4 */
else
SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */
/* Evaluates to code below. Do you see the problem? */
if (a)
if (b)
(&p)->px = (3); (&p)->py = (4);
else
(&p)->px = (5); (&p)->py = (6);
/* Or if we rewrite it a little */
if (a)
if (b)
(&p)->px = (3);
(&p)->py = (4);
else
(&p)->px = (5);
(&p)->py = (6);
/*
* Ask yourself a question: To which `if` statement does the `else` keyword belong?
*
* Based on first part of code, answer is straight-forward. To inner `if` statement when we check `b` condition
* Actual answer: Compilation error as `else` belongs nowhere
*/
/* Better and correct implementation of macro */
#define SET_POINT(p, x, y) do { (p)->px = (x); (p)->py = (y); } while (0) /* 2 statements. No semicolon after while loop */
/* Or even better */
#define SET_POINT(p, x, y) do { /* Backslash indicates statement continues in new line */
(p)->px = (x);
(p)->py = (y);
} while (0) /* 2 statements. No semicolon after while loop */
/* Now original code evaluates to */
if (a)
if (b)
do { (&p)->px = (3); (&p)->py = (4); } while (0);
else
do { (&p)->px = (5); (&p)->py = (6); } while (0);
/* Every part of `if` or `else` contains only `1` inner statement (do-while), hence this is valid evaluation */
/* To make code perfect, use brackets for every if-ifelse-else statements */
if (a) { /* If a is true */
if (b) { /* If b is true */
SET_POINT(&p, 3, 4);/* Set point to x = 3, y = 4 */
} else {
SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */
}
}
#ifdef
or #ifndef
. Use defined()
or !defined()
instead#ifdef XYZ
/* do something */
#endif /* XYZ */
if/elif/else/endif
statements/* OK */
#if defined(XYZ)
/* Do if XYZ defined */
#else /* defined(XYZ) */
/* Do if XYZ not defined */
#endif /* !defined(XYZ) */
/* Wrong */
#if defined(XYZ)
/* Do if XYZ defined */
#else
/* Do if XYZ not defined */
#endif
#if
statement/* OK */
#if defined(XYZ)
#if defined(ABC)
/* do when ABC defined */
#endif /* defined(ABC) */
#else /* defined(XYZ) */
/* Do when XYZ not defined */
#endif /* !defined(XYZ) */
/* Wrong */
#if defined(XYZ)
#if defined(ABC)
/* do when ABC defined */
#endif /* defined(ABC) */
#else /* defined(XYZ) */
/* Do when XYZ not defined */
#endif /* !defined(XYZ) */
Documented code allows doxygen to parse and generate html/pdf/latex output, thus it is very important to do it properly at an early stage of the project.
variables
, functions
and structures/enumerations
for doxygen, do not use @
5x4
spaces (5
tabs) offset from beginning of line for text/**
* brief Holds pointer to first entry in linked list
* Beginning of this text is 5 tabs (20 spaces) from beginning of line
*/
static
type_t* list;
/**
* brief This is point struct
* note This structure is used to calculate all point
* related stuff
*/
typedef struct {
int32_t x; /*!< Point X coordinate */
int32_t y; /*!< Point Y coordinate */
int32_t size; /*!< Point size.
Since comment is very big,
you may go to next line */
} point_t;
/**
* brief Point color enumeration
*/
typedef enum {
COLOR_RED, /*!< Red color */
COLOR_GREEN, /*!< Green color */
COLOR_BLUE, /*!< Blue color */
} point_color_t;
brief
and all parameters documentationin
or out
for input and output respectivelyreturn
parameter if it returns something. This does not apply for void
functionsnote
or warning
:
between parameter name and its description/**
* brief Sum `2` numbers
* param[in] a: First number
* param[in] b: Second number
* return Sum of input values
*/
int32_t
sum(int32_t a, int32_t b) {
return a + b;
}
/**
* brief Sum `2` numbers and write it to pointer
* note This function does not return value, it stores it to pointer instead
* param[in] a: First number
* param[in] b: Second number
* param[out] result: Output variable used to save result
*/
void
void_sum(int32_t a, int32_t b, int32_t* result) {
*result = a + b;
}
ref
keyword to specify which one/**
* brief My enumeration
*/
typedef enum {
MY_ERR, /*!< Error value */
MY_OK /*!< OK value */
} my_enum_t;
/**
* brief Check some value
* return ref MY_OK on success, member of ref my_enum_t otherwise
*/
my_enum_t
check_value(void) {
return MY_OK;
}
NULL
) for constants or numbers/**
* brief Get data from input array
* param[in] in: Input data
* return Pointer to output data on success, `NULL` otherwise
*/
const void *
get_data(const void* in) {
return in;
}
hideinitializer
doxygen command/**
* brief Get minimal value between `x` and `y`
* param[in] x: First value
* param[in] y: Second value
* return Minimal value between `x` and `y`
* hideinitializer
*/
#define MIN(x, y) ((x) < (y) ? (x) : (y))
file
and brief
description followed by empty line (when using doxygen)/**
* file template.h
* brief Template include file
*/
/* Here is empty line */
/**
* file template.h
* brief Template include file
*/
/*
* Copyright (c) year FirstName LASTNAME
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of library_name.
*
* Author: FirstName LASTNAME <[email protected]>
*/
#ifndef
C++
checkC++
checkextern
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
.c
file should first include corresponding .h
file, later others, unless otherwise explicitly necessary
Do not include module private declarations in header file
Header file example (no license for sake of an example)
/* License comes here */
#ifndef TEMPLATE_HDR_H
#define TEMPLATE_HDR_H
/* Include headers */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* File content here */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* TEMPLATE_HDR_H */
Repository comes with always-up-to-date .clang-format
file, an input configuration
for clang-format
tool. It can be seamlessly integrated with most of latest techno
IDEs, including VSCode. Formatting then happens on the spot on each file save.
https://code.visualstudio.com/docs/cpp/cpp-ide#_code-formatting
AStyle is a great piece of software that can help with formatting the code based on input configuration.
This repository contains astyle-code-format.cfg
file which can be used with AStyle
software.
astyle --options="astyle-code-format.cfg" "input_path/*.c,*.h" "input_path2/*.c,*.h"
Artistic style configuration is obsolete and no longer updated
Repository contains eclipse-ext-kr-format.xml
file that can be used with
eclipse-based toolchains to set formatter options.
It is based on K&R formatter with modifications to respect above rules.
You can import it within eclipse settings, Preferences -> LANGUAGE -> Code Style -> Formatter
tab.