Starboard C and C++ Style Guide

A description of the coding conventions for Starboard code and API headers.

Status: REVIEWED
Created: 2016-11-08

Starboard generally tries to follow the coding conventions of Cobalt, which itself mostly follows the conventions of Chromium, which mostly follows the externally-published Google C++ coding conventions. But, Starboard has some special requirements due to its unusual constraints, so it must add a few new conventions and loosen some of the existing style prescriptions.

Background

Before looking at this document, bear in mind that it is not intending to completely describe all conventions that apply to Starboard. You probably want to take some time to familiarize yourself with these documents first, probably in this order:

The main additional constraints that Starboard has to deal with are:

  • The Starboard API is defined in straight-C. It must be able to interface with all possible third-party components, many of which are in C and not C++.
  • Starboard is a public API. Starboard platform implementations and applications written on top of Starboard will change independently. This means there are intense requirements for API stability, usage predictability, searchability, and documentation.
  • Note that even though it is presented as a “style guide,” the conventions presented here are required to be approved for check-in unless otherwise noted.

Definitions

snake-case

Words separated with underscores.

lower_snake_case
ALL_CAPS_SNAKE_CASE

camel-case

Words separated by letter capitalization.

camelCase
CapitalizedCamelCase

C++ Guidelines

What follows are hereby the guidelines for Starboard C and C++ code. Heretofore the guidelines follow thusly as follows.

API Definitions

  • Starboard API definitions must always be compatible with straight-C99 compilers.
  • All public API declarations must be specified in headers in src/starboard/*.h, not in any subdirectories.
  • Non-public declarations must NOT be specified in headers in src/starboard/*.h.
  • C++ inline helper definitions may be included inside an #if defined(__cplusplus) preprocessor block. They must only provide convenience, and must NOT be required for any API functionality.
  • All public API functions should be exported symbols with the SB_EXPORT attribute.
  • No non-const variables shall be exposed as part of the public API.
  • All APIs should be implemented in C++ source files, not straight-C source files.

Modules

  • Each module header must be contained with a single header file.

  • The name of the module must be the singular form of the noun being interfaced with by the module, without any “sb” or “starboard”.

    • file.h
    • directory.h
    • window.h
  • Module interfaces should not have circular dependencies.

File Names

  • Like in the other conventions (e.g. Google, Chromium), file names must be in lower_snake_case.
  • File names must not contain sb_ or starboard_.
  • The name of a module header file must be the lower_snake_case form of the module name.
    • SbConditionVariablestarboard/condition_variable.h
  • A header that is intended to be an internal implementation detail of one or more platform implementations should have the suffix _internal.h, and include the header starboard/shared/internal_only.h.
  • See “Implementations” for conventions about where to place implementation files.

Types

  • Like in the other conventions, types should be CapitalizedCamelCase.
  • Every public Starboard type must start with Sb. There are no namespaces in C, so Sb is the Starboard namespace.
  • Every public Starboard type must be declared by a module, and must have the name of the module following the Sb.
    • file.h contains SbFile, SbFileInfo, SbFileWhence, etc...
  • Every seemingly-allocatable, platform-specific Starboard type should be defined as an opaque handle to a publicly undefined struct with the Private suffix. Follow this pattern for all such type declarations.
    • struct SbFilePrivate is declared, but not defined in the public header.
    • SbFilePrivate is typedef'd to struct SbFilePrivate. This is a C thing where types are defined as names with the “struct” keyword prepended unless typedef'd.
    • SbFile is defined as a typedef of struct SbFilePrivate*.
  • C structs may be defined internally to have functions and visibility. It is allowed for such structs to have constructors, destructors, methods, members, and public members.
  • It is also considered idiomatic to never define the private struct but to just treat it like a handle into some other method of object tracking, casting the handle back and forth to the pointer type.
  • If a word in the name of a type is redundant with the module name, it is omitted. * A monotonic time type in the Time module is SbTimeMonotonic, not SbMonotonicTime, SbTimeMonotonicTime, or SbTimeMonotonicSbTime.

Functions

  • Like in the other conventions, functions should be CapitalizedCamelCase.
  • Every public Starboard function must start with Sb. There are no namespaces in C, so Sb is the Starboard namespace.
  • Every public Starboard function must be declared by a module, and must have the name of the module following the Sb.
    • system.h contains SbSystemGetPath()
    • file.h contains SbFileOpen()
  • After the Starboard and Module prefix, functions should start with an imperative verb indicating what the function does.
    • The Thread module defines SbThreadCreateLocalKey() to create a key for thread local storage.
  • If a word in the name of a function is redundant with the module name, it is omitted.
    • The File module as the function SbFileOpen, not SbOpenFile, SbFileOpenFile or SbFileOpenSbFile.
    • If this gets awkward, it may indicate a need to split into a different module.

Variables, Parameters, Fields

  • Like in the other conventions, variable, function parameter, and field names must be in lower_snake_case.
  • Private member fields end in an underscore.
  • Public member fields do not end in an underscore.

Namespaces

Most Starboard API headers are straight-C compatible, so cannot live inside a namespace. Implementations, since they implement straight-C interface functions, also cannot live inside a namespace.

But, in all other cases, Starboard C++ code should follow the inherited conventions and use a namespace for each directory starting with a “starboard” namespace at the starboard repository root.

Preprocessor Macros

  • Like in the other conventions, variable, function parameter, and field names must be in ALL_CAPS_SNAKE_CASE.
  • Macros may be used as compile-time constants because straight-C does not have a proper facility for typed constants. This is as opposed to macros used primarily at preprocessor-time to filter or modify what gets sent to the compiler. Macros used as compile-time constants and that are not configuration parameters should be explicitly-typed with a c-style cast, and should follow the Constants naming conventions.
  • Macros must start with SB_, and then must further be namespaced with the module name, with the exception of configuration definitions.
  • Configuration definitions should be namespaced with the module name that they primarily affect, if applicable, or a scope that generally indicates its domain.
    • SB_FILE_MAX_NAME
    • SB_MEMORY_PAGE_SIZE
  • Always use #if defined(MACRO) over #ifdef MACRO.

Constants

  • Constants (including enum entries) are named using the Google constant naming convention, CapitalizedCamelCased, but starting with a lower-case k.
  • After the k, all constants have Sb, the Starboard namespace.
    • kSb
  • After kSb, all constants then have the module name.
    • kSbTime
    • kSbFile
  • After kSb<module> comes the rest of the name of the constant.
    • kSbTimeMillisecond
    • kSbFileInvalid
  • Enum entries are prefixed with the full name of the enum.
    • The enum SbSystemDeviceType contains entries like kSbSystemDeviceTypeBlueRayDiskPlayer.

Comments

  • All files must have a license and copyright comment.
  • It is expected that the straight-C compiler supports C99 single-line comments. Block comments should be avoided whenever possible, even in license and copyright headers.
  • Each public API module file should have a Module Overview documentation comment below the license explaining what the module is for, and how to use it effectively.
  • The Module Overview must be separated by a completely blank line from the license comment.
  • The first line of the Module Overview documentation comment must say “Module Overview: Starboard <module-name> module”, followed by a blank comment line (i.e. a line that contains a //, but nothing else).
  • The first sentence of a documentation comment describing any entity (module, type, function, variable, etc...) should assume “This module,” “This type,” “This function,” or “This variable” at the beginning of the sentence, and not include it.
  • The first sentence of a documentation comment describing any entity should be a single-sentence summary description of the entire entity.
  • The first paragraph of a documentation comment should describe the overall behavior of the entity.
  • Paragraphs in comments should be separated by a blank comment line.
  • All public entities must have documentation comments, including enum entries.
  • Documentation comments should be formatted with Markdown.
  • Variables, constants, literals, and expressions should be referenced in comments with pipes around them.
  • All comments must be full grammatically-correct English sentences with proper punctuation.
  • Comments in Starboard headers must be written as requirements for the porter, for example: “must not return NULL” or “should not return NULL” rather than “will not return NULL”. The choice of “must” vs “should” must follow the guidelines of IETF RFC, https://www.ietf.org/rfc/rfc2119.txt .

Implementations

  • Each API implementation should attempt to minimize other platform assumptions, and should therefore use Starboard APIs to accomplish platform-specific work unless directly related to the platform functionality being implemented. * For example, SbFile can use POSIX file I/O, because that what it is abstracting, but it should use SbMemoryAllocate for any memory allocations, because it might be used with a variety of SbMemory implementations.
  • Whenever possible, each shared function implementation should be implemented in an individual file so as to maximize the chances of reuse between implementations.
  • This does not apply to platform-specific functions that have no chance of being reusable on other platforms.
  • Implementation files that can conceivably be shared between one or more implementations should be placed in a starboard/shared/<dependency>/ directory, where <dependency> is the primary platform dependency of that implementation. (e.g. libevent, posix, c++11, etc.)
  • Implementation files that don't have a specific platform dependency, but whether to use them should be a platform decision should be placed in starboard/shared/starboard/, and must only have dependencies on other Starboard APIs.
  • Implementation files that definitely can be common to ALL implementations should be placed in starboard/common/.

Language Features

  • In public headers, particularly in inline functions and macros, only C-Style casts may be used, though they are forbidden everywhere else.
  • It is expected that the C compiler supports inline functions. They must be declared static, and they must use the SB_C_INLINE or SB_C_FORCE_INLINE attribute. In straight-C code, there is no anonymous namespace, so static is allowed and required for inline functions.
  • No straight-C ISO or POSIX headers should be assumed to exist. Basic C++03 headers may be assumed to exist in C++ code. The ISO C standards have grown up over a long period of time and have historically been implemented with quirks, missing pieces, and so on. Support for the core C++ standard library is much more consistent on those platforms that do support it.
  • It is idiomatic to include thin C++ inline wrappers inside public API headers, gated by an #if defined(cplusplus__) check.