Difference between revisions of "MansOS Coding Standard"
m (→Constants and defines) |
|||
(9 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
{{TocRight}} |
|||
Be advised to use the following coding style when writing or editing '''[[MansOS]]''' source code. |
|||
== General guidelines == |
|||
# The world of miniature embedded systems has limited resources. Therefore, the code must be very efficient. |
|||
== General == |
|||
# Provide easy to use and simple API. There are many users that are less than advanced in programming. In addition, but not at a great code expense, you may also provide advanced features for the professional developers. |
|||
# Keep API short. That means less code and easier to understand. |
|||
# When trading the data size with code size it is often better to keep the data smaller. Micro-controllers (MCU) tend to have more program memory than data memory. |
|||
# When trading code size for execution time often it is better to reduce the code size at the expense of time, providing that the time increase is O(constant). Often the MCU have free execution cycles available. |
|||
#* Beware, however, that the increased execution time will burn more energy. |
|||
#* Beware also, that there might be sections of code where the short execution time is more important, for example, in the interrupt handlers. |
|||
== General issues == |
|||
* Write comments. Respect the reader! |
|||
=== Indentation === |
|||
* Keep the line length under 78 to help the viewer and those that want to print your code. Break the line in several as necessary. |
|||
Never ever exceed the line length of 120 symbols. |
|||
'''Do not use the TAB character for indentation, use spaces instead'''. Otherwise the code indentation may break when viewed by different developers. |
|||
Use '''indentation at 4 spaces'''. You may find it useful to set TAB key to 4 spaces in your editor. |
|||
== |
=== Size limits === |
||
Keep the '''line length under 80 characters''' to help the viewer and those that want to print your code. Break the line in several as necessary. |
|||
Indent new code blocks by 4 spaces |
|||
Functions in general should be no longer than ~50 lines (one screen). Functions that has only simple logic (e.g. initialization function) can be longer. |
|||
Note that TAB character is always 8 spaces. |
|||
However, set your editor to use spaces instead of the TAB character to avoid confusion when loading text files that may contain tab characters and was written using a different editor. |
|||
Try to not use too much levels of indentation - code is harder to read if there are too many. |
|||
You may want to select TAB key to indent and Backspace key to un-indent in your text editor (SciTe, Emacs, ...) |
|||
=== Line endings === |
|||
Always use UNIX-style line endings (newline charachter only). Avoid Windows-style line endings (newline + carriage return). |
|||
== Variable and function names == |
|||
Use <code>fromdos</code> command line utility to convert files to the proper format, if necessary. |
|||
Variable names and function names shall start in lowercase and be written in [http://en.wikipedia.org/wiki/Camelcase CamelCase] - writeByte, not write_byte, wrt_byte or WrItEbYtE. |
|||
=== Cross platform code === |
|||
int writeByte; |
|||
void printByte(void *buf); |
|||
Try to put all platform specific code in appropriate .h or .c files; avoid having <code>#ifdef</code>'s in code, when possible. |
|||
Use: |
|||
// In pc/platfor_header.h: |
|||
#define PLATFORM_SPECIFIC_CONST 1 |
|||
... |
|||
// In telosb/platfor_header.h: |
|||
#define PLATFORM_SPECIFIC_CONST 2 |
|||
... |
|||
// In .c file: |
|||
a = PLATFORM_SPECIFIC_CONST; |
|||
Avoid: |
|||
// In .c file |
|||
#ifdef PC |
|||
a = 1; |
|||
#else |
|||
a = 2; |
|||
#endif |
|||
=== Inline functions === |
|||
Linux coding style manual suggests that <em>"a reasonable rule of thumb is to not put inline at functions that have more |
|||
than 3 lines of code in them"</em>. For embedded systems this suggestion may be relaxed (to avoid function call overhead), but they should be kept at reasonable size. |
|||
'''Note!''' The compiler can make decision to inline even functions that are not declared as inlines, but only if the definition of that function is visible at the point where it is called. |
|||
=== |
=== Warnings === |
||
Do not ignore compilation warnings. Never commit code that compiles with warnings, unless you have a very good reason! |
|||
Add trailing "_p" to show this is a pointer |
|||
If there are really "invalid" warnings, it's better to disable them in Makefile than to ignore them. |
|||
int writeByte; |
|||
int writeByte_p; <-- like this |
|||
== Types == |
|||
=== Constants and defines === |
=== Constants and defines === |
||
Define constants with '''#define''' preprocessor directive or (preferred) as '''enum''' member. |
|||
Use all-uppercase for <code>#define</code> constants, with underscore between the words |
|||
The '''enum''' way is preferred because a symbol table entry is created in this way (can be useful for debugging), and the scope of the declaration is honoured. |
|||
#define MAX_LENGTH 15 |
|||
#define HEIGHT 7 |
|||
Do no use '''const''' keyword, except in cases where it is really needed. '''const''' keyword should not be used, because in this way not only a symbol, but also a variable is always created. Note that this only applies to C, for C++ '''const''' can be used to make real constants. |
|||
For constants use first letter in uppercase with the rest in camelcase |
|||
Add trailing _c to show this is a constant. |
|||
Use all-uppercase names for constants, with underscore between the words. |
|||
const MaxBufferSize_c = 64; |
|||
Use: |
|||
When using <code>#ifdef</code> or <code>#if</code> constructs, |
|||
#define DEFINE_A 123 |
|||
you should always use comments for <code>#endif</code>. |
|||
#define DEFINE_B 1000000ul |
|||
The comment for <code>#endif</code> should match the expression used in the corresponding |
|||
#define DEFINE_FLAGS 0xEC |
|||
<code>#if</code> or <code>#ifdef</code>. The comment for <code>#else</code> and <code>#elif</code> should match the |
|||
enum { DEFINE_C = 123 }; |
|||
inverse of the expression(s) used in the preceding <code>#if</code> and/or <code>#elif</code> |
|||
statements. |
|||
Avoid: |
|||
const unsigned VARIABLE_C = 123; |
|||
Avoid magic numbers. Use symbolic constants, defined in one way or another. |
|||
=== Integer types === |
|||
Use only in integer types with explicitly specified size. This makes portability easier. |
|||
Use: |
|||
int8_t a; |
|||
uint32_t b; |
|||
Avoid: |
|||
char a; |
|||
unsigned long b; |
|||
=== Bool type === |
|||
Use <code>bool</code> type where possible. |
|||
=== Comments === |
|||
Respect the reader and write comments. In general, comment '''what''' your code does, not '''how'''. |
|||
==== Inline comments ==== |
|||
Inside functions use comments only when necessary. In most cases, if you see some code that cannot be understood without commenting, that means you should ''rewrite'' it, not ''comment'' it! |
|||
Use descriptive function and variable names to reduce the need to comment your code. |
|||
Do not overcomment the code. Why? First, comments are not checked by compiler. That means they are often "broken" - invalid or out of date. Second, the more comments there are, the more difficult is to keep them up to date! |
|||
==== Comments in global scope ==== |
|||
Describe what a function does before to it's prototype, unless that's obvious. |
|||
Example (quite obvious): |
|||
// |
|||
// Calculate greatest common divisor (GCD) of 'a' and 'b' |
|||
// |
|||
uint32_t gcd(uint32_t a, uint32_t b); |
|||
In general, there is no need to document function definitions. All exported functions should have prototypes anyway! |
|||
==== Preprocessor directives and comments ==== |
|||
When using <code>#ifdef</code> or <code>#if</code> constructs, you should use comments for <code>#endif</code> if the code blocks are long enough. The comment for <code>#endif</code> should match the expression used in the corresponding <code>#if</code> or <code>#ifdef</code>. The comment for <code>#else</code> and <code>#elif</code> should match the inverse of the expression(s) used in the preceding <code>#if</code> and/or <code>#elif</code> statements. |
|||
<code> |
<code> |
||
#ifdef MSP430 |
#ifdef MSP430 |
||
/* msp430 stuff here */ |
|||
/* msp430 stuff here */ |
|||
#else // !MSP430 |
#else // !MSP430 |
||
/* non msp430 stuff here */ |
|||
/* non msp430 stuff here */ |
|||
#endif // MSP430 |
#endif // MSP430 |
||
</code> |
|||
==== Special comments ==== |
|||
Use: |
|||
* '''XXX''' in comments to mark code that may not be strictly correct, but is working. |
|||
* '''FIXME''' in comments to mark code that does not work correctly. |
|||
* '''TODO''' in comments to mark places for not-yet-made features. |
|||
== Syntax == |
|||
=== Variable and function names === |
|||
Variable names and function names shall start in lowercase and be written in [http://en.wikipedia.org/wiki/Camelcase CamelCase] - <code>writeByte</code>, not <code>write_byte, wrt_byte</code> or <code>WrItEbYtE</code>. |
|||
<code> |
|||
int writeByte; |
|||
void printByte(void *buf); |
|||
</code> |
</code> |
||
=== Enums, structs and all other types === |
=== Enums, structs and all other types === |
||
Use the first letter in uppercase, the rest is CamelCase. |
Use the first letter in uppercase, the rest is CamelCase. Add trailing "<code>_t</code>" to show this is a type, or "<code>_e</code>" to show this is an enum. |
||
Add trailing "_t" to show this is a type, or "_e" to show this is an enum |
|||
<code> |
<code> |
||
Line 78: | Line 170: | ||
int c; |
int c; |
||
} MyStruct_t; |
} MyStruct_t; |
||
</code> |
|||
Enums example: |
|||
<code> |
|||
typedef enum |
typedef enum |
||
{ |
{ |
||
Line 94: | Line 181: | ||
Enumeration values all should be written in uppercase. |
Enumeration values all should be written in uppercase. |
||
=== Code control structures === |
|||
<code> |
|||
typedef enum { WHITE, BLACK } Color_e; |
|||
</code> |
|||
== |
==== Using braces ==== |
||
Place brace in the same line for short functions (3 lines or less), and place braces in next line for long functions. |
|||
=== if, else, for, while, do === |
|||
Use: |
|||
Always use braces after <code>if(), else, for(), while(), do</code>, even when there is only one statement in the block. |
|||
void shortFunction(void) { |
|||
There might be a few exceptions when if and the statement is on the same line and unmistakeably has one simple statement such as an assignment. |
|||
doThis(); |
|||
doThat(); |
|||
} |
|||
void longFunction(void) |
|||
{ |
|||
doSomething(); |
|||
doSomethingElse(); |
|||
// ... 10 more lines ... |
|||
} |
|||
Always use braces after <code>if(), else, for(), while(), do</code>, even when there is only one statement in the block. There might be a few exceptions when if and the statement is on the same line and unmistakeably has one simple statement such as an assignment. |
|||
You may write the opening brace on the same or the next line. Use common sense. Generally to make a short if() with just one statement in the body I use it on the same line. |
|||
You may write the opening brace on the same or the next line. Use common sense. Generally to make a short <code>if()</code> with just one statement in the body I use it on the same line. |
|||
<code> |
|||
if ( 17 == a ) b++; |
|||
Use: |
|||
if (17 == a) b++; |
|||
if (...) { |
if (...) { |
||
foo(); |
foo(); |
||
} |
} |
||
if (...) |
if (...) |
||
{ |
{ |
||
Line 119: | Line 216: | ||
bar(); |
bar(); |
||
} |
} |
||
</code> |
|||
Avoid: |
|||
When using comparisons in control structures, always try to use left-hand comparisons. |
|||
if (foo) |
|||
Because these types of comparisons detects typo errors early. |
|||
bar(); |
|||
Typical errors arise in right-hand style when equality operator is required but assignment operator is used, |
|||
in left-hand style these typos will result in compilation error and crash early. |
|||
This should be avoid because the code is harder to modify (e.g. add more statements insides the body of the <code>if</code> statement) in the future; and because the code flow may be unclear in some cases. |
|||
daily usage of comparisons - right hand style |
|||
<code> |
|||
if ( a == 17 ) |
|||
{ |
|||
... |
|||
} |
|||
</code> |
|||
==== <code>switch</code> statement ==== |
|||
left-hand style |
|||
<code> |
|||
if ( 17 == a ) |
|||
{ |
|||
... |
|||
} |
|||
</code> |
|||
In all places where <code>break</code> keyword is not being used by intention, a <code>/* falls through */</code> comment should be inserted. |
|||
=== Spacing in expressions === |
|||
Example: |
|||
Use space to separate tokens in expressions and braces everywhere in the code |
|||
switch (condition) { |
|||
However, you may use no space between unary operation and operand, for example, i++; |
|||
case A: |
|||
if (x == 13) return 5; |
|||
// falls through |
|||
case B: |
|||
case C: |
|||
return 6; |
|||
} |
|||
==== Spacing in expressions ==== |
|||
Use space to separate tokens in expressions and braces everywhere in the code. However, you may use no space between unary operation and operand, for example, <code>i++;</code> |
|||
x = y + 16 - z++; |
x = y + 16 - z++; |
||
if( 17 == a ) { |
if ( 17 == a ) { |
||
b = foo( c, d ); |
b = foo( c, d ); |
||
} |
} |
||
Use spaces before braces with <code>if, for, while</code>. |
|||
=== Function and procedure heads === |
|||
Do not use space before braces in <code>foo()</code> (i.e. function calls). |
|||
Start every function or procedure with a comment block describing it. |
|||
The return type for functions may be on a separate line prior to the function name to make the function fit in a single line. |
|||
Use spaces around binary and ternary operators: |
|||
//----------------------------------------------------------------------------- |
|||
x = a ? b + c : d; |
|||
// Prints formated text in a box with x,y,w,h parameters. |
|||
//----------------------------------------------------------------------------- |
|||
=== Variable initialization === |
|||
uint32_t |
|||
printInBox(char* text, char *format, int16_t x, int16_t y, int16_t w, int16_t z) |
|||
No explicit global variable initialization to zeros need, because by C standard all static variables (including global) are by default initialized to zero. |
|||
{ |
|||
... |
|||
Use: |
|||
} |
|||
uint8_t array1[10] = {0}; // all members set to zero |
|||
uint8_t array2[10]; // all members set to zero by default |
|||
uint8_t array3[10] = {1, 2, 3}; // all except first three members set to zero by default |
|||
int someInteger; // 0 by default |
|||
Avoid: |
|||
uint8_t array[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
|||
int someInteger = 0; |
|||
If for some reason you want to avoid initalization, use "noinit" attribute. For example, this may be need for large arrays, because apparently the initialization can take long enough time for watchdog timer to expire! |
|||
Example: |
|||
uint16_t array[500] __attribute__((section (".noinit"))); |
|||
For some compilers there is __no_init keyword that does the same: |
|||
__no_init uint16_t array[500]; |
|||
Structures can be initialized like this: |
|||
typedef struct { |
|||
uint16_t i; |
|||
int8_t c; |
|||
uint8_t a[3]; |
|||
} SomeStruct_t; |
|||
SomeStruct_t var1 = {123, 'a', {1, 2, 3}}; |
|||
SomeStruct_t var2 = {123}; // member 'i' is 123, all other members are implicitly initialized to 0 |
|||
Or (the preferred way): |
|||
SomeStruct_t var3 = { |
|||
.i = 123, |
|||
.c = 'a', |
|||
.a = {1, 2, 3} |
|||
}; |
|||
=== Include paths === |
|||
All internal MansOS code should use include paths with directory explicitly specified. |
|||
Use: |
|||
#include <lib/dprint.h> |
|||
Avoid: |
|||
#include <dprint.h> |
|||
#include "dprint.h" |
|||
This is not a requirement for ''application'' code! |
|||
This is also not a requirement if the included file is under <code>arch</code> directories, because in this case the path is platform specific. Because of this, it is recommended to add <code>_hal</code> suffix in those filenames, for example, <code>radio_hal.h</code>. |
|||
== Conclusion == |
|||
Remember that rules are made to be broken; do not follow them blindly! |
Latest revision as of 18:15, 14 November 2012
General guidelines
- The world of miniature embedded systems has limited resources. Therefore, the code must be very efficient.
- Provide easy to use and simple API. There are many users that are less than advanced in programming. In addition, but not at a great code expense, you may also provide advanced features for the professional developers.
- Keep API short. That means less code and easier to understand.
- When trading the data size with code size it is often better to keep the data smaller. Micro-controllers (MCU) tend to have more program memory than data memory.
- When trading code size for execution time often it is better to reduce the code size at the expense of time, providing that the time increase is O(constant). Often the MCU have free execution cycles available.
- Beware, however, that the increased execution time will burn more energy.
- Beware also, that there might be sections of code where the short execution time is more important, for example, in the interrupt handlers.
General issues
Indentation
Do not use the TAB character for indentation, use spaces instead. Otherwise the code indentation may break when viewed by different developers. Use indentation at 4 spaces. You may find it useful to set TAB key to 4 spaces in your editor.
Size limits
Keep the line length under 80 characters to help the viewer and those that want to print your code. Break the line in several as necessary.
Functions in general should be no longer than ~50 lines (one screen). Functions that has only simple logic (e.g. initialization function) can be longer.
Try to not use too much levels of indentation - code is harder to read if there are too many.
Line endings
Always use UNIX-style line endings (newline charachter only). Avoid Windows-style line endings (newline + carriage return).
Use fromdos
command line utility to convert files to the proper format, if necessary.
Cross platform code
Try to put all platform specific code in appropriate .h or .c files; avoid having #ifdef
's in code, when possible.
Use:
// In pc/platfor_header.h: #define PLATFORM_SPECIFIC_CONST 1 ... // In telosb/platfor_header.h: #define PLATFORM_SPECIFIC_CONST 2 ... // In .c file: a = PLATFORM_SPECIFIC_CONST;
Avoid:
// In .c file #ifdef PC a = 1; #else a = 2; #endif
Inline functions
Linux coding style manual suggests that "a reasonable rule of thumb is to not put inline at functions that have more than 3 lines of code in them". For embedded systems this suggestion may be relaxed (to avoid function call overhead), but they should be kept at reasonable size.
Note! The compiler can make decision to inline even functions that are not declared as inlines, but only if the definition of that function is visible at the point where it is called.
Warnings
Do not ignore compilation warnings. Never commit code that compiles with warnings, unless you have a very good reason!
If there are really "invalid" warnings, it's better to disable them in Makefile than to ignore them.
Types
Constants and defines
Define constants with #define preprocessor directive or (preferred) as enum member.
The enum way is preferred because a symbol table entry is created in this way (can be useful for debugging), and the scope of the declaration is honoured.
Do no use const keyword, except in cases where it is really needed. const keyword should not be used, because in this way not only a symbol, but also a variable is always created. Note that this only applies to C, for C++ const can be used to make real constants.
Use all-uppercase names for constants, with underscore between the words.
Use:
#define DEFINE_A 123 #define DEFINE_B 1000000ul #define DEFINE_FLAGS 0xEC enum { DEFINE_C = 123 };
Avoid:
const unsigned VARIABLE_C = 123;
Avoid magic numbers. Use symbolic constants, defined in one way or another.
Integer types
Use only in integer types with explicitly specified size. This makes portability easier.
Use:
int8_t a; uint32_t b;
Avoid:
char a; unsigned long b;
Bool type
Use bool
type where possible.
Comments
Respect the reader and write comments. In general, comment what your code does, not how.
Inline comments
Inside functions use comments only when necessary. In most cases, if you see some code that cannot be understood without commenting, that means you should rewrite it, not comment it!
Use descriptive function and variable names to reduce the need to comment your code.
Do not overcomment the code. Why? First, comments are not checked by compiler. That means they are often "broken" - invalid or out of date. Second, the more comments there are, the more difficult is to keep them up to date!
Comments in global scope
Describe what a function does before to it's prototype, unless that's obvious.
Example (quite obvious):
// // Calculate greatest common divisor (GCD) of 'a' and 'b' // uint32_t gcd(uint32_t a, uint32_t b);
In general, there is no need to document function definitions. All exported functions should have prototypes anyway!
Preprocessor directives and comments
When using #ifdef
or #if
constructs, you should use comments for #endif
if the code blocks are long enough. The comment for #endif
should match the expression used in the corresponding #if
or #ifdef
. The comment for #else
and #elif
should match the inverse of the expression(s) used in the preceding #if
and/or #elif
statements.
#ifdef MSP430
/* msp430 stuff here */
#else // !MSP430
/* non msp430 stuff here */
#endif // MSP430
Special comments
Use:
- XXX in comments to mark code that may not be strictly correct, but is working.
- FIXME in comments to mark code that does not work correctly.
- TODO in comments to mark places for not-yet-made features.
Syntax
Variable and function names
Variable names and function names shall start in lowercase and be written in CamelCase - writeByte
, not write_byte, wrt_byte
or WrItEbYtE
.
int writeByte;
void printByte(void *buf);
Enums, structs and all other types
Use the first letter in uppercase, the rest is CamelCase. Add trailing "_t
" to show this is a type, or "_e
" to show this is an enum.
typedef struct
{
int a;
int b;
int c;
} MyStruct_t;
typedef enum
{
RECTANGLE,
CIRCLE = 17,
LAST // Use "Last" as the last enum available, as needed
} ShapeType_e;
Enumeration values all should be written in uppercase.
Code control structures
Using braces
Place brace in the same line for short functions (3 lines or less), and place braces in next line for long functions.
Use:
void shortFunction(void) { doThis(); doThat(); } void longFunction(void) { doSomething(); doSomethingElse(); // ... 10 more lines ... }
Always use braces after if(), else, for(), while(), do
, even when there is only one statement in the block. There might be a few exceptions when if and the statement is on the same line and unmistakeably has one simple statement such as an assignment.
You may write the opening brace on the same or the next line. Use common sense. Generally to make a short if()
with just one statement in the body I use it on the same line.
Use:
if (17 == a) b++; if (...) { foo(); } if (...) { foo(); bar(); }
Avoid:
if (foo) bar();
This should be avoid because the code is harder to modify (e.g. add more statements insides the body of the if
statement) in the future; and because the code flow may be unclear in some cases.
switch
statement
In all places where break
keyword is not being used by intention, a /* falls through */
comment should be inserted.
Example:
switch (condition) { case A: if (x == 13) return 5; // falls through case B: case C: return 6; }
Spacing in expressions
Use space to separate tokens in expressions and braces everywhere in the code. However, you may use no space between unary operation and operand, for example, i++;
x = y + 16 - z++;
if ( 17 == a ) { b = foo( c, d ); }
Use spaces before braces with if, for, while
.
Do not use space before braces in foo()
(i.e. function calls).
Use spaces around binary and ternary operators:
x = a ? b + c : d;
Variable initialization
No explicit global variable initialization to zeros need, because by C standard all static variables (including global) are by default initialized to zero.
Use:
uint8_t array1[10] = {0}; // all members set to zero uint8_t array2[10]; // all members set to zero by default uint8_t array3[10] = {1, 2, 3}; // all except first three members set to zero by default int someInteger; // 0 by default
Avoid:
uint8_t array[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int someInteger = 0;
If for some reason you want to avoid initalization, use "noinit" attribute. For example, this may be need for large arrays, because apparently the initialization can take long enough time for watchdog timer to expire!
Example:
uint16_t array[500] __attribute__((section (".noinit")));
For some compilers there is __no_init keyword that does the same:
__no_init uint16_t array[500];
Structures can be initialized like this:
typedef struct { uint16_t i; int8_t c; uint8_t a[3]; } SomeStruct_t; SomeStruct_t var1 = {123, 'a', {1, 2, 3}}; SomeStruct_t var2 = {123}; // member 'i' is 123, all other members are implicitly initialized to 0
Or (the preferred way):
SomeStruct_t var3 = { .i = 123, .c = 'a', .a = {1, 2, 3} };
Include paths
All internal MansOS code should use include paths with directory explicitly specified. Use:
#include <lib/dprint.h>
Avoid:
#include <dprint.h> #include "dprint.h"
This is not a requirement for application code!
This is also not a requirement if the included file is under arch
directories, because in this case the path is platform specific. Because of this, it is recommended to add _hal
suffix in those filenames, for example, radio_hal.h
.
Conclusion
Remember that rules are made to be broken; do not follow them blindly!