From 15b5280d7e68795f3110c4fe5c6033637e0167c2 Mon Sep 17 00:00:00 2001 From: Danny Holman Date: Wed, 13 Sep 2023 11:28:12 -0500 Subject: doc: add a coding style document Add a document that lists the prescribed coding style to the doc directory. Signed-off-by: Danny Holman --- doc/coding-style.md | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 doc/coding-style.md diff --git a/doc/coding-style.md b/doc/coding-style.md new file mode 100644 index 0000000..df29fb9 --- /dev/null +++ b/doc/coding-style.md @@ -0,0 +1,282 @@ +# Coding style + +This is a short document that describes my preferred coding style on many of +my personal C projects. Much like Linus Torvalds, I won't force my views on +anyone but please take note of some of the points made here. + +## Indentation + +Don't use tabs, use 8 spaces. Modern editors such as vim or emacs can handle +this quite easily. Why so large? Because larger indentations make the code much +easier to look at and parse with human eyes; it makes mistakes much more +glaringly obvious. Large indents have the added benefit of warning when +functions are nested too deep. + +Case labels should be indented past their parent switch statement, e.g.: + + switch (suffix) { + case 'G': + foo(); + break; + case 'g': + bar(); + break; + default: + break; + } + +Don't put multiple statements on a single line (it makes it look like you're +trying to hide something!) + + if (foo) conditional_func(); + always_done(); + +Don't leave whitespace at the end of lines. + + +## Breaking long lines + +The column limit is (in general) 80 characters. Statements longer than 80 +characters should be broken into more readable chunks. The exception to this is +strings; don't break these as it hinders the ability to grep for them. + +## Braces and spaces + +Braces should be done as it was by K&R C, e.g.: + + if (foo) { + bar(); + } + +This also applies to switch statements, while and for loops, and, yes, even +functions. + +The closing brace, if followed by a continuation of the same statement, should +be placed on a line of its own, e.g.: + + if (foo) { + .. + } else { + .. + } + +Furthermore, braces should not be used when there is only a single statement. + + if (foo) + bar(); + +The exception to this rule is if only one branch is itself a single statemnt. + + if (fizz) { + statement0(); + statement1(); + } else if (buzz) { + single_statement(); + } + +Use a space after these keywords:: + + if switch case for do while + +But not with functions or function-like statements: + + x = sizeof(struct thing); + +Do not add spaces inside parenthesized expressions: + + // BAD + s = sizeof( struct bar ); + +When declaring a pointer, the asterisk should be adjacent to the variable not +the type. + + char *buffer; + +If the pointer is in a function return, the opposite is true: + + struct bar* init_bar(void) { + .. + } + +Use one space around most binary and ternary operators, such as:: + + = + - < > * / % | & ^ <= >= == != ? : + +But no space after unary operators:: + + & * + - ~ ! sizeof + +No space before or after ``++`` or ``--``, and no space around ``.`` or ``->``. + +Do not leave trailing whitespace at the ends of lines or files. + +## Naming + +Since C is a terse language, name conventions should be as well. Variables +should not have verbose names, i.e., instead of ``TemporaryPointerToInt``, ``tmp`` +is just less descriptive and carries the same intent. The exception to this is +obviously globals: global variables and functions need to be descriptive. + +Encoding the type name into the name of the variable or function is frowned +upon, as this just confuses the programmer. + +## Typedefs + +Using constructs like 'typename_t' is heavily frowned upon, because this removes +the backing type information. Data structures, enums and pointers should not be +hidden behind a typedef. + +Most of the time, use of a typedef should be avoided unless you're using +totally opaque objects, or if you're using clear integer types such as:: + + typedef long int u32; + +Most importantly, objects should not be behind more than a single typedef. For +instance, things like this:: + + typedef long int u32; + ... + some_other_code(); + ... + typedef u32 uint32_t; + +should be avoided. + +## Data structures + +Data structures that are *readable* by more than one thread must have a +reference count. If it is *writable* by more than one thread, it must have a +lock. These are not mutually exclusive and are usually employed together. + +## Functions returns and names + +Functions can exit in a multitude of different ways, and one of the most common +is a value indicating success or failure. This can be represented with an +error-code or a boolean value. Mixing these two can lead to problematic bugs. +To prevent bugs like this, follow this convention:: + + If the name of a function is an action or imperative command, + the function should return an error-code integer. If the name + is a predicate, the function should return a "succeeded" boolean. + +For instance, ``add_ints()`` should return 0 or -1, and ``list_empty()`` +should return 0 or 1. + +Functions that create or destroy a data structure should just return a pointer, +or ``NULL`` if error, or void respectively. + +## Using ``goto`` + +Use of ``goto`` is often encouraged in *certain* situations. Most of the time, +if you think you need a ``goto`` statement, go back over your code just to be +sure. However, if you think you need a ``goto`` statement for readability or to +exit a deeply nested loop, use of a ``goto`` is preferred. For instance, a +function might return in several places but some common work might need to be +done. In cases like these, a ``goto`` can be immensely helpful. + +Choose label names that describe why the ``goto`` even exists. + + int foo(int x) { + int ret = 0; + char *buffer = malloc(x); + if (!buffer) + return -1; + + if (condition0) { + while (loop_var) { + ... + } + ret = 1; + goto cleanup; + } + ... + cleanup: + free(buffer); + return ret; + } + +## Commenting + +Comments are good, but there is a danger of over-commenting. Don't try to +explain *how* your code works in a comment. It's much better to write the code +so that the *working* is obvious. + +Generally, you want your comments to tell *what* your code does, not *how*. +Functions should generally not have comments inside the function body. That is +a sign of an over-engineered function that should be broken into helper +functions. You should place comments at the head of the function, telling people +what it does, and possibly *why* it does it. + +When commenting functions, please use the format specified at :ref:`doc_guide`. + +Multiline comments should look like the following: + + /* + * Multiline comment + * Please use this style of commenting consistently. + */ + +## Macros and enums + +All macros should be capitalized. + + #define PI 3.1415 + #define FOO(int var) bar(var, 13) + +Macros with multiple statements should be enclosed in a do - while block: + + #define MACRO(arg0, arg1) \ + do { \ + if (arg0 == arg1) \ + call(arg0, arg1) \ + } while (0) + +This makes the macro uniform when calling it alongside other functions. + +Don't affect control flow in a macro: + + #define FOO(x) \ + do { \ + if (blah(x) < 0) \ + return -1; \ + } while (0) + +Don't have magic values in macros: + + #define FOO(val) bar(index, val) + +Don't have arguments that can be used as l-values. + +Remember operator precedence; always enclose expressions in parentheses: + + #define PI 3.1415 + #define CIR (PI * 10) + +Don't use common local variable names in macros: + + #define FOO(x) \ + ({ \ + ret = sizeof(int); \ + }) + +Enums are preferred when defining several related constants. + +Macros that behave like functions should not be capitalized and should be +treated as if they were functions; however, an inlined function should always +be preferred over this. This side-steps many of the problems addressed here. + +## Printing messages + +User-visible output should have correct spelling and mostly correct grammar. +These messages do not have to end in a period. Make these messages concise and +unambiguous. + + +## Inline assembly + +Inline assembly is a thing of the past. Don't use it. + +## References + +* The Linux Kernel Coding Style - Linus Torvalds. +* The C Programming Language - Brian W. Kernighan, Dennis M. Ritchie -- cgit v1.2.3