Files
playground/docs/adr/0002-pda-aufteilung.md
T
paulhorn db41258d16 feat: PDA als libpda + pda nach P1204R0
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>
2026-08-30 16:39:35 +02:00

2.8 KiB

ADR 0002 — Aufteilung in libpda und pda

  • Status: akzeptiert
  • Datum: 2026-08-30

Kontext

Das Projekt soll einen kleinen PDA umsetzen (Kontakte, Rechner, Notizen, Dateiexplorer) und dabei ausdrücklich als Bibliothek benutzbar sein — man soll damit eigene Sachen bauen können.

P1204R0 sagt dazu: „If a project consists of a library and an executable, then they should be split into separate projects."

Die naheliegende Alternative wäre ein Projekt mit einem pda_core-Target und einer Executable daneben — so machen es mydb und cxx_scaffold_cli.

Entscheidung

Zwei eigenständige Projekte unter einem Superprojekt:

  • libpda/ mit eigenem project(libpda), exportiert pda::pda
  • pda/ mit eigenem project(pda), linkt pda::pda

Beide sind einzeln konfigurierbar. pda/CMakeLists.txt benutzt im Alleinbetrieb find_package(pda REQUIRED).

Konsequenzen

Die Bibliothek ist nachweislich unabhängig: cmake -S libpda -B build/nur-lib baut und testet sie ohne die Anwendung (47 der 71 Tests).

Die Anwendung ist damit gleichzeitig der Integrationstest für den Export-Mechanismus. Was sie im Alleinbetrieb tut, tut auch jedes fremde Projekt.

examples/consumer/ und tools/check-install.sh machen das explizit: das Skript installiert libpda in ein temporäres Präfix und baut ein fremdes Programm dagegen. Genau diese Kette fängt den häufigsten Verteilungsfehler — ein target_include_directories ohne BUILD_INTERFACE/INSTALL_INTERFACE zeigt beim Benutzer ins Leere und fällt im eigenen Build nie auf.

Der Preis ist eine Ebene mehr im Pfad (libpda/libpda/contact.hpp) und der PROJECT_IS_TOP_LEVEL-Block in beiden Teilprojekten. Beides ist der Preis dafür, dass „Bibliothek" hier nicht nur ein Wort ist.

Nebenentscheidungen

Ein Namespace für beide Projekte (namespace pda), unterschieden über den Include-Pfad — so wie libhello/hello in P1204R0. Das lib-Präfix steht im Projekt- und Verzeichnisnamen, nicht im Namespace.

Die C-Schicht ist echt, nicht dekorativ. textfile.c macht Datei-I/O und Feld-Escaping — die Stelle, an der Besitzverhältnisse und rohe Puffer sichtbar sind. Alles darüber ist C++ und verpackt das in RAII. Die Naht ist in contact.cpp zu sehen: unique_ptr mit eigenem Deleter um die char*, die textfile_escape liefert.

std::expected statt Exceptions für Benutzerfehler (Tippfehler im Ausdruck, fehlende Datei). Ein Parser-Fehler ist der Normalfall dieser Funktionen, kein Ausnahmezustand. Exceptions bleiben für das, was wirklich nicht vorgesehen ist.

Die Shell kennt keine Ein-/Ausgabe. Shell::execute() bekommt eine Zeile und gibt Text zurück; wer ihn anzeigt, ist ihre Sache nicht. Deshalb braucht kein einziger der Shell-Tests ein Terminal, und eine GUI ließe sich ohne Änderung an shell.cpp davorsetzen.