blob: 1a0fc2c1f1a208b7ea31fea77d8db3ce2353cfc8 [file] [log] [blame] [view]
# C Library Readability Rubric
This document describes heuristics and rules for writing C libraries
that are published in the Fuchsia SDK.
A different document will be written for C++ libraries. While C++ is
almost an extension of C, and has some influence in this document, the
patterns for writing C++ libraries will be quite different than for C.
Most of this document is concerned with the description of an
interface in a C header. This is not a full C style guide, and has
little to say about the contents of C source files. Nor is this a
documentation rubric (though public interfaces should be well
documented).
Some C libraries have external constraints that contradict these
rules. For instance, the C standard library itself does not follow
these rules. This document should still be followed where applicable.
[TOC]
## Goals
### ABI Stability
Some Fuchsia interfaces with a stable ABI will be published as C
libraries. One goal of this document is to make it easy for Fuchsia
developers to write and to maintain a stable ABI. Accordingly, we
suggest not using certain features of the C language that have
potentially surprising or complicated implications on the ABI of an
interface. We also disallow nonstandard compiler extensions, since we
cannot assume that third parties are using any particular compiler,
with a handful of exceptions for the DDK described below.
### Resource Management
Parts of this document describe best practices for resource management
in C. This includes resources, Zircon handles, and any other type of
resource.
### Standardization
We would also like to adopt reasonably uniform standards for Fuchsia C
libraries. This is especially true of naming schemes. Out parameter
ordering is another example of standardization.
### FFI Friendliness
Some amount of attention is paid to Foreign Function Interface (FFI)
friendliness. Many non-C languages support C interfaces. The
sophistication of these FFI systems varies wildly, from essentially
sed to sophisticated libclang-based tooling. Some amount of
consideration of FFI friendliness went into these decisions.
## Language versions
### C
Fuchsia C libraries are written against the C11 standard (with a small
set of exceptions, such as unix signal support, that are not
particularly relevant to our C library ABI). C99 compliance is not a
goal.
In particular, Fuchsia C code can use the `<threads.h>` and
`<stdatomic.h>` headers from the C11 standard library, as well as the
`_Thread_local` and alignment language features.
The thread locals should use the `thread_local` spelling from
`<threads.h>`, rather than the built in `_Thread_local`. Similarly,
prefer `alignas` and `alignof` from `<stdalign.h>`, rather than
`_Alignas` and `_Alignof`.
Note that compilers support flags that may alter the ABI of the
code. For instance, GCC has a `-m96bit-long-double` flag that alters
the size of a long double. We assume that such flags are not used.
Finally, some libraries (such as Fuchsia's C standard library) in our
IDK are a mix of externally defined interfaces and Fuchsia specific
extensions. In these cases, we allow some pragmatism. For instance,
libc defines functions like `thrd_get_zx_handle` and
`dlopen_vmo`. These names are not strictly in accordance with the
rules below: the name of the library is not a prefix. Doing so would
make the names fit less well next to other functions like
`thrd_current` and `dlopen`, and so we allow the exceptions.
### C++
While C++ is not an exact superset of C, we still design C libraries
to be usable from C++. Fuchsia C headers should be compatible with the
C++11, C++14, and C++17 standards. In particular, function
declarations must be `extern "C"`, as described below.
C and C++ interfaces should not be mixed in one header. Instead,
create a separate `cpp` subdirectory and place C++ interfaces in their
own headers there.
## Library Layout and Naming
A Fuchsia C library has a name. This name determines its include path
(as described in the [library naming document]) as well as identifiers
within the library.
In this document, the library is always named `tag`, and is variously
referred to as `tag` or `TAG` or `Tag` or `kTag` to reflect a
particular lexical convention. The `tag` should be a single identifier
without underscores. The all-lowercase form of a tag is given by the
regular expression `[a-z][a-z0-9]*`. A tag can be replaced by a shorter
version of the library name, for example `zx` instead of `zircon`.
The include path for a header `foo.h`, as described by the [library
naming document], should be `lib/tag/foo.h`.
## Header Layout
A single header in a C library contains a few kinds of things.
- A copyright banner
- A header guard
- A list of file inclusions
- Extern C guards
- Constant declarations
- Extern symbol declarations
- Including extern function declarations
- Static inline functions
- Macro definitions
### Header Guards
Use #ifndef guards in headers. These look like:
```C
#ifndef SOMETHING_MUMBLE_H_
#define SOMETHING_MUMBLE_H_
// code
// code
// code
#endif // SOMETHING_MUMBLE_H_
```
The exact form of the define is as follows:
- Take the canonical include path to the header
- Replace all ., /, and - with _
- Convert all letters to UPPERCASE
- Add a trailing _
For example, the header located in the SDK at `lib/tag/object_bits.h`
should have a header guard `LIB_TAG_OBJECT_BITS_H_`.
### Inclusions
Headers should include what they use. In particular, any public header
in a library should be safe to include first in a source file.
Libraries can depend on the C standard library headers.
Some libraries may also depend on a subset of POSIX headers. Exactly
which are appropriate is pending a forthcoming libc API review.
### Constant Definitions
Most constants in a library will be compile-time constants, created
via a `#define`. There are also read-only variables, declared via
`extern const TYPE NAME;`, as it sometimes is useful to have storage
for a constant (particularly for some forms of FFI). This section
describes how to provide compile time constants in a header.
There are several types of compile time constants.
- Single integer constants
- Enumerated integer constants
- Floating point constants
#### Single integer constants
A single integer constants has some `NAME` in a library `TAG`, and its
definition looks like the following.
```C
#define TAG_NAME EXPR
```
where `EXPR` has one of the following forms (for a `uint32_t`)
- `((uint32_t)23)`
- `((uint32_t)0x23)`
- `((uint32_t)(EXPR | EXPR | ...))`
#### Enumerated integer constants
Given an enumerated set of integer constants named `NAME` in a library
`TAG`, a related set of compile-time constants has the following parts.
First, a typedef to give the type a name, a size, and a
signedness. The typedef should be of an explicitly sized integer
type. For example, if `uint32_t` is used:
```C
typedef uint32_t tag_name_t;
```
Each constant then has the form
```C
#define TAG_NAME_... EXPR
```
where `EXPR` is one of a handful of types of compile-time integer
constants (always wrapped in parentheses):
- `((tag_name_t)23)`
- `((tag_name_t)0x23)`
- `((tag_name_t)(TAG_NAME_FOO | TAG_NAME_BAR | ...))`
Do not include a count of values, which is difficult to maintain as
the set of constants grows.
#### Floating point constants
Floating point constants are similar to single integer constants,
except that a different mechanism is used to describe the type. Float
constants must end in `f` or `F`; double constants have no suffix;
long double constants must end in `l` or `L`. Hexadecimal versions of
floating point constants are allowed.
```C
// A float constant
#define TAG_FREQUENCY_LOW 1.0f
// A double constant
#define TAG_FREQUENCY_MEDIUM 2.0
// A long double constant
#define TAG_FREQUENCY_HIGH 4.0L
```
### Function Declarations
Function declarations should all have names beginning with `tag_...`.
Function declarations should be placed in `extern "C"` guards. These
are canonically provided by using the `__BEGIN_CDECLS` and
`__END_CDECLS` macros from [compiler.h].
#### Function parameters
Function parameters must be named. For example,
```C
// Disallowed: missing parameter name
zx_status_t tag_frob_vmo(zx_handle_t, size_t num_bytes);
// Allowed: all parameters named
zx_status_t tag_frob_vmo(zx_handle_t vmo, size_t num_bytes);
```
It should be clear which parameters are consumed and which are
borrowed. Avoid interfaces in which clients may or may not own a
resource after a function call. If this is infeasible, consider noting
the ownership hazard in the name of the function, or one of its
parameters. For example:
```C
zx_status_t tag_frobinate_subtle(zx_handle_t foo);
zx_status_t tag_frobinate_if_frobable(zx_handle_t foo);
zx_status_t tag_try_frobinate(zx_handle_t foo);
zx_status_t tag_frobinate(zx_handle_t maybe_consumed_foo);
```
By convention, out parameters go last in a function's signature, and
should be named `out_*`.
#### Variadic functions
Variadic functions should be avoided for everything except printf-like
functions. Those functions should document their format string
contract with the `__PRINTFLIKE` attribute from [compiler.h].
#### Static inline functions
Static inline functions are allowed, and are preferable to
function-like macros. Inline-only (that is, not also `static`) C
functions have complicated linkage rules and few use cases.
### Types
Prefer explicitly sized integer types (e.g. `int32_t`) to
non-explicitly sized types (e.g. `int` or `unsigned long int`). An
exemption is made for `int` when referring to POSIX file descriptors,
and for typedefs like `size_t` from the C or POSIX headers.
When possible, pointer types mentioned in interfaces should refer to
specific types. This includes pointers to opaque structs. `void*` is
acceptable for referring to raw memory, and to interfaces that pass
around opaque user cookies or contexts.
#### Opaque/Explicit types
Defining an opaque struct is preferable to using `void*`. Opaque
structs should be declared like:
```C
typedef struct tag_thing tag_thing_t;
```
Exposed structs should be declared like:
```C
typedef struct tag_thing {
} tag_thing_t;
```
#### Reserved fields
Any reserved fields in a struct should be documented as to the purpose
of the reservation.
A future version of this document will give guidance as to how to
describe string parameters in C interfaces.
#### Anonymous types
Top-level anonymous types are not allowed. Anonymous structures and
unions are allowed inside other structures, and inside function
bodies, as they are then not part of the top level namespace. For
instance, the following contains an allowed anonymous union.
```C
typedef struct tag_message {
tag_message_type_t type;
union {
message_foo_t foo;
message_bar_t bar;
};
} tag_message_t;
```
#### Function typedefs
Typedefs for function types are permitted.
Functions should not overload return values with a `zx_status_t` on
failure and a positive success value. Functions should not overload
return values with a `zx_status_t` that contains additional values not
described in [zircon/errors.h].
#### Status return
Prefer `zx_status_t` as a return value to describe errors relating to
Zircon primitives and to I/O.
## Resource Management
Libraries can traffic in several kinds of resources. Memory and Zircon
handles are examples of resources common across many
libraries. Libraries may also define their own resources with
lifetimes to manage.
Ownership of all resources should be unambiguous. Transfer of
resources should be explicit in the name of a function. For example,
`create` and `take` connote a function transferring ownership.
Libraries should be memory tight. Memory allocated by a function like
`tag_thing_create` should released via `tag_thing_destroy` or some
such, not via `free`.
Libraries should not expose global variables. Instead, provide
functions to manipulate that state. Libraries with process-global
state must be dynamically linked, not statically. A common pattern is
to split a library into a stateless static part, containing almost all
of the code, and a small dynamic library holding global state.
In particular, the `errno` interface (which is a global thread-local
global) should be avoided in new code.
## Linkage
The default symbol visibility in a library should be hidden. Use
either an allowlist of exported symbols, or explicit visibility
annotations on symbols to exported.
C libraries must not export C++ symbols.
## Evolution
### Deprecation
Deprecated functions should be marked with the __DEPRECATED attribute
from [compiler.h]. They should also be commented with a description
about what to do instead, and a bug tracking the deprecation.
## Disallowed or Discouraged Language Features
This section describes language features that cannot or should not be
used in the interfaces to Fuchsia's C libraries, and the rationales
behind the decisions to disallow them.
### Enums
C enums are banned. They are brittle from an ABI standpoint.
- The size of integer used to represent a constant of enum type is
compiler (and compiler flag) dependent.
- The signedness of an enum is brittle, as adding a negative value to
an enumeration can change the underlying type.
### Bitfields
C's bitfields are banned. They are brittle from an ABI standpoint, and
have a lot of nonintuitive sharp edges.
Note that this applies to the C language feature, not to an API that
exposes bit flags. The C bitfield feature looks like:
```C
typedef struct tag_some_flags {
// Four bits for the frob state.
uint8_t frob : 4;
// Two bits for the grob state.
uint8_t grob : 2;
} tag_some_flags_t;
```
We instead prefer exposing bit flags as compile-time integer
constants.
### Empty Parameter Lists
C allows for function `with_empty_parameter_lists()`, which are
distinct from `functions_that_take(void)`. The first means "take any
number and type of parameters", while the second means "take zero
parameters". We ban the empty parameter list for being too dangerous.
### Flexible Array Members
This is the C99 feature that allows declaring an incomplete array as
the last member of a struct with more than one parameter. For example:
```C
typedef struct foo_buffer {
size_t length;
void* elements[];
} foo_buffer_t;
```
As an exception, DDK structures are allowed to use this pattern when
referring to an external layout that fits this header-plus-payload
pattern.
The similar GCC extension of declaring a 0-sized array member is
similarly disallowed.
### Module Maps
These are part of a Clang extension to C-like languages that attempt to solve
many of the issues with header-driven compilation. While the Fuchsia
toolchain team is very likely to invest in these in the future, we
currently do not support them.
### Compiler Extensions
These are, by definition, not portable across toolchains.
This in particular includes packed attributes or pragmas, with one
exception for the DDK.
DDK structures often reflect an external layout that does not match
the system ABI. For instance, it may refer to an integer field that is
less aligned than required by the language. This can be expressed via
compiler extensions such as pragma pack.
[compiler.h]: /zircon/system/public/zircon/compiler.h
[library naming document]: /docs/development/languages/c-cpp/naming.md
[zircon/errors.h]: /zircon/system/public/zircon/errors.h