Choco OS
V.0.16.9.0
Join to the chocolate world
|
This is a document that describes coding style for the ChocoOS system sources. It is required to follow this rules in case of coding part of the system. This provides, that it will be easy to understand independently from author of the module. Moreover this rules helps to quickly find bugs and add new features to the existing code. Note, that each rule has assigned it's unique number with &
sign. Thanks to that it is possible to use rule number during code review.
The first of all, the project is adapted to work with Eclipse IDE. There is special file, in the resources directory, named code_style.xml that can be imported in the eclipse environment. It contains general style for the system.
&1. Margin for the project is set to 140. This should be readable for most of todays screens.
&2. Do not use tabs - most of editors supports system, that replace tabs to spaces. The width of the tab should be set to 4 spaces. This is because tabs are treated by editors differently, but the code must look the same in each one.
&3. In each case, braces should be inserted in separated line. It is easier to find blocks using this rule.
For example this is bad:
And this is good:
&4. Spaces should be inserted after opening parenthesis and before closing parenthesis like in the following example:
&5. Each block keyword (if,else,else if,do,while,for,switch) should be placed in new line (the 'do..while' is an exception from this rule)
Examples that shows this rule:
&6. Single statement blocks are prohibited. The reason of it is that there is a risk, that someone will add new line to the block and will forgot that only the first line will executed. This is presented in the example below:
Suppose, that someone want to add bar() function to this condition:
Note, that only foo()
will be executed in the condition statement. The bar()
will be executed in any time. The correct way to write this if
is as follows:
&7. Avoid magic numbers - each number in the code should be defined as definition, value of enumerator, or constant variable in the place that provide usage of it everywhere when it can be useful. Every time, when you are need to place some number in the code, you should try to name it and create some definition for it. Only the 0 value does not need to be defined. The code below represents some examples for this rule:
The example above shows magic numbers that are inserted into the function. It is not clear why these numbers were used - why the Index is initialized with 9, and not 0, why the Index is incremented to 33, and why it is compared with the "hello" string. Each one of these values should be defined. Here is correct version of this function:
&8. There can be only one return in function and only one break in block. When a function need to return some value, the variable should be created, and set to this value to return at the end of the function. The same is in case of 'break' keyword. Here some example:
&9. Use enumerators instead of integers when it is possible. When you need to create some status type, commands list, etc, you should define a special enum type for it, instead of use universal integer. It is easier to find out what values can be stored in the type. For example:
&10. Use the error code type (oC_ErrorCode_t
from the oc_errors.h header) to return result of an operation everywhere where it is possible. You can add your own codes to the list, but you should firstly ensure, that this code not exist already.
&11. Each function, type, global variable, definition, macro and value, that is part of the system and is not local, must starts with prefix oC_
. Note that when it is only local thing, it cannot contain it. This rule ensure, that names will not be duplicated, and helps to identify local names.
&12. Each function, type, global variable, definition, macro and value name, that is not local, must contain name of the module, that is a part of. Name of the module is given after oC_
prefix, as in example below:
There are some exceptions from this rule, but in each case it should be discussed with the ChocoOS team owner. Moreover it is possible to define a type, or function that is specific and unique for the module using only module name without type/function name. For example:
Moreover name of the module should be written with uppercase only when it is driver name, otherwise only the first letter of the word should be uppercase (also if a word is shortcut).
&13. Each type should be defined used typedef
keyword, and the name must contain _t
suffix. It is for comfortable usage of types, and for recognition of its. Here some example:
&14. Avoid using shortcuts. It is allowed only in special situations (in case of names longer than 60 characters or when the shortcut exist in standard C libraries), and in modules names.
&15. Do not use i
,j
,k
for index variables. It is easier to find a bug and understand how it works, when the index is named as its destination. For example:
&16. Global variables must starts with uppercase for each word. Moreover, when the variable is not static, it must contain prefix with oC_
and name of a module. For example:
&17. Local variables must starts with lowercase and then must contain uppercase for each word. Some examples below:
&18. Name of variable must describe, what it stores. It cannot contain predicates, and there should be noun at the end of the name. Here examples:
&19. Output parameters in functions must starts with out
prefix as in example below:
&20. Name of function must describe, what it do. It must be in order form, and it must contain predicate. There are only few exception from this rule - is
and new
functions types. Function names should be written without words: 'a', 'the' and 'an'. Here some examples:
&21. There is few special types of function, that should be defined always with following rules:
Set
- Sets parameter to the value. It must return oC_ErrorCode_t Example: Read
- Reads parameter state and return it via function argument or reads data from a stream. It must get outData
or outBuffer
argument and must return oC_ErrorCode_t. Example: Write
- Writes data to a stream, for example to the SPI channel queue. It must return oC_ErrorCode_t, and get Data
or Buffer
argument. Example: Get
- Returns parameter state via return value. It is important, that this function type is for reasons of speed operations, so it must not checks arguments. Example: Is
- Checks if the argument is correct. It must return bool
type. Example: TurnOn
- Prepares a module to work. It must return oC_ErrorCode_t type. Example: TurnOff
- Release module. It must work after call of this function. Function must return oC_ErrorCode_t type. Example: New
- Allocates and initialize memory for an object. It must return a pointer to the allocated memory. Example: Delete
- Deletes and free memory allocated for an object. It must take a double pointer (pointer to pointer) to the object, set the object pointer to the NULL, and return oC_ErrorCode_t type. Example: &22. Enumerators values must starts with the name of the type (of course without _t
suffix). Most of todays editors supports hints system. Thanks to that it will be easier to find values that are assigned for the enum type (for example in eclipse press Ctrl+Space). There is some example below, that illustrate this rule:
&23. Constant macros definitions must be named uppercase, with _
words seperated by _
sign. Modules names in it should also be written uppercase, but oC_
stay without changes. Example:
&24. Function-like macros should be named like functions
&25. Hungarian notation is prohibited! Do not use any suffixes or prefixes that means what type is stored in the variable.
&26. The sign =
in adjacent lines must always be aligned. The example below represent this rule:
&27. Names of types and names of variables in variables definitions should also be aligned like in example:
&28. Everytime when operation is repeated in adjacent lines without or with small changes, it should be aligned to emphasize these differences. Example:
&29. Each type in the system must be defined using typedef
keyword. It is easier to use.
&30. Only name of the type is required. There is no reason to set also enum, or struct name. The example below shows this rule:
The _SomeEnumType_t word is not necessary.
&31. Fields in structures should be ordered via size from the biggest to the smallest one. This helps to save memory (alignment). Example:
&32. Interface functions must check all arguments. It should be checked as much as possible. Note, that there are functions in the memory manager (in kernel for core layer) and in the standard memory libraries, that helps to verify if the pointer is correct. You should use them everywhere where it is a possible to give some pointer. Moreover it is possible to check the size of dynamic allocated address (from malloc
type functions).
&33. Static and internal module functions must not check arguments. It should be done in interface functions.
&34. Use oC_ErrorCode_t
type for as status. Dont be afraid to add new error codes, but always you could try to use existing.
&35. Error codes variables should in functions should be initialized as oC_ErrorCode_ImplementError
. Thanks to that, when you will not set correct state for each condition in a function, you will receive an error, that you will find and track shortly. Remember, that always it is better to return failure, when everything is OK, than return success, when something is wrong. Example:
&36. Definitions of variables should be ordered via size from the biggest to the smallest one. This helps to save memory (alignment). Example:
&37. Each file must contain oc_
prefix in the name. (standard libraries are exceptions from this rule)
&38. File name must be lowercase with _
instead of spaces. Example: oc_gpio.h
&39. File name must contain also module name, example: oc_gpio_defs.h
&40. Functions can take maximum 5 arguments. If some function need to more, it should be given using structures.
TODO:
Features: