diff options
Diffstat (limited to 'doc/CODING-STYLE')
-rw-r--r-- | doc/CODING-STYLE | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/doc/CODING-STYLE b/doc/CODING-STYLE new file mode 100644 index 0000000..4287cd2 --- /dev/null +++ b/doc/CODING-STYLE @@ -0,0 +1,312 @@ + +1. General Style + +Indentation level is 4. The indentation character is space, regardless of +the overall indentation level. IOW, even if nested indentation causes the +overall indentation level to be greater or equal to 8 use spaces only. +Don't leave trailing white space at the end of lines. + +Line length is 80 columns. You need to break up longer lines to multiple +chunks at reasonable boundaries. What counts as a reasonable boundary +depends on what you are breaking up. There is very seldom, if ever, a +good enough reason to break the 80-column line rule. + + +2. Layout and Tabulation + +2.1 Curly Braces + +Opening curly braces are put last on the line, while closing curly braces +are on lines of their own. Generally this applies to all control-flow +blocks, IOW to if, for, while, do, and switch statements. The most notable +exceptions are the while in a do-while statement, if-else-if chains and +functions. + +Some examples: + + if (!condition) { + foo(); + foobar(); + } + else { + bar(); + barfoo(); + } + + while (check) { + do_something(); + and_something_else(); + } + +Exceptions: + + do { + process(); + and_more(); + } while (need_to); + + if (foo < bar) { + foo(); + bar(); + } else if (bar < foo) { + foobar(); + barfoo(); + } else { + frobnicate(); + xyzzy(); + } + +In a function you put both the opening and the closing braces on lines +of their own: + + int foo(int x) + { + if (x < 10) + return foobar(x); + else + return barfoo(x); + } + + +If you have a single statement in a block, you can omit the opening +and closing braces. However in an if-else it is preferred to keep the +braces unless both branches are single statements. Also, do not omit +the braces for do-while statements. + +Examples: + + if (x < 0) + foo(x); + else + bar(x); + +Note however, that we prefer to keep braces here: + + if (y < 10) { + foo(y); + } + else { + foobar(y); + barfoo(x); + } + + do { + x = xyzzy(); + } while (x < 10); + + +2.2 Indentation and Spaces + +Individual cases and the default case is indented to align with the +switch statement itself: + + switch (foo) { + case 1: + so_its_one(); + break; + case 2: + uh_two(); + break; + case 3: + oh_boy_three(); + break; + default: + oh_no(foo); + } + +If you have a more complex set of statements in the cases, it is preferable +to include an extra empty line for clarity before all but the first case and +the default branch. + + switch (x) { + case 1: + if (y < 10) + horribly(x); + else if (y < 20) + complex(x); + else if (x + y > 200) + processing(x, y); + break; + + case 2: + ... + break; + + ... + default: + ... + } + +The general rule for space usage is the following: + + - use one space after control-flow keywords (if, for, while, do, + switch, case) + - use one space around binary and and ternary operators (=, +, -, + *, /, %, |, &, ^, <, >, <=, >=, ==, !=, ? :) + - don't use space after unary operators (&, *, +, -, ~, !) + - don't use space after the following keywords: sizeof, typeof, + alignof, __attribute__ + - don't use spaces before postincrement/postdecrement or pre- + increment/predecrement operators (++, --) + - don't use spaces around structure or union member operators (., ->) + - don't use spaces after the preprocessor directive 'defined' + + +Deviating from these basic preferences is okay if it helps increasing +readibility, for instance by resulting in more compact code and hence +more code per editor surface area. For example, one might choose an +alternative, more compact layout for a switch statement that consists +only of trivially simple case branches, like this: + + switch (f->value.type) { + case SI_TYPE_INT16: f->value.i16 = va_arg(ap, int32_t); break; + case SI_TYPE_UINT16: f->value.u16 = va_arg(ap, uint32_t); break; + case SI_TYPE_INT32: f->value.i32 = va_arg(ap, int32_t); break; + case SI_TYPE_UINT32: f->value.u32 = va_arg(ap, uint32_t); break; + case SI_TYPE_INT64: f->value.i64 = va_arg(ap, int64_t); break; + case SI_TYPE_UINT64: f->value.u64 = va_arg(ap, uint64_t); break; + case SI_TYPE_BOOL: f->value.bln = va_arg(ap, int); break; + case SI_TYPE_DOUBLE: f->value.dbl = va_arg(ap, double); break; + default: + return FALSE; +} + + +2.3 Accepted Tabulation Schemes + +There are two tabulation schemes you can choose from: + + 1) obsessive alignitis: the manic alignment of variable definitions, + structure and union member definitions, variable assignments within + a code block end so forth + + 2) traditional K&R-ish reductionist style, minimizing the amount of + white space while adhering to the rules of the above sections + +But be consistent with yourself, do not try to mix these. Choose one +and stick with it within a component. Also if you are touching any +existing code obey the existing style. + + +2.4 Code Layout + +Put licensing information to the beginning of every file. +Within a module put includes and macro definitions in the beginning +(of course). Then put type definitions, any necessary static function +prototypes and finally module-local variables before the actual +functions of the module. + +Within a function, define variables in the beginning of the function. +You can also put variable definitions at the beginning of code blocks +with curly braces, eg. within if, else, for, while, and switch-constructs. +Please do not pull variables out of your arse^H^H^H^Hstack in the middle +of a function at any other place. + + +2.5 Error Handling + +There are two acceptable error-indication mechanism from functions: +boolean style and libc style. It depends on your component which one makes +sense. If upon failure it is of no use to the caller to get more +information than the mere fact of failure use boolean style error +indication returning true on success and false otherwise. In this case, +include stdbool.h in your public header and use bool as the return type +of your function to indicate the boolean convention to your readers. If +you choose the libc-style, return 0 on success, return -1 on failure and +also set errno to some reasonable error code in this case. Never ever +clear errno on success. Naturally, functions that return objects should +return NULL on failure with both of these styles. + +Whenever you have a non-trivial function with complex control-flow try +to organise your code so that there is a single common error cleanup +and return path. You can put this at the end of your function, label it +as 'fail', and use goto's to jump from the middle of the function to the +error-handling branch. + + +2.6 Function Prototypes + +Use function prototypes with variable names in public header files. + + +3. Naming Conventions + +The basic general naming rule is no CamelCase, use only lower-case +letters, use underscore ('_') as a word separator within symbol names +for functions and variables. Macros are all upper-case with the exception +of function-like macros that take arguments. Just like functions, these +can be all-lower case at will if this makes more sense. Typical cases +when this makes sense would be a macro that wraps a function call and +extends the argument list with additional parameters, or a guarding +convenience wrapper macro with extra error checks for calling a function +in some external component that does not handle some common error cases, +such as being called with a NULL pointer on cleanup code paths. + +For Murphy core, all externally visible symbols must be prefixed with +mrp_ to avoid name-space collisions with other components. + +3.1 Type Names + +Externally visible types (at least structs, unions and enums) must be +typedef'd. The chosen type name must be prefixed consistently with the +other publicly visible types from the same component. All typedef'd +names are suffixed with _t. If you need to also have a non-typedef'd +struct, or union for internal use for some reason, suffix it with _s +or _u depending on its type. + +3.2 Function and Variable Names + +All externally visible functions and variables must be prefixed with +a component-specific prefix-. For Murphy core this is mrp_. For libraries +that can be easily re-used without Murphy, you can chose any prefix you +like, but keep it short, preferably up to 3 or 4 characters max. + +For global variables always use fulle/long descriptive names, keeping in +mind the lower-case only and underscore restrictions. For local variables +and function arguments try to strike the right balance between compactness +and descriptiveness. On one hand too long names hurt readability and waste +a lot of precious real estate on our 80 character character terminals from +the 70's. On the other hand too short or unintuitively shortened names +fail to provide the right association about the usage of the variables for +the reader. So this is not that black and white and it is not easy to give +a set of strict rules that one could blindly follow. If someone familiar +with the subject your code is addressing gets constantly confused with +what your variables are for, you are probably overdoing the compactness +part. If the same person gets constantly frustrated/tired reading your +variable names, you are probably underdoing it... + +Library global function and variable names, ie. symbols whose visibility +is limited to the scope of the library, should be prefixed with the module +prefix within the library they are defined in. + +3.3 File Naming Conventions + +Don't prefix your file names with a component name. Use dashes instead +of underscores in file and directory names. + + +4. Miscallanea + +4.1 Use of Goto + +There are cases when the careful use of gotos make the code both easier +to read/follow and less error-prone to modify/extend than with any of +the alternatives. Typical examples are consolidated bailout/cleanup code +on error paths from complex functions with nested blocks of if/for/while +statements. Also it is often cleaner to use directly gotos instead of +emulating them with an while-(0)-break non-loop construct. + + + + + + + + +Generally speaking, it would be beneficial to come up with a basic set +of rules that can also be expressed as a set of command line options to +indent (or any other available alternative). That would provide a canonical +way for people to check their code for indentation-conformance. Also we +could use it ourselves in git hooks to warn about code with non-conformant +style which otherwise might be difficult to avoid in the beginning. + |