You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
430 lines
13 KiB
430 lines
13 KiB
# Fluoride Style Guide
|
|
This document outlines the coding conventions and code style used in Fluoride.
|
|
Its primary purpose is to provide explicit guidance on style so that developers
|
|
are consistent with one another and spend less time debating style.
|
|
|
|
As a rule, we follow the Google C++
|
|
[Style Guide](https://google.github.io/styleguide/cppguide.html).
|
|
Exceptions will be noted below.
|
|
|
|
## Directory structure
|
|
Directories at the top-level should consist of major subsystems in Fluoride.
|
|
Each subsystem's purpose should be documented in the `doc/directory_layout.md`
|
|
file, even if it seems obvious from the name.
|
|
|
|
For a subsystem that contains code, its directory structure should look like:
|
|
```
|
|
Android.mk
|
|
include/
|
|
src/
|
|
test/
|
|
```
|
|
Further, the directory structure inside `src/` and `include/` should be
|
|
mirrored. In other words, if `src/` contains a subdirectory called `foo/`,
|
|
`include/` must also have a subdirectory named `foo/`.
|
|
|
|
## Target architecture
|
|
Fluoride targets a variety of hardware and cannot make many assumptions about
|
|
memory layout, sizes, byte order, etc. As a result, some operations are
|
|
considered unsafe and this section outlines the most important ones to watch out
|
|
for.
|
|
|
|
### Pointer / integer casts
|
|
In general, do not cast pointers to integers or vice versa.
|
|
|
|
The one exception is if an integer needs to be temporarily converted to a
|
|
pointer and then back to the original integer. This style of code is typically
|
|
needed when providing an integral value as the context to a callback, as in the
|
|
following example.
|
|
```
|
|
void my_callback(void *context) {
|
|
uintptr_t arg = context;
|
|
}
|
|
|
|
set_up_callback(my_callback, (uintptr_t)5);
|
|
```
|
|
Note, however, that the integral value was written into the pointer and read
|
|
from the pointer as a `uintptr_t` to avoid a loss of precision (or to make the
|
|
loss explicit).
|
|
|
|
### Byte order
|
|
It is not safe to assume any particular byte order. When serializing or
|
|
deserializing data, it is unsafe to memcpy unless both source and destination
|
|
pointers have the same type.
|
|
|
|
## Language
|
|
Fluoride is written in C99 and should take advantage of the features offered by
|
|
it. However, not all language features lend themselves well to the type of
|
|
development required by Fluoride. This section provides guidance on some of the
|
|
features to embrace or avoid.
|
|
|
|
### C Preprocessor
|
|
The use of the C preprocessor should be minimized. In particular:
|
|
* use functions or, if absolutely necessary, inline functions instead of macros
|
|
* use `static const` variables instead of `#define`
|
|
* use `enum` for enumerations, not a collection of `#define`s
|
|
* minimize the use of feature / conditional macros
|
|
|
|
The last point is perhaps the most contentious. It's well-understood that
|
|
feature macros are useful in reducing code size but it leads to an exponential
|
|
explosion in build configurations. Setting up, testing, and verifying each of
|
|
the `2^n` build configurations is untenable for `n` greater than, say, 4.
|
|
|
|
### C++
|
|
Although C++ offers constructs that may make Fluoride development faster,
|
|
safer, more pleasant, etc. the decision _for the time being_ is to stick with
|
|
pure C99. The exceptions are when linking against libraries that are written
|
|
in C++. At the time of writing these libraries are `gtest` and `tinyxml2`,
|
|
where the latter is a dependency that should be eliminated in favor of simpler,
|
|
non-XML formats.
|
|
|
|
### Variadic functions
|
|
Variadic functions are dangerous and should be avoided for most code. The
|
|
exception is when implementing logging since the benefits of readability
|
|
outweigh the cost of safety.
|
|
|
|
### Functions with zero arguments
|
|
Functions that do not take any arguments (0 arity) should be declared like so:
|
|
```
|
|
void function(void);
|
|
```
|
|
Note that the function explicitly includes `void` in its parameter list to
|
|
indicate to the compiler that it takes no arguments.
|
|
|
|
### Variable declarations
|
|
Variables should be declared one per line as close to initialization as possible.
|
|
In nearly all cases, variables should be declared and initialized on the same line.
|
|
Variable declarations should not include extra whitespace to line up fields. For
|
|
example, the following style is preferred:
|
|
```
|
|
int my_long_variable_name = 0;
|
|
int x = 5;
|
|
```
|
|
whereas this code is not acceptable:
|
|
```
|
|
int my_long_variable_name = 0;
|
|
int x = 5;
|
|
```
|
|
|
|
As a result of the above rule to declare and initialize variables together,
|
|
`for` loops should declare and initialize their iterator variable in the
|
|
initializer statement:
|
|
```
|
|
for (int i = 0; i < 10; ++i) {
|
|
// use i
|
|
}
|
|
```
|
|
|
|
### Contiguous memory structs
|
|
Use C99 flexible arrays as the last member of a struct if the array needs
|
|
to be allocated in contiguous memory with its containing struct.
|
|
A flexible array member is writen as `array_name[]` without a specified size.
|
|
For example:
|
|
```
|
|
typedef struct {
|
|
size_t length;
|
|
uint8_t data[];
|
|
} buffer_t;
|
|
|
|
// Allocate a buffer with 128 bytes available for my_buffer->data.
|
|
buffer_t *my_buffer = malloc(sizeof(buffer_t) + 128);
|
|
uint8_t *data = my_buffer->data;
|
|
```
|
|
|
|
### Pointer arithmetic
|
|
Avoid pointer arithmetic when possible as it results in difficult to read code.
|
|
Prefer array-indexing syntax over pointer arithmetic.
|
|
|
|
In particular, do not write code like this:
|
|
```
|
|
typedef struct {
|
|
size_t length;
|
|
} buffer_t;
|
|
|
|
buffer_t *my_buffer = malloc(sizeof(buffer_t) + 128);
|
|
uint8_t *data = (uint8_t *)(my_buffer + 1);
|
|
```
|
|
Instead, use zero-length arrays as described above to avoid pointer arithmetic
|
|
and array indexing entirely.
|
|
|
|
### Boolean type
|
|
Use the C99 `bool` type with values `true` and `false` defined in `stdbool.h`.
|
|
Not only is this a standardized type, it is also safer and provides more
|
|
compile-time checks.
|
|
|
|
### Booleans instead of bitfields
|
|
Use booleans to represent boolean state, instead of a set of masks into an
|
|
integer. It's more transparent and readable, and less error prone.
|
|
|
|
### Function names as strings
|
|
C99 defines `__func__` as an identifier that represents the function's name
|
|
in which it is used. The magic identifier `__FUNCTION__` should not be used
|
|
as it is a non-standard language extension and an equivalent standardized
|
|
mechanism exists. In other words, use `__func__` over `__FUNCTION__`.
|
|
|
|
## Fluoride conventions
|
|
This section describes coding conventions that are specific to Fluoride.
|
|
Whereas the _Language_ section describes the use of language features, this
|
|
section describes idioms, best practices, and conventions that are independent
|
|
of language features.
|
|
|
|
### Memory management
|
|
Use `osi_malloc` or `osi_calloc` to allocate bytes instead of plain `malloc`.
|
|
Likewise, use `osi_free` over `free`. These wrapped functions provide additional
|
|
lightweight memory bounds checks that can help track down memory errors.
|
|
|
|
By convention, functions that contain `*_new` in their name are allocation
|
|
routines and objects returned from those functions must be freed with the
|
|
corresponding `*_free` function. For example, list objects returned from
|
|
`list_new` should be freed with `list_free` and no other freeing routine.
|
|
|
|
### Asserts
|
|
Use `CHECK` liberally throughout the code to enforce invariants. Assertions
|
|
should not have any side-effects and should be used to detect programming logic
|
|
errors. Please do not use `assert`.
|
|
|
|
At minimum, every function should assert expectations on its arguments. The
|
|
following example demonstrates the kinds of assertions one should make on
|
|
function arguments.
|
|
```
|
|
size_t open_and_read_file(const char *filename, void *target_buffer, size_t max_bytes) {
|
|
CHECK(filename != NULL);
|
|
CHECK(filename[0] != '\0');
|
|
CHECK(target_buffer != NULL);
|
|
CHECK(max_bytes > 0);
|
|
|
|
// function implementation begins here
|
|
}
|
|
```
|
|
|
|
## Header files
|
|
In general, every source file (`.c` or `.cpp`) in a `src/` directory should
|
|
have a corresponding header (`.h`) in the `include/` directory.
|
|
|
|
### Template header file
|
|
```
|
|
[copyright header]
|
|
|
|
#pragma once
|
|
|
|
#include <system/a.h>
|
|
#include <system/b.h>
|
|
|
|
#include "subsystem/include/a.h"
|
|
#include "subsystem/include/b.h"
|
|
|
|
typedef struct alarm_t alarm_t;
|
|
typedef struct list_t list_t;
|
|
|
|
// This comment describes the following function. It is not a structured
|
|
// comment, it's English prose. Function arguments can be referred to as
|
|
// |param|. This function returns true if a new object was created, false
|
|
// otherwise.
|
|
bool template_new(const list_t *param);
|
|
|
|
// Each public function must have a comment describing its semantics. In
|
|
// particular, edge cases, and whether a pointer argument may or may not be
|
|
// NULL.
|
|
void template_use_alarm(alarm_t *alarm);
|
|
```
|
|
|
|
### License header
|
|
Each header file must begin with the following Apache 2.0 License with `<year>`
|
|
and `<owner>` replaced with the year in which the file was authored and the
|
|
owner of the copyright, respectively.
|
|
```
|
|
/******************************************************************************
|
|
*
|
|
* Copyright <year> <owner>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
******************************************************************************/
|
|
```
|
|
|
|
### Include guard
|
|
After the license header, each header file must contain the include guard:
|
|
```
|
|
#pragma once
|
|
```
|
|
This form is used over traditional `#define`-based include guards as it is less
|
|
error-prone, doesn't pollute the global namespace, is more compact, and can
|
|
result in faster compilation.
|
|
|
|
## Formatting
|
|
Code formatting is done automatically using clang-format.
|
|
|
|
The style file is located at the root of the source tree in .clang-format. The
|
|
-style=file option instructs clang-format to look for this file. You may find
|
|
clang-format --help useful for more advanced usage. The [Online clang-format
|
|
Documentation](http://clang.llvm.org/docs/ClangFormat.html) can also be helpful.
|
|
|
|
`clang-format -style=file -i path_to_files/filename_or_*`
|
|
|
|
### My Patch Doesn't Apply Anymore!
|
|
Choose one of the options below. The fewer patches that have been applied to
|
|
the tree since the formatting change was applied, the better. In this short
|
|
guide, commands you type will be marked as `code`, with output in *italics*.
|
|
|
|
#### Option 1: The Best Case
|
|
Use this option if your patch touches only a few files with few intermediate
|
|
patches.
|
|
|
|
##### Find the formatting patch
|
|
|
|
`git log --oneline path_to_files/filename_or_* | grep clang-format | head -n 5`
|
|
|
|
**15ce1bd** subtree: Apply **clang-format** for the first time*
|
|
|
|
##### Revert the formatting patch
|
|
|
|
`git revert HASH -n`
|
|
|
|
(Replace HASH with 15ce1bd in this example.)
|
|
|
|
##### Check for conflicts with your patch
|
|
|
|
`git status | grep both.modified`
|
|
|
|
If this list contains files modified by your patch, you should give up
|
|
|
|
`git revert --abort`
|
|
|
|
and try a different method.
|
|
|
|
If this list contains files not modified by your patch, you should unstage them
|
|
|
|
`git reset HEAD both_modified_file`
|
|
|
|
and remove their changes
|
|
|
|
`git checkout both_modified_file`
|
|
|
|
##### Apply your patch
|
|
|
|
`git cherry-pick your_patch_that_used_to_apply_cleanly`
|
|
|
|
##### Reformat the code
|
|
|
|
`clang-format -style=file -i path_to_files/filename_or_*`
|
|
|
|
##### Commit the code that your patch touched
|
|
|
|
`git add path_to_files/filename_or_*`
|
|
|
|
`git commit --amend`
|
|
|
|
##### Clean up any other files
|
|
|
|
`git checkout .`
|
|
|
|
##### Review your new patch
|
|
|
|
`git diff`
|
|
|
|
#### Option 2: Reformat your patch
|
|
|
|
##### Start with a tree with your patch applied to the tip of tree
|
|
|
|
`git log --oneline | head -n 1`
|
|
|
|
**dc5f0e2** Unformatted but vital patch*
|
|
|
|
(**Remember the HASH from this step**)
|
|
|
|
##### Create a new branch starting from the first unrelated patch
|
|
|
|
`git checkout HEAD^ -b reformat_my_patch_branch`
|
|
|
|
`git log --oneline | head -n 1`
|
|
|
|
**15ce1bd** First Unrelated patch*
|
|
|
|
##### Reformat the code
|
|
|
|
`clang-format -style=file -i path_to_files/filename_or_*`
|
|
|
|
##### Commit your temporary formatting patch
|
|
|
|
`git add path_to_files/filename_or_*`
|
|
|
|
`git commit -m tmp_format_patch`
|
|
|
|
##### Revert your temporary formatting patch (**Bear with me!**)
|
|
|
|
`git revert HEAD --no-edit`
|
|
|
|
##### Apply your patch
|
|
|
|
`git cherry-pick HASH`
|
|
|
|
(The HASH of your patch, dc5f0e2 in this case)
|
|
|
|
##### Reformat the code
|
|
|
|
`clang-format -style=file -i path_to_files/filename_or_*`
|
|
|
|
##### Commit your second temporary formatting patch
|
|
|
|
`git add path_to_files/filename_or_*`
|
|
|
|
`git commit -m tmp_format_patch_2`
|
|
|
|
##### Check to see that everything looks right
|
|
|
|
`git log --oneline | head -n 5`
|
|
|
|
*04c97cf tmp_format_patch_2*
|
|
|
|
*cf8560c Unformatted but vital patch*
|
|
|
|
*04c97cf Revert "tmp_format_patch"*
|
|
|
|
*d66bb6f tmp_format_patch*
|
|
|
|
*15ce1bd First Unrelated patch*
|
|
|
|
##### Squash the last three patches with git rebase
|
|
|
|
`git rebase -i HEAD^^^`
|
|
|
|
*pick 04c97cf tmp_format_patch_2*
|
|
|
|
*squash cf8560c Unformatted but vital patch*
|
|
|
|
*squash 04c97cf Revert "tmp_format_patch"*
|
|
|
|
##### Remember to edit the commit message!
|
|
|
|
`clang-format -style=file -i path_to_files/filename_or_*`
|
|
|
|
##### Check to see that everything looks right
|
|
|
|
`git log --oneline | head -n 2`
|
|
|
|
*523078f Reformatted vital patch*
|
|
|
|
*d66bb6f tmp_format_patch*
|
|
|
|
##### Review your new patch
|
|
|
|
`git show`
|
|
|
|
##### Checkout the current tree and cherry pick your reformatted patch!
|
|
|
|
`git checkout aosp/master`
|
|
|
|
`git cherry-pick HASH`
|
|
|
|
(HASH is 523078f in this example)
|