The style rules make ownership, failure, and platform behavior visible in ordinary C++ code. Consistency matters, but the deeper goal is code that a reviewer or coding agent can change without guessing at hidden contracts.
Library code does not use STL containers, exceptions, RTTI, or hidden allocation. Public headers do not include system headers. APIs return errors and accept caller-owned storage. These constraints are architectural; formatting should make them easier to see rather than disguise them.
Before editing a subsystem, read its neighboring headers, tests, and any local AGENTS.md. Prefer an existing type or pattern over introducing a second vocabulary for the same operation.
The repository uses the pinned clang-format version installed by SC::Package:
CI checks the same format. Use // clang-format off only around a small structure whose manual alignment communicates a real pattern, such as a dense reflection table. Turn formatting back on immediately afterward.
Formatting can create excessive horizontal padding when unrelated declarations are aligned together. Split fields or methods into short semantic groups before formatting rather than accepting a wide block of whitespace.
CamelCase for structs, namespaces, template parameters, and source file names.camelCase for functions, methods, variables, and fields.SCREAMING_CASE for preprocessor macros.SC or, for a cohesive subsystem, a nested namespace below SC.using namespace inside function scope.The SC namespace is intentionally flat in many libraries. Search before adding a public name so two independent libraries do not collide when amalgamated into one translation unit.
Operations that can fail return Result or another checked value. Propagate failures with the SC_TRY family rather than ignoring them:
Value-returning functions should normally be [[nodiscard]]. If a deliberately best-effort operation must ignore a result, keep the warning suppression narrow and explain why the result cannot affect correctness.
Do not use exceptions for control flow. A cleanup path must be visible on every return path, usually through MakeDeferred or an explicit close operation.
When a function already needs to return status, write its value through an output parameter:
Views such as StringView and Span borrow memory. Owning containers and storage objects must outlive every borrowed view. APIs that may grow should accept an IGrowableBuffer or another explicit storage interface instead of allocating internally.
Pointers mean “optional” unless a more specific contract says otherwise. References mean “required”. Avoid raw owning pointers.
Public headers:
Put operating-system includes and implementation details in .cpp, .inl, or Internal files. Isolate platform branches in focused functions rather than scattering #if blocks through an algorithm.
Use struct for consistency with the existing codebase. Order a struct so a reader sees its public contract before implementation details: types and constants, lifecycle, operations, then storage.
Functions should do one coherent job and return enough information for the caller to decide what happens next. Avoid boolean parameters that silently switch between unrelated modes; use an options struct or distinct operation when the behavior deserves a name.
Comments should explain a constraint, invariant, or non-obvious platform choice. Do not narrate syntax. Public API documentation should state ownership, failure behavior, and important lifetime rules.
Add or update the test in Tests/Libraries/<Library>. Name sections after behavior rather than implementation steps. Cover the successful path, caller-storage exhaustion, invalid input, and relevant platform differences.
Tests often become the most precise usage examples in the repository. Keep setup explicit enough that a reader can see which object owns each buffer, task, handle, and callback.
Before committing, ask:
The contribution workflow and required validation are described in CONTRIBUTING.md and Building (Contributor).