{{% lowrisc-doc-hdr C and C++ Coding Style Guide }}
C and C++ are widely used languages for (embedded) software.
Our C and C++ style guide follows the Google C++ Style Guide, with some exceptions and clarifications.
As with all style guides the intention is to:
{{% toc 3 }}
Unless otherwise noted, the following terminology conventions apply to this style guide:
We use the Google C++ Style Guide for both C and C++ code. The following exceptions and additions to this style guide apply to both C and C++ code.
When declaring pointer types, the asterisk (*
) should be placed next to the variable name, not the type.
Example:
int *ptr;
Single-statement blocks are not allowed. All conditions and loops must use braces.
Example:
if (foo) { do_something(); }
Comments should be // C99-style
for consistency with C++.
Variables mentioned in comments should be delimited with pipe (|
) characters.
Example:
// |ptr| can never be NULL for reasons.
TODO comments should be in the format TODO: message
.
TODO comments which require more explanation should reference an issue.
It is recommended to use fully-qualified issue numbers or URLs when referencing issues or pull requests.
Example:
// TODO: This algorithm should be rewritten to be more efficient. // (Bug lowrisc/reponame#27)
#include
directives must, with exceptions, be rooted at $REPO_TOP
.
Every #include
directive must be rooted at the repository base, including files in the same directory. This helps the reader quickly find headers in the repository, without having to worry about local include-search rules.
Example: my/device/library.c
would start with a directive like the following:
#include "my/device/library.h"
This rule does not apply to generated headers, since they do not yet have a designated location in the source tree, and are instead included from ad-hoc locations. Until these questions are resolved, these includes must be marked as follows:
#include "my_generated_file.h" // Generated.
This convention helps readers distinguish which files they should not expect to find in-tree.
The above rules also do not apply to system includes, which should be included by the names dictated by the ISO standard, e.g. #include <stddef.h>
.
It is recommended to document public functions, classes, methods, and data structures in the header file with a Doxygen-style comment.
The first line of the comment is the summary, followed by a new line, and an optional longer description. Input arguments and return arguments can be documented with @param
and @return
if they are not self-explanatory from the name.
Example:
/** * Do something amazing * * Create a rainbow and place a unicorn at the bottom of it. @p arg1 pots of * gold will be positioned on the east end of the rainbow. * * @param pots_of_gold Number of gold pots to place next to the rainbow * @param unicorns Number of unicorns to position on the rainbow * @return 0 if the function was successful, -1 otherwise */ int create_rainbow(int pots_of_gold, int unicorns);
The Google C++ Style Guide targets C++, but it can also be used for C code with minor adjustments. Consequently, C++-specific rules don't apply. In addition to the shared C and C++ style guide rules outlined before, the following C-specific rules apply.
Names of functions, enum
s, struct
s, and typedef
s must be lower_snake_case
.
This rule deviates from the Google C++ style guide to align closer with a typical way of writing C code.
Macros are often necessary and reasonable coding practice C (as opposed to C++) projects. In contrast to the recommendation in the Google C++ style guide, exporting macros as part of the public API is allowed in C code. A typical use case is a header with register definitions.
C99 introduces designated initializers: when initializing a type of struct, array, or union type, it is possible to designate an initializer as being for a particular field or array index. For example:
my_struct_t s = { .my_field = 42 }; int arr[5] = { [3] = 0xff, [4] = 0x1b };
With judicious use, designated initializers can make code more readable and robust; struct field reordering will not affect downstream users, and weak typing will not lead to surprising union initialization.
When initializing a struct or union, initializers within must be designated; array-style initialization (or mixing designated and undesignated initializers) is forbidden.
Furthermore, the nested forms of designated initialization are forbidden (e.g., .x.y = foo
and .x[0] = bar
), to discourage initialization of deeply nested structures with flat syntax. This may change if we find cases where this initialization improves readability.
When initializing an array, initializers may be designated when that makes the array more readable (e.g., lookup tables that are mostly zeroed). Mixing designated and undesignated initializers, or using nested initializers, is still forbidden.
The clang-format tool can check for adherence to this style guide. The repository contains a .clang-format
file which configures clang-format according to the rules outlined in this style guide.
You can run clang-format on you changes by calling git clang-format
.
cd $REPO_TOP # make changes to the code ... git add your_modified_file.c # format the staged changes git clang-format
To reformat the whole tree the script util/run-clang-format.sh
can be used.