Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Tap House Rules — the Tap family house style. Copy verbatim into every *Tap repo.
# 4-space indent (incl. namespaces), aligned declaration/assignment columns,
# attached braces (else/catch break), comma-first ctor initializers,
# left-bound pointers, 120-column limit. Layout only — naming and mandatory
# braces are enforced separately by .clang-tidy (clang-format cannot check
# identifier names, and its brace insertion is not semantically aware).
Language: Cpp
BasedOnStyle: LLVM
Standard: c++20

ColumnLimit: 120
IndentWidth: 4
AccessModifierOffset: -2
NamespaceIndentation: All

PointerAlignment: Left
DerivePointerAlignment: false
BreakBeforeBinaryOperators: NonAssignment
SpaceBeforeCpp11BracedList: false
AlwaysBreakTemplateDeclarations: Yes

# Braces attach everywhere (including functions); only else/catch break.
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: false
AfterClass: false
AfterStruct: false
AfterNamespace: false
AfterControlStatement: Never
BeforeElse: true
BeforeCatch: true
BreakConstructorInitializers: BeforeComma
PackConstructorInitializers: Never

AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignTrailingComments: true

# Short accessor functions and lambdas may stay inline, but control-flow
# statements never do: every if/for/while is braced AND expanded (see
# .clang-tidy readability-braces-around-statements).
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: Never

BreakStringLiterals: false
KeepEmptyLinesAtTheStartOfBlocks: false
InsertNewlineAtEOF: true

# Include ordering: main header (auto, priority 0) -> C++ standard ->
# third-party -> this project. Regroup enforces it; blank lines between groups.
SortIncludes: CaseSensitive
IncludeBlocks: Regroup
IncludeCategories:
# C++ standard library: <angle> with no '/' and no '.' (e.g. <vector>)
- Regex: '^<[[:alnum:]_]+>$'
Priority: 2
# Other angle-bracket headers (third-party, e.g. <gtest/gtest.h>)
- Regex: '^<.*>$'
Priority: 3
# This project: quoted includes
- Regex: '^".*"$'
Priority: 4

# Min-DevKit declarative DSL (Max/Min externals: TapTools, AmbiTap-Max, ...).
# MIN_FUNCTION / MIN_ARGUMENT_FUNCTION expand to a lambda; teach clang-format
# their shape so attribute/message/argument setter bodies format as lambda
# blocks instead of being shredded. Completely inert for repos that don't use
# these macros (the pure-C++ libraries). Requires clang-format >= 15.
Macros:
- 'MIN_FUNCTION=[](const atoms& args, int inlet) -> atoms'
- 'MIN_ARGUMENT_FUNCTION=[](const atom& arg, int index) -> void'
30 changes: 30 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# OscTap-local Tap House Rules — naming DISABLED (documented exception).
#
# OscTap is a drop-in source-compatible continuation of oscpack, so its public
# API deliberately keeps oscpack's original identifiers: PascalCase types and
# methods (ReceivedMessage, OutboundPacketStream, BeginBundle(), AsFloat()) and
# trailing-underscore data members (size_, value_). Renaming them to the house
# snake_case/m_ scheme would break the compatibility that is the library's
# reason to exist, so the naming half of the house rules does NOT apply here.
# See TapHouse STYLE.md "Repo exception — OscTap (drop-in legacy continuation)".
#
# This file therefore intentionally DIVERGES from the canonical TapHouse
# .clang-tidy (it drops readability-identifier-naming) and OscTap's CI runs a
# format-only style gate (.github/workflows/style.yml) rather than the shared
# drift-check.yml, which requires the three configs to be byte-identical.
#
# The LAYOUT half is still fully adopted via the verbatim .clang-format, and
# mandatory braces around every control-flow body are kept below.
#
# NOTE: WarningsAsErrors is intentionally NOT set here so local runs only warn.
Checks: >
-*,
readability-braces-around-statements

# The library headers live under osctap/; tests under tests/.
HeaderFilterRegex: '.*/(osctap|tests)/.*'

CheckOptions:
# --- Mandatory braces: brace every control-flow body, even one-liners ---
- key: readability-braces-around-statements.ShortStatementLines
value: '0'
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Bulk clang-format reformat under the shared Tap house style.
46047f3e3859e7945e5054879d239cb073d80bda
26 changes: 26 additions & 0 deletions .github/workflows/style.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Tap House Style

# Enforces the Tap House Rules LAYOUT half only (.clang-format).
#
# OscTap is a drop-in source-compatible continuation of oscpack and is exempt
# from the house NAMING rules (readability-identifier-naming) — renaming the
# public API would break compatibility. See TapHouse STYLE.md "Repo exception —
# OscTap". Because a naming-exempt repo cannot keep the three shared configs
# byte-identical, this deliberately runs a format-only gate instead of calling
# the shared tap/taphouse drift-check.yml.
on: [push, pull_request]

jobs:
clang-format:
name: clang-format (layout)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Pinned major version: formatting output can differ across releases.
- name: Install clang-format
run: sudo apt-get update -q && sudo apt-get install -y -q clang-format-18
- name: Check formatting (own sources)
run: |
files=$(find osctap oscpack tests demos examples fuzz android \
-type f \( -name '*.h' -o -name '*.cpp' -o -name '*.cc' -o -name '*.c' \))
clang-format-18 --dry-run --Werror $files
122 changes: 122 additions & 0 deletions STYLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Tap House Rules

> *The Tap house style — always on tap.*

The shared house style for the Tap libraries (AmbiTap, SampleRateTap, OscTap,
and future `*Tap` libraries). Anchored to the C++ standard library's own
conventions (per the ISO C++ Core Guidelines "NL" section), with a small set
of deliberate, documented exceptions.

Two config files enforce this and must be copied verbatim into every repo:

- **`.clang-format`** — layout (whitespace, braces, alignment, includes).
- **`.clang-tidy`** — identifier naming (`readability-identifier-naming`).
clang-format *cannot* check names; this is what does.

CI runs `clang-format --dry-run --Werror` and `clang-tidy` so drift can't
return.

---

## 1. Naming

| Kind | Convention | Example |
|------|-----------|---------|
| Types (class/struct/enum/alias) | `snake_case` | `encoder`, `spsc_ring` |
| Functions / methods | `snake_case` | `push`, `write_available` |
| Variables / parameters / locals | `snake_case` | `frame_count`, `min_capacity` |
| Concepts | `snake_case` (like types) | `sample_type` |
| Template parameters | `PascalCase` — the ONLY leading-capital names | `T`, `S`, `Sample`, `Allocator` |
| Private/protected data members | `m_` + `snake_case` | `m_channels`, `m_order` |
| Public data members (struct fields) | `snake_case`, no prefix | `sample_rate_hz` |
| Constants (namespace/class/static) | `k_` + `snake_case` | `k_smoothing_samples`, `k_cache_line` |
| Enumerators | `snake_case` | `state::filling` |
| Macros | `ALL_CAPS` | `SRT_VERSION_MAJOR`, `TAP_EXPECTS` |

A leading capital letter means **template parameter** and nothing else. This
is the standard library's own allocation (`CharT`, `Rep`, `Period`,
`Allocator`) and is why concepts are lower-case: they read in type position,
so they look like the types they constrain.

**Deliberate deviations from strict std:**
- `k_` prefix on constants (std uses bare `snake_case`) — kept for use-site
clarity. Applies to namespace-, class-, and static-scope constants;
`constexpr` *locals* stay bare.
- `m_` prefix on encapsulated data members (std reserves `_`; user code has no
standard convention here) — kept for self-documentation and greppability.

**Parameters take no prefix** (no `a_`/`an_`). `m_` already prevents any
member/parameter collision, and prefixes would clutter the public signatures
that *are* the library's contract. Lean on `const` and small functions for
input/local clarity.

**Repo exception — OscTap (drop-in legacy continuation).** OscTap continues
[oscpack](http://www.rossbencina.com/code/oscpack) as a *drop-in
source-compatible* successor: its public API keeps oscpack's original
identifiers — PascalCase types and methods (`ReceivedMessage`,
`OutboundPacketStream`, `BeginBundle()`, `AsFloat()`) and trailing-underscore
data members (`size_`, `value_`). Renaming these to the house `snake_case`/`m_`
scheme would break the source compatibility that is the library's reason to
exist. OscTap therefore adopts the **layout** rules (`.clang-format`) in full
but is **exempt from the naming rules** (`readability-identifier-naming`): it
ships a local `.clang-tidy` that disables that check while keeping mandatory
braces, and its CI runs a format-only style gate instead of the shared
`drift-check.yml`. The exemption is specific to legacy-continuation repos;
greenfield `*Tap` code follows the naming rules above.

## 2. Layout

- **Indent:** 4 spaces, including inside namespaces.
- **Braces:** attached everywhere (functions included); only `else` and
`catch` break onto their own line. Every control-flow body is braced *and
expanded* onto its own lines — no single-line `if`/`for`/`while`, even for
guard clauses (braces via clang-tidy `readability-braces-around-statements`;
expansion via `AllowShortBlocksOnASingleLine: Never`). Short accessor
functions and lambdas may still be inline.
- **Brace-init spacing:** no space before a braced-init list — `float x{0.0f}`,
not `x {0.0f}`.
- **Column alignment:** consecutive declarations, assignments, and trailing
comments are aligned.
- **Constructor initializers:** comma-first, one per line, never packed.
- **Pointers/references:** bound to the type — `const float* p`, `T& r`.
- **Member declaration order (per NL.16):** `public` -> `protected` ->
`private`; within a class: types/aliases -> constructors/assignment/
destructor -> functions -> data members last.
- **Column limit:** 120.
- **`const` placement:** west-const (`const T`, not `T const`) — enforced by
review, not tooling.

## 3. Files

- **Extension:** `.h` for headers (family-wide).
- **Header guard:** `#pragma once` (first line after the banner). Universally
supported by GCC/Clang/MSVC; replaces the `#ifndef`/`#define`/`#endif` triple.
- **Per-file banner:** three lines —
```cpp
/// @file spsc_ring.h
/// @brief Lock-free single-producer single-consumer ring buffer.
// SPDX-License-Identifier: MIT
// Copyright 2025-2026 Timothy Place.
```
- **Doc comments:** `///` triple-slash with `@`-style commands
(`@param`, `@return`, `@throws`, `@pre`). Not the `\`-command dialect.
- **Include ordering:** (1) the file's own corresponding header (in a `.cpp`),
(2) C++ standard headers, (3) third-party, (4) this project — enforced by
clang-format `IncludeBlocks: Regroup`, blank line between groups.

## 4. Safety idioms

The Tap libraries are header-only, zero-dependency, and target real-time /
embedded use (some build `-fno-exceptions`). We adopt the *vocabulary* of the
GSL but take **no dependency on it**; the helpers below are freestanding.

- **Contracts:** `TAP_EXPECTS(cond)` / `TAP_ENSURES(cond)` — assert in debug,
clamp or no-op in release, **never throw**. (Generalizes AmbiTap's
`validate.h`.) Not `gsl::Expects` (terminates + adds a dependency).
- **Narrowing:** `narrow_cast<T>(x)` — a documented `static_cast` synonym for
intentional lossy conversions (e.g. Q15/Q31 fixed-point). Not `gsl::narrow`
(throws; unusable under `-fno-exceptions`).
- **Views:** `std::span` (C++20, freestanding-friendly). Never `gsl::span`.
- **Non-null / ownership / bounds:** expressed via `@pre` documentation and
debug asserts, **not** wrapper types. Raw pointers and raw indexing stay in
hot paths for performance; `not_null`/`owner`/`at()` are not used as types.
Loading
Loading