db41258d16
Ersetzt das Geruest (counter/notebook) durch ein echtes Beispiel und teilt es in zwei eigenstaendige Projekte, wie P1204R0 es fuer Bibliothek plus Programm verlangt. libpda: textfile (C, Datei-I/O und Feld-Escaping), calculator (Recursive-Descent-Parser mit std::expected), contact, editor, explorer. Exportiert pda::pda ueber install(EXPORT) und ist per find_package(pda) benutzbar. pda: Shell ohne Ein-/Ausgabe (execute() gibt Text zurueck, deshalb ohne Terminal testbar), REPL und Stapelbetrieb in main.cpp. examples/consumer und tools/check-install.sh pruefen die Export-Kette Ende-zu-Ende: installieren, dann ein fremdes Projekt dagegen bauen. docs/CMAKE.md erklaert das Target-Modell, PUBLIC/PRIVATE/INTERFACE, Generator-Ausdruecke, install/export und Symbolsichtbarkeit an diesem Projekt. 71 Tests gruen unter Homebrew-clang 22, Apple clang und GCC 16, statisch und shared, mit und ohne ASan/UBSan. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
190 lines
7.1 KiB
Markdown
190 lines
7.1 KiB
Markdown
# Konventionen
|
|
|
|
Was `clang-format` und `clang-tidy` nicht erzwingen können, steht hier.
|
|
Mechanisch erzwungen wird bisher nur, was `clang-format` und `clang-tidy`
|
|
abdecken. Ein `check_style.sh` gibt es in diesem Repo **nicht** (die
|
|
CONVENTIONS.md von mydb verweist darauf, aber die Datei existiert dort
|
|
ebenfalls nicht) — die folgenden Regeln gelten also per Konvention.
|
|
|
|
## Projektstruktur (P1204R0)
|
|
|
|
Zwei eigenständige Projekte unter einem Superprojekt:
|
|
|
|
```
|
|
playground/ Superprojekt, kein eigener Quellcode
|
|
├── libpda/ project(libpda) -> libpda.a
|
|
│ ├── libpda/ Quellcode UND Header zusammen
|
|
│ │ ├── textfile.h public API (C)
|
|
│ │ ├── textfile.c
|
|
│ │ ├── textfile.test.c Unit-Test, direkt neben dem Modul
|
|
│ │ ├── contact.hpp public API (C++)
|
|
│ │ ├── contact.cpp
|
|
│ │ ├── contact.test.cpp
|
|
│ │ └── details/ Implementation Details, nicht public API
|
|
│ └── tests/basics/ Integrationstests gegen die öffentliche API
|
|
├── pda/ project(pda) -> bin/pda
|
|
│ ├── pda/ shell.hpp/.cpp, main.cpp
|
|
│ └── tests/session/
|
|
├── examples/consumer/ fremdes Projekt, find_package(pda)
|
|
├── third_party/ vendorter Fremdcode, nie editieren
|
|
├── cmake/ ProjectDefaults, Warnings, Sanitizers, pdaConfig.cmake.in
|
|
└── docs/ CMAKE.md, diese Datei, ADRs
|
|
```
|
|
|
|
**Kein `include/` + `src/`.** Header und Implementierung liegen nebeneinander.
|
|
Bei Templates, `inline` und Modulen ist die Trennung ohnehin nicht sauber zu
|
|
ziehen, und der Verzeichnisname übernimmt die Rolle des Namespace im
|
|
Dateisystem.
|
|
|
|
**Bibliothek und Programm sind getrennte Projekte.** P1204R0 verlangt das; der
|
|
praktische Grund ist, dass `libpda` ohne `pda` baubar sein muss. Die Probe:
|
|
|
|
```sh
|
|
cmake -S libpda -B build/nur-lib && cmake --build build/nur-lib
|
|
```
|
|
|
|
**Ein Namespace, zwei Include-Wurzeln.** Beide Projekte benutzen
|
|
`namespace pda` — auseinandergehalten wird über den Pfad:
|
|
|
|
```cpp
|
|
#include <libpda/contact.hpp> // Bibliothek
|
|
#include <pda/shell.hpp> // Anwendung
|
|
```
|
|
|
|
Das ist genau das Schema aus P1204R0 (`libhello` / `hello`): das `lib`-Präfix
|
|
steht im Projekt- und Verzeichnisnamen, nicht im Namespace.
|
|
|
|
## Includes
|
|
|
|
**Eigene Header immer mit spitzen Klammern und Projektpräfix:**
|
|
|
|
```c
|
|
#include <libpda/textfile.h> /* richtig */
|
|
#include "textfile.h" /* falsch */
|
|
```
|
|
|
|
Das ist P1204R0s wichtigste Einzelregel. Spitze Klammern durchsuchen nur die
|
|
`-I`-Pfade, niemals das Verzeichnis der inkludierenden Datei — ein fehlender
|
|
oder falsch installierter Header fällt damit sofort auf, statt dass zufällig
|
|
ein gleichnamiger Nachbar gefunden wird.
|
|
|
|
Reihenfolge macht `clang-format` (`IncludeBlocks: Regroup`): eigener Header,
|
|
Projekt-Header, Test-Framework, C-Standardbibliothek, C++-Standardbibliothek,
|
|
Rest.
|
|
|
|
## Extensions
|
|
|
|
| Extension | Bedeutung |
|
|
|---|---|
|
|
| `.h` / `.c` | C |
|
|
| `.hpp` / `.cpp` | C++ |
|
|
| `.test.c` / `.test.cpp` | Unit-Test neben dem getesteten Modul |
|
|
|
|
Die Extension sagt die Sprache. In einem gemischten Projekt ist das der Punkt —
|
|
ein `.h` ist aus C **und** C++ benutzbar (der `extern "C"`-Block im Header),
|
|
ein `.hpp` nur aus C++.
|
|
|
|
## Namen
|
|
|
|
| Was | Form | Beispiel |
|
|
|---|---|---|
|
|
| Typen | `PascalCase` | `ContactBook`, `TextBuffer`, `Explorer` |
|
|
| Funktionen, Variablen | `snake_case` | `human_size`, `line_count` |
|
|
| Makros | `UPPER_CASE` mit Projektpräfix | `LIBPDA_TEXTFILE_H`, `PDA_EXPORT` |
|
|
| C-API | Modulpräfix | `textfile_read`, `textfile_write`, `textfile_escape` |
|
|
| Out-Parameter | `out_`-Präfix | `size_t* out_len` |
|
|
| Member | blank, kein `m_`, kein `_` | `entries`, `rows`, `here` |
|
|
| Namespace | Projektname ohne `lib` | `namespace pda` |
|
|
|
|
## Kommentare
|
|
|
|
Ausschließlich `/* */`, niemals `//` — auch nicht einzeilig. Abschnitte werden
|
|
so getrennt:
|
|
|
|
```c
|
|
/* ---- API ---- */
|
|
```
|
|
|
|
Kommentare sind auf Deutsch, **Umlaute transliteriert** (`ae oe ue ss`), damit
|
|
die Quellen reines ASCII bleiben. README und öffentliche Header auf Englisch.
|
|
Prüfen lässt sich das mit:
|
|
|
|
```sh
|
|
grep -rlP '[^\x00-\x7F]' playground tests
|
|
```
|
|
|
|
## Header
|
|
|
|
`#ifndef LIBPDA_TEXTFILE_H` — Include-Guards, kein `#pragma once`.
|
|
Öffentliche C-Header bekommen einen `extern "C"`-Block, damit die C++-Seite sie
|
|
benutzen kann.
|
|
|
|
Structs, deren Layout niemanden angeht, sind opak: `typedef struct Page Page;`
|
|
im Header, die Definition in der `.c`.
|
|
|
|
## Tests
|
|
|
|
**Unit-Test** (`<modul>.test.c` / `.test.cpp`) liegt neben dem Modul, kennt
|
|
dessen Interna und ist eine eigenständige Executable.
|
|
|
|
- C → Unity, registriert als CTest `unity.<modul>` (hier: `unity.textfile`)
|
|
- C++ → doctest, `doctest_discover_tests()` legt **einen CTest-Eintrag pro
|
|
`TEST_CASE`** an
|
|
|
|
**Integrationstest** (`tests/<name>/driver.c` oder `.cpp`) benutzt nur die öffentliche API,
|
|
kein Framework, Ergebnis über den Exit-Code. Er würde genauso gegen eine
|
|
installierte Bibliothek laufen.
|
|
|
|
TDD-Zyklus: RED (Test schlägt aus dem richtigen Grund fehl) → GREEN (kleinste
|
|
Implementierung) → REFACTOR.
|
|
|
|
## Build
|
|
|
|
Die ausführliche Erklärung steht in [CMAKE.md](CMAKE.md). Das Wichtigste:
|
|
|
|
- Öffentliche Deklarationen der Bibliothek brauchen `PDA_EXPORT`, sonst
|
|
exportiert der Shared-Bau sie nicht.
|
|
- Neue Dateien immer von Hand in `LIBPDA_SOURCES` / `LIBPDA_HEADERS`.
|
|
|
|
Kein `file(GLOB)`. Neue Dateien werden in `CMakeLists.txt` eingetragen — CMake
|
|
merkt sonst nicht, dass eine Datei dazugekommen ist, der Build bleibt grün und
|
|
die Datei fehlt einfach.
|
|
|
|
| Preset | Zweck |
|
|
|---|---|
|
|
| `debug` | Alltag |
|
|
| `release` | RelWithDebInfo |
|
|
| `asan-ubsan` | AddressSanitizer + UBSan |
|
|
| `apple-clang` | Portabilitätsprobe (macOS-System-Compiler) |
|
|
| `homebrew-gcc` | Portabilitätsprobe (GCC-Frontend) |
|
|
| `linux-gcc` | nur auf Linux sichtbar |
|
|
|
|
`build/<preset>/` — die Tiefe ist bindend: der neotest-CTest-Adapter sucht nur
|
|
drei Ebenen tief, und der Neovim-Target-Picker leitet den File-API-Pfad daraus ab.
|
|
|
|
## Commits
|
|
|
|
Conventional Commits: `feat:`, `fix:`, `test:`, `refactor:`, `chore:`, `docs:`.
|
|
Aktuell ohne Hook — es gibt kein `check_style.sh`, das einen installieren
|
|
könnte.
|
|
|
|
## Zwei Dinge, die wie Fehler aussehen, aber keine sind
|
|
|
|
**Nach einem frischen Clone zeigt clangd nichts.** Es gibt noch kein
|
|
`compile_commands.json`. Einmal `<leader>oc` (configure), dann ist alles da.
|
|
Neovim weist beim Öffnen einer C/C++-Datei darauf hin.
|
|
|
|
**Der Testbaum (`<leader>ts`) ist nach einem frischen Clone leer.**
|
|
`doctest_discover_tests()` ruft die Test-Executable beim **Build** auf, um die
|
|
`TEST_CASE`s aufzulisten. Vor dem ersten Build kennt CTest sie also nicht. Einmal
|
|
`<leader>b`, dann füllt sich der Baum.
|
|
|
|
## Rückfallebene für die C-Tests
|
|
|
|
Der neotest-Adapter für Unity (`nvim-local/neotest-unity`) ist selbst
|
|
geschrieben, weil `neotest-ctest` `.c`-Dateien grundsätzlich nicht sieht. Wenn er
|
|
je zur Last wird: aus der `adapters`-Liste in `nvim/lua/plugins/neotest.lua`
|
|
entfernen, neotest bleibt für C++ zuständig, und die C-Tests laufen über
|
|
`<leader>ot` (overseer → ctest → Quickfix). Dann fehlt der Rot/Grün-Baum für C,
|
|
aber ein Tastendruck und Sprung zur Fehlerstelle bleiben.
|