Files
vlibc/STYLEGUIDE.md
T

3.8 KiB

Styleguide

This file is the source of truth for how vlibc code looks. .clang-format is generated from / kept in sync with it.

Language

  • C23 is the default standard; C2y (C29) is opt-in via --enable-c29.
  • GCC only. Compiler extensions (__attribute__, statement expressions, defer under C2y) are permitted and encouraged where they improve clarity or performance.

Formatting

  • 4-space indent, no tabs.
  • 100-column limit.
  • Allman braces: opening brace on its own line, for functions and blocks.
  • Always use braces, even for single-statement blocks (avoids goto fail-style bugs and matches .clang-tidy).
  • Pointer and qualifier attach to the name: const char *s, int *p.
  • Spaces around binary operators; no space after unary operators.

The one exception to Allman braces is the C++ linkage guard: extern "C" { keeps its opening brace attached (encoded as AfterExternBlock: false in .clang-format), because that is the idiomatic form every C header uses.

int
foo(const char *s, size_t n)
{
    if (n == 0)
    {
        return 0;
    }

    return (int)(s[0] == 'x');
}

Naming

  • Functions and variables: snake_case.
  • Types: snake_case (struct tags); typedefs avoid the POSIX-reserved _t suffix.
  • Macros and constants: UPPER_SNAKE_CASE.
  • Public identifiers are prefixed vlibc_ to avoid collisions.
  • Identifiers beginning with _ (or __, or _[A-Z]) are reserved for the implementation and the C/POSIX standards — do not introduce new ones.

Include guards

Public headers use traditional include guards:

#ifndef VLIBC_H
#define VLIBC_H
...
#endif /* VLIBC_H */

#pragma once is a widely supported extension but is deliberately not used: it relies on compiler-specific path canonicalization and can mis-deduplicate headers reachable through symlinks or bind mounts. For a libc whose headers are consumed in many toolchain configurations, explicit guards are the robust default.

Compiler intent

Public declarations annotate intent so the compiler can optimize statically linked binaries (see docs/overview.md):

  • __attribute__((const)) — result depends only on arguments.
  • __attribute__((pure)) — no side effects; may read memory.
  • __attribute__((always_inline)), __attribute__((leaf)), __attribute__((malloc)), __attribute__((access, ...)) — as appropriate.

Every public function carries the tightest correct attribute.

Profile gating

Profile-gated code follows these formatting and naming conventions (the semantic classification of functions into levels lives in docs/compatibility.md, not here):

  • Public headers gate declarations with #if VLIBC_LEVEL >= N. Prefer the VLIBC_LEVEL_GE(n) helper from include/vlibc/features.h; fall back to the raw comparison only when the level is a literal constant:

    #if VLIBC_LEVEL_GE(2)
    size_t strlcpy(char *dst, const char *src, size_t n);
    #endif
    
  • Group implementation sources by level in Makefile.am using the per-level conditionals if PROFILE_GE_2 / if PROFILE_GE_3, mirroring the #if VLIBC_LEVEL >= N rule in headers.

  • Standard headers use VLIBC_-prefixed include guards (VLIBC_STDDEF_H, VLIBC_STRING_H), never the _STDDEF_H-style reserved forms. A leading underscore is reserved for the implementation and the C/POSIX standards (see Naming); the VLIBC_ prefix stays out of the reserved namespace while remaining unambiguous.

  • Intent attributes respect the gating: pure / const are only valid on side-effect-free functions. Never annotate a mutating function (memcpy, memmove, memset, strlcpy, strlcat) with either.

Error handling

  • No empty blocks; no silent failure.
  • Library functions return errors via return codes or errno; they never terminate the caller's process.