Development in this repository roughly follows the Google C++ style guide (https://google.github.io/styleguide/cppguide.html) modified to allow for the fact we are working primarily in C.
Code formatting is controlled by the contents of .clang-format
which we apply through the use of version 18 of the clang-format
tool.
- The code is written to the C11 standard
- POSIX Compliance is preferred when using library functions (e.g use
statin preference toaccess) - GCC Must be version 11 or greater
- CMake version 3.31.5 is used, being the version supported by Ubuntu 22.04
- Follow the guidelines under "Self-contained Headers".
- Use
#defineguard definitions in all header files, do not use the trailing underscore. - Follow the "Include what you use" recommendations
- The Forward Declarations section is focused on C++ issues. In C, they are sometimes necessary
- To declare an opaque type
- To allow a
structto contain a pointer-to-self member.
- Inline functions should be used sparingly,
- should not force the disclosure of internal implementation
details (such as the fields of a
structwhich could otherwise be opaque). - Are always preferable to parameterized
#definemacros.
- should not force the disclosure of internal implementation
details (such as the fields of a
- We use a different order for include statements, whether in a header
or an implementation (.c) file.
- System include files first, enclosed in angle-brackets
- Intra-project files next. These would be includes from a different component within the SDK
- Include files located in the same component
Groups of #include statements should be separated by a blank line.
In the sense of the Style Guide, these are a C++ feature and not applicable to C.
However, related functions should be named consistently. A good convention is to use the same initial term for all public identifiers.
Also, a prefix should be used to avoid potential clashing
- All internal functions for a module should be declared
static - Functions that may be called by other modules within the component (e.g. a library), but not from outside, use normal external linkage.
- Functions which are intended to form the public API of a library
must be annotated with the
SO_EXPORTqualifier in both the header file and the implementation file.
Thus, in a module X within a library we might have
// x.h
...
#include "so_export.h"
...
void bar(); // called only by other modules in the library
SO_EXPORT void bang(); // called by library clients
--------------
// x.c
#include "X.H"
...
// Only called from within x.c
static foo() {}
void bar() {} // only called from within the library
SO_EXPORT void bang() {} // Part of the public API for the libraryAs per the style guide
- use the narrowest scope possible
- initialize on declaration
- avoid cryptic names
But be aware, Coverity (if you use it) may force you to move a declaration to the top of a function.
In a multithreaded environment, static storage is a "Bad Idea" and should be avoided.
The exception is for things which are write-once/read-many.
C does not have classes, per se, though they can be emulated with
struct's (indeed, that's how early C++ compilers did it).
This section largely replaces the Classes section of the style guide.
When declaring ta struct, the following style is strongly preferred
typedef {
... // members
} StructTypeName;This allows them to be used naturally within the code body, without
constantly restating the fact that they are structs.
NOTE: It is a common practice to apply the
_tsuffix to type names. We do NOT follow this. The_tsuffix is reserved by POSIX for its own use.
Opaque types are to be preferred in a module's API whenever that API is solely concerned with operating on objects of that type.
An opaque type is one in which the header file contains an incomplete (or forward) declaration, and the full declaration is only available within the body file.
// X.h
typedef struct implementation implementation;
implementation * implementation_new();
void do_something_with(implementation *thing);
void implementation_free(implementation *thing);
////////////////
// X.c
#include "X.h"
// The typedef is in the header file, so we do not repeat it here.
struct implementation {
...
};
...The best example, from the earliest days of C, is the FILE structure
used in all of the file-i/o standard library functions. If you dig into
the header files for your compiler, you will never find a struct
body declaration for FILE. Only those functions know what is inside
a FILE structure, so it can change between operating systems or
even compiler versions without breaking any code.
However, this approach necessitates dynamic memory management by the client -
including the need for new and delete functions in the API.
The alternative is to expose the internal details of the struct within
the header file. This allows clients to declare arrays of these objects
and obviates the need to manage memory manually.
Whichever approach is used, functions must never either accept a structure as an argument, or return one as the function value.
Always pass and return pointers.
The following terms are used to describe different case forms:
- snake-case: All lower case, with words separated by underscores, E.g. snake_case_name
- UPPER-SNAKE-CASE: Like snake case, but all letters are upper case. E.g. UPPER_SNAKE_CASE_NAME
- camel-case: First word is all lower case, remaining words begin with an upper case letter. E.g. camelCaseName
- Pascal-case: Like camel-case except the first letter is also upper case. E.g. PascalCaseName
In general, the following naming conventions must be followed:
- (source) file names: snake-case, no spaces.
typedefs,structs, andenumtypes: Pascal-case#defineconstants andenumtype members: UPPER-SNAKE-CASE- functions: snake-case
- variables: snake-case