Home

07 Mehrsprachige Projekte: C++ Codequalität und Testing

lecture cpp python clang-format clang-tidy google-test gcov coverage code-quality

1. Einführung: Aufbauend auf C++-Grundlagen

In der vorherigen Vorlesung (Teil 1) haben Sie die Grundlagen der C++-Entwicklung kennengelernt:

Diese Vorlesung konzentriert sich auf Codequalität:

Code zu schreiben, der kompiliert, ist nur die halbe Miete—wir brauchen Code, der lesbar, wartbar und gut getestet ist.

┌─────────────────────────────────────────────────────────────────┐
│                    C++ QUALITY PIPELINE                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Source Code → clang-format → clang-tidy → Google Test → gcov   │
│       │              │              │            │          │   │
│       ▼              ▼              ▼            ▼          ▼   │
│   Your .cpp    Consistent     Bug-free     Verified    Measured │
│   files        style          code         behavior    coverage │
│                                                                 │
│  Just like Python: Ruff → Pyright → pytest → pytest-cov         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Diese Vorlesung behandelt die C++-Äquivalente zu Ihren Python-Qualitätswerkzeugen:

  1. Code-Formatierung — clang-format (wie Black/Ruff)
  2. Statische Analyse — clang-tidy + Compiler-Warnungen (wie Pyright/Ruff)
  3. Unit-Testing — Google Test (wie pytest)
  4. Coverage-Reports — gcov/llvm-cov (wie pytest-cov)

Am Ende werden Sie die gleiche Qualitätssicherung für C++ haben, die Sie bereits für Python haben. Teil 3 wird Ihnen dann zeigen, wie Sie beide Sprachen in einem einzigen Projekt integrieren.


2. Lernziele

Am Ende dieser Vorlesung werden Sie in der Lage sein:

Code-Formatierung:

  1. clang-format anzuwenden, um einen konsistenten C++-Code-Stil durchzusetzen
  2. .clang-format-Dateien zu konfigurieren unter Verwendung des Google C++ Style Guide

Statische Analyse:

  1. Compiler-Warnungen zu konfigurieren für GCC, Clang und MSVC
  2. clang-tidy zu verwenden für erweiterte statische Analyse und Modernisierungsprüfungen

Unit-Testing:

  1. Google Test-Tests zu schreiben mit TEST(), EXPECT_* und ASSERT_* Makros
  2. Test-Fixtures zu verwenden für gemeinsames Setup und Teardown

Coverage:

  1. C++ Coverage-Reports zu generieren mit gcov/llvm-cov
  2. Coverage-Metriken zu interpretieren und ungetestete Codepfade zu identifizieren

Vergleich:

  1. Python-Qualitätswerkzeuge auf C++-Äquivalente abzubilden (Ruff → clang-format/clang-tidy, pytest → Google Test, pytest-cov → gcov)

3. Codequalität für C++

In Teil 1 haben wir gelernt, wie man C++-Code kompiliert, CMake für die Build-Konfiguration verwendet und Abhängigkeiten verwaltet. Aber Code zu schreiben, der kompiliert, ist nur die halbe Miete—wir brauchen auch Code, der lesbar, wartbar und fehlerfrei ist.

Sie kennen diese Disziplin bereits aus Python. Erinnern Sie sich an die pyproject.toml des Road-Profile-Viewers?

[tool.ruff]
line-length = 120
target-version = "py312"

[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "B", "C4", "UP"]

[tool.pyright]
typeCheckingMode = "strict"
pythonVersion = "3.12"

Diese Konfiguration erzwingt:

Jetzt brauchen wir die gleiche Disziplin für C++. Aber es gibt eine Komplikation…

3.1 Das Problem: C++ hat kein PEP 8

In Python ist Codequalität bemerkenswert standardisiert:

┌─────────────────────────────────────────────────────────────────────┐
│                    PYTHON CODE QUALITY ECOSYSTEM                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Style Guide:      PEP 8 (the standard, everyone agrees)            │
│                           │                                         │
│                           ▼                                         │
│  Tool:             Ruff (or Black + Flake8 + isort)                 │
│                           │                                         │
│                           ▼                                         │
│  Configuration:    pyproject.toml (one file for everything)         │
│                           │                                         │
│                           ▼                                         │
│  Result:           Consistent code across all Python projects       │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

C++ ist fragmentierter:

┌─────────────────────────────────────────────────────────────────────┐
│                    C++ CODE QUALITY ECOSYSTEM                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Style Guides:     Google Style    LLVM Style    Mozilla Style      │
│                    GNU Style       Chromium      Linux Kernel       │
│                    MISRA C++       (and dozens more...)             │
│                           │                                         │
│                           ▼                                         │
│  Tools:            clang-format (formatting)                        │
│                    clang-tidy (linting)                             │
│                    cppcheck, cpplint, PVS-Studio, ...               │
│                           │                                         │
│                           ▼                                         │
│  Configuration:    .clang-format + .clang-tidy (separate files)     │
│                    No single standard configuration                 │
│                           │                                         │
│                           ▼                                         │
│  Result:           Every project looks different                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Warum das wichtig ist:

Wenn Sie ein neues Python-Projekt klonen, können Sie den Code sofort lesen—er sieht alles wie PEP 8 aus. Wenn Sie ein C++-Projekt klonen, finden Sie möglicherweise:

Die gute Nachricht: Die C++-Community hat sich auf zwei ausgezeichnete Werkzeuge aus dem LLVM-Projekt geeinigt:

  1. clang-format — Automatische Code-Formatierung (wie ruff format)
  2. clang-tidy — Statische Analyse und Linting (wie ruff check)

Mit der richtigen Konfiguration können Sie in C++ die gleiche Konsistenz erreichen, die Sie aus Python gewohnt sind.

3.2 Der Google C++ Style Guide

Für diesen Kurs verwenden wir den Google C++ Style Guide—einen der am weitesten verbreiteten Standards. Google hat seinen internen Style Guide als Open Source veröffentlicht, und er wird jetzt verwendet von:

Warum Google Style für diesen Kurs?

Grund Erklärung
Gut dokumentiert Der Style Guide erklärt warum jede Regel existiert, nicht nur was zu tun ist
Tool-Unterstützung clang-format hat eingebautes BasedOnStyle: Google
Python-freundlich Verwendet snake_case für Funktionen/Variablen (wie Python!)
Modernes C++ Aktualisiert für C++11/14/17/20-Features
Industrie-Akzeptanz Google Style zu kennen ist nützlich für Ihre Karriere

3.3 Namenskonventionen: Python vs. Google C++

So vergleichen sich die Namenskonventionen:

Element Python (PEP 8) Google C++ Beispiel
Funktionen snake_case snake_case calculate_distance()
Variablen snake_case snake_case ray_angle
Klassen PascalCase PascalCase RoadProfile
Konstanten ALL_CAPS kPascalCase Python: MAX_SPEED
C++: kMaxSpeed
Private Member _leading_underscore trailing_underscore_ Python: self._data
C++: data_

Beachte: Funktionen und Variablen verwenden die gleiche Konvention! Das ist beabsichtigt—Googles Style Guide wurde von Python beeinflusst.

// Google C++ Style - feels familiar to Python developers
class RoadProfile {
 public:
  double calculate_elevation(double x_position) const;

 private:
  std::vector<double> elevation_data_;  // trailing underscore for private
};

// compare to Python:
// class RoadProfile:
//     def calculate_elevation(self, x_position: float) -> float:
//
//     _elevation_data: list[float]  # leading underscore for private

3.4 Formatierungskonventionen

Einige wichtige Unterschiede zu Python:

// Google C++ Style

// 2-space indentation (not 4 like Python)
if (condition) {
  do_something();
  do_something_else();
}

// Opening brace on same line (like Python's ":" at end of line)
void calculate_ray_line(double angle) {
  // implementation
}

// Line length: 80 characters (configurable to 100/120)
// This matches what you configured in pyproject.toml: line-length = 120

Die Philosophie ist die gleiche: Konsistente Formatierung macht Code leichter lesbar. Die spezifischen Regeln unterscheiden sich leicht, aber die Disziplin ist identisch.


4. clang-format: Der C++-Formatter

In Python führen Sie ruff format (oder black) aus, um Ihren Code automatisch zu formatieren. In C++ ist das Äquivalent clang-format.

4.1 Wie clang-format mit Ruff zusammenhängt

Denke an clang-format als “Ruff format für C++”:

Aspekt Python (Ruff format) C++ (clang-format)
Zweck Python-Code automatisch nach PEP 8 formatieren C++-Code automatisch nach gewähltem Stil formatieren
Config-Datei pyproject.toml oder ruff.toml .clang-format
In-Place formatieren ruff format . clang-format -i file.cpp
Nur prüfen (CI) ruff format --check . clang-format --dry-run --Werror file.cpp
Basis-Stil PEP 8 (implizit) BasedOnStyle: Google (explizit)

4.2 Konfigurationsvergleich: pyproject.toml vs .clang-format

Vergleichen wir die Formatierungskonfiguration deines Road-Profile-Viewers mit einer äquivalenten C++-Konfiguration:

Python (pyproject.toml):

[tool.ruff]
line-length = 120
target-version = "py312"

[tool.ruff.format]
quote-style = "double"
line-ending = "lf"
indent-style = "space"
docstring-code-format = true

C++ (.clang-format):

# .clang-format
---
Language: Cpp
BasedOnStyle: Google

# Line length (like line-length = 120)
ColumnLimit: 100

# Indentation (like indent-style = "space")
IndentWidth: 2
UseTab: Never

# Quote style doesn't apply (C++ uses "" for strings, '' for chars)
# But we can control include style:
IncludeBlocks: Regroup

# Brace style
BreakBeforeBraces: Attach

# Short forms
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false

# Template formatting
AlwaysBreakTemplateDeclarations: Yes

# Include ordering (like isort in Python)
IncludeCategories:
  - Regex: '^<.*\.h>'     # C headers first
    Priority: 1
  - Regex: '^<.*>'        # C++ standard library
    Priority: 2
  - Regex: '^".*"'        # Project headers last
    Priority: 3
...

Wichtige Erkenntnis: Beide Konfigurationen lösen die gleichen Probleme—sie verwenden nur unterschiedliche Syntax. Der Abschnitt IncludeCategories in clang-format entspricht direkt dem, was isort (jetzt Teil von Ruff) für Python-Imports tut.

4.3 clang-format installieren

# Ubuntu/Debian
sudo apt install clang-format

# macOS
brew install clang-format

# Windows (with LLVM - includes clang-format and clang-tidy)
choco install llvm
# Or with winget:
winget install LLVM.LLVM

Installation überprüfen:

clang-format --version
# Output: clang-format version 17.0.6 (or similar)

4.4 clang-format ausführen

Der Workflow spiegelt das wider, was Sie mit Ruff machen:

Eine einzelne Datei formatieren (wie ruff format file.py):

clang-format -i src/geometry.cpp

Das -i-Flag bedeutet “in-place” (Datei modifizieren). Ohne es gibt clang-format den formatierten Code auf stdout aus.

Prüfen ohne zu modifizieren (für CI, wie ruff format --check):

clang-format --dry-run --Werror src/geometry.cpp

Alle C++-Dateien formatieren (wie ruff format .):

# Linux/macOS
find . -name '*.cpp' -o -name '*.hpp' | xargs clang-format -i

# Or use a more portable approach with CMake (we'll see this in CI)

4.5 Vorher und Nachher: clang-format in Aktion

Vor der Formatierung:

#include "geometry.hpp"
#include <cmath>
#include <vector>
#include <iostream>

double calculate_ray_line(double angle,double camera_x,double camera_y,double x_max){
double angle_rad=-angle*3.14159265359/180.0;
double slope=std::tan(angle_rad);
if(std::abs(std::cos(angle_rad))<1e-10){return 0.0;}
double x_end=x_max;
return slope*(x_end-camera_x)+camera_y;
}

Nach clang-format -i mit Google-Stil:

#include <cmath>
#include <iostream>
#include <vector>

#include "geometry.hpp"

double calculate_ray_line(double angle, double camera_x, double camera_y,
                          double x_max) {
  double angle_rad = -angle * 3.14159265359 / 180.0;
  double slope = std::tan(angle_rad);
  if (std::abs(std::cos(angle_rad)) < 1e-10) {
    return 0.0;
  }
  double x_end = x_max;
  return slope * (x_end - camera_x) + camera_y;
}

Was sich geändert hat:


5. clang-tidy: Der C++-Linter

Während clang-format behandelt, wie Ihr Code aussieht, analysiert clang-tidy, was Ihr Code tut. Es findet Bugs, schlägt Verbesserungen vor und erzwingt Best Practices—genau wie ruff check es für Python tut.

5.1 Wie clang-tidy mit Ruff Check zusammenhängt

Erinnern Sie sich an die Ruff-Konfiguration in Ihrem Road-Profile-Viewer?

[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "B", "C4", "UP"]

Jeder Buchstabe ist eine Kategorie von Prüfungen:

clang-tidy funktioniert genauso—es hat Kategorien von Prüfungen, die Sie aktivieren oder deaktivieren können:

Python (Ruff) C++ (clang-tidy) Was es findet
F (Pyflakes) bugprone-* Wahrscheinliche Bugs, undefiniertes Verhalten
B (Bugbear) bugprone-* Subtile Bugs, gefährliche Muster
C4 (Comprehensions) performance-* Performance-Verbesserungen
UP (pyupgrade) modernize-* Moderne Sprachfeatures verwenden
N (pep8-naming) readability-identifier-naming Namenskonventionen
E, W (pycodestyle) readability-* Stil und Klarheit
(Kein direktes Äquivalent) cppcoreguidelines-* C++ Core Guidelines (Sicherheit)

5.2 Konfigurationsvergleich: pyproject.toml vs .clang-tidy

Python (pyproject.toml):

[tool.ruff.lint]
# Select which rule categories to enable
select = ["E", "W", "F", "I", "N", "B", "C4", "UP"]

# Make all rules auto-fixable
fixable = ["ALL"]

# Ignore specific rules
ignore = ["E501"]  # Ignore line length (handled by formatter)

C++ (.clang-tidy):

# .clang-tidy
---
# Enable check categories (like select = [...])
Checks: >
  -*,
  bugprone-*,
  performance-*,
  modernize-*,
  readability-*,
  -modernize-use-trailing-return-type,
  -readability-magic-numbers

# Treat specific warnings as errors (like --error-on in Ruff)
WarningsAsErrors: ''

# Configure specific checks (like per-rule settings in Ruff)
CheckOptions:
  - key: readability-identifier-naming.NamespaceCase
    value: lower_case
  - key: readability-identifier-naming.ClassCase
    value: CamelCase
  - key: readability-identifier-naming.FunctionCase
    value: lower_case
  - key: readability-identifier-naming.VariableCase
    value: lower_case
...

Das Muster ist identisch:

  1. Beginne damit, alle Prüfungen zu deaktivieren (-* oder mit leerer Auswahl starten)
  2. Aktiviere gewünschte Kategorien (bugprone-* oder "F")
  3. Deaktiviere spezifische nervige Regeln (-readability-magic-numbers oder ignore = ["E501"])

5.3 Warum clang-tidy compile_commands.json braucht

Hier ist ein wichtiger Unterschied: clang-tidy muss Ihre Projektstruktur verstehen.

Python (Ruff): Kann jede .py-Datei sofort analysieren—Pythons Import-System ist standardisiert.

C++ (clang-tidy): Muss wissen:

CMake generiert eine compile_commands.json-Datei, die all diese Informationen enthält:

# Generate compile_commands.json
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

# This creates build/compile_commands.json:
# [
#   {
#     "directory": "/path/to/project/build",
#     "command": "g++ -std=c++20 -I../include -c ../src/geometry.cpp",
#     "file": "../src/geometry.cpp"
#   },
#   ...
# ]

Jetzt weiß clang-tidy genau, wie jede Datei kompiliert werden soll, und kann sie korrekt analysieren.

5.4 clang-tidy ausführen

Schritt 1: Kompilierungsdatenbank generieren:

cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

Schritt 2: Auf einer einzelnen Datei ausführen (wie ruff check file.py):

clang-tidy -p build src/geometry.cpp

Das -p build-Flag sagt clang-tidy, wo es compile_commands.json findet.

Schritt 3: Auf allen Quelldateien ausführen (wie ruff check .):

# Linux/macOS
find src -name '*.cpp' | xargs clang-tidy -p build

# Or use run-clang-tidy (parallel, faster)
run-clang-tidy -p build src/

5.5 Beispiel: Was clang-tidy findet

Ihr Code:

void process_data(std::vector<double> data) {  // Pass by value (expensive copy!)
    for (int i = 0; i < data.size(); i++) {    // Signed/unsigned comparison
        if (data[i] = 0.0) {                   // Assignment instead of comparison!
            continue;
        }
    }
}

clang-tidy-Ausgabe:

geometry.cpp:1:25: warning: parameter 'data' is passed by value; consider
                  passing by const reference [performance-unnecessary-value-param]
geometry.cpp:2:21: warning: comparison of integers of different signs
                  [bugprone-signed-unsigned-comparison]
geometry.cpp:3:21: warning: using the result of an assignment as a condition
                  without parentheses [bugprone-assignment-in-if-condition]

Vergleiche mit Ruff, das ähnliche Probleme in Python findet:

def process_data(data):
    for i in range(len(data)):  # Ruff: Use enumerate() instead [C416]
        if data[i] = 0.0:       # Python: SyntaxError (caught by interpreter)
            continue

Die Philosophie ist die gleiche: Bugs finden, bevor sie in die Produktion gelangen.

5.6 VS Code-Integration: clangd-Erweiterung

In Python haben Sie die Ruff-Erweiterung für VS Code installiert, um Echtzeit-Formatierung und Linting zu erhalten. Für C++ ist das Äquivalent clangd—ein Language Server, der die Leistung von clang-format und clang-tidy direkt in Ihre IDE bringt.

clangd installieren:

  1. Öffne VS Code Extensions (Strg+Umschalt+X)
  2. Suche nach “clangd” (von LLVM)
  3. Klicke auf Installieren

Was clangd bietet:

🐍 Ruff = clangd
Feature 🐍Python (Ruff) C++ (clangd)
Formatierung beim Speichern
Automatisch
Automatisch
Echtzeit-Linting
Wellenlinien
Wellenlinien
Schnellkorrekturen
Code-Aktionen
Code-Aktionen
Gehe zur Definition
Pylance/Pyright
clangd
Hover-Dokumentation
Typ-Hinweise
Typ-Info + Doku

VS Code-Einstellungsvergleich:

Python (settings.json):

{
  // Ruff as the formatter
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true
  },
  // Enable Ruff linting
  "ruff.lint.enable": true
}

C++ (settings.json):

{
  // clangd as the formatter (uses .clang-format)
  "[cpp]": {
    "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd",
    "editor.formatOnSave": true
  },
  // clangd settings
  "clangd.arguments": [
    "--background-index",
    "--clang-tidy",
    "--header-insertion=never"
  ]
}

Wichtige clangd-Argumente:

Argument Zweck Ruff-Äquivalent
--clang-tidy Echtzeit-Linting aktivieren ruff.lint.enable: true
--background-index Projekt für schnelle Navigation indizieren (Pylance macht das automatisch)
--header-insertion=never Includes nicht automatisch hinzufügen (N/A für Python)
--compile-commands-dir=build Wo compile_commands.json zu finden ist (Python braucht das nicht)

Der Workflow ist identisch:

  1. Code schreiben → clangd zeigt Echtzeit-Warnungen (gelbe/rote Wellenlinien)
  2. Datei speichern → clang-format formatiert automatisch
  3. Über Warnung hovern → Erklärung und Lösungsvorschlag sehen
  4. Auf Glühbirne klicken → Automatische Korrektur anwenden

Empfohlene Erweiterungen für C++-Entwicklung:

Erweiterung Zweck Python-Äquivalent
clangd (LLVM) Formatierung, Linting, Navigation Ruff + Pylance
CMake Tools Build, Debug, CMake konfigurieren (uv/pip sind einfacher)
C/C++ Extension Pack Debugger, zusätzliche Werkzeuge Python Debugger

Wichtig: Wenn Sie die Microsoft C/C++-Erweiterung installiert haben, müssen Sie möglicherweise deren IntelliSense deaktivieren, um Konflikte mit clangd zu vermeiden. Fügen Sie zu settings.json hinzu:

{
  "C_Cpp.intelliSenseEngine": "disabled"
}

Die wichtige Erkenntnis: So wie Sie Ruff in VS Code konfiguriert haben, um Python beim Speichern zu formatieren und zu linten, macht clangd das Gleiche für C++. Die Konfigurationsdateien sind unterschiedlich (.clang-format + .clang-tidy vs pyproject.toml), aber das Entwicklererlebnis ist identisch.


6. Statische Codeanalyse

Statische Analyse untersucht Code ohne ihn auszuführen. In Python war dies eingeschränkt, weil Python dynamisch typisiert ist. In C++ ist statische Analyse mächtiger, weil die Sprache statisch typisiert ist.

6.1 Warum statische Codeanalyse?

Bevor wir uns mit den Werkzeugen beschäftigen, lass uns verstehen, warum statische Analyse wichtig ist:

1. Fehler vor der Laufzeit finden

Statische Analyse findet Probleme, die sonst erst zur Laufzeit auftreten würden (oder schlimmer noch, in der Produktion):

Problemtyp Ohne statische Analyse Mit statischer Analyse
Null-Pointer-Dereferenzierung Absturz in Produktion Zur Compile-Zeit gefunden
Buffer-Overflow Sicherheitslücke Warnung vor dem Merge
Ressourcen-Leak Speicherverbrauch steigt über Zeit Im PR-Review erkannt
Typverwechslung Undefiniertes Verhalten Compiler-Fehler

2. Code-Review-Aufwand reduzieren

Wenn statische Analyse Stilprobleme und offensichtliche Fehler automatisch findet, können sich menschliche Reviewer auf folgendes konzentrieren:

3. Konsistenz im Team durchsetzen

Ohne automatisierte Prüfungen hängt der Code-Stil davon ab, wer den PR reviewed. Mit statischer Analyse:

4. Kosten für Fehlerbehebung steigen über die Zeit

Kosten, um einen Fehler zu beheben:
  Während des Codens:   1x (Sie sind eh schon da)
  Im Code-Review:       5x (Kontextwechsel für Reviewer)
  Während des Testens: 10x (Test schreiben, debuggen, fixen)
  In Produktion:       100x (Incident-Response, Kundenauswirkung)

Statische Analyse findet Fehler in der 1x-Kosten-Phase.

6.2 CI-Integration: Der Schlüssel zu effektiver Analyse

Statische Analyse-Werkzeuge lokal installiert zu haben ist nützlich, aber die wahre Stärke kommt von der Integration in Ihre CI-Pipeline. Das Schlüsselprinzip:

Jeder gemergte PR muss die statische Analyse bestehen. Keine Ausnahmen.

Warum CI-Integration essentiell ist:

  1. Entwickler können es nicht überspringen - Lokale Prüfungen können umgangen werden; CI nicht
  2. Konsistente Umgebung - Gleiche Compiler-Version, gleiche Flags, gleiche Werkzeuge
  3. Für alle sichtbar - PR-Status zeigt Erfolg/Fehlschlag für alle Reviewer
  4. Historische Nachverfolgung - Qualitätstrends über Zeit sehen

Python CI-Beispiel (aus Vorlesung 2):

# .github/workflows/ci.yml
name: Python Quality
on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install uv
        uses: astral-sh/setup-uv@v4
      - name: Run Ruff linter
        run: uv run ruff check src/
      - name: Run Ruff formatter check
        run: uv run ruff format --check src/
      - name: Run Pyright type checker
        run: uv run pyright src/

C++ CI-Beispiel:

# .github/workflows/cpp-ci.yml
name: C++ Quality
on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure CMake
        run: cmake -B build -DCMAKE_BUILD_TYPE=Release

      - name: Build with warnings as errors
        run: cmake --build build -- -j$(nproc)
        # Build schlägt fehl, wenn eine Warnung auftritt (-Werror)

      - name: Run clang-tidy
        run: |
          find src -name '*.cpp' | xargs clang-tidy \
            -p build \
            --warnings-as-errors='*'

      - name: Check formatting
        run: |
          find src -name '*.cpp' -o -name '*.hpp' | xargs clang-format --dry-run --Werror

Das PR-Gate-Muster:

Entwickler pusht Code
        ↓
CI läuft automatisch
        ↓
    ┌───────────────────────────────────┐
    │  ✅ Build erfolgreich             │
    │  ✅ clang-format-Prüfung bestanden│
    │  ✅ clang-tidy-Prüfung bestanden  │
    │  ✅ Alle Tests bestanden          │
    └───────────────────────────────────┘
        ↓
PR kann gemergt werden (grüner Haken)

Wenn eine Prüfung fehlschlägt, ist der PR blockiert, bis der Fehler behoben ist.

6.3 Monitoring und Dashboards

Prüfungen in CI laufen zu lassen ist Schritt eins. Schritt zwei ist, Ergebnisse sichtbar und umsetzbar zu machen.

GitHub Actions: Inline-Annotationen

GitHub Actions kann Warnungen direkt im PR-Diff anzeigen:

- name: Run clang-tidy with annotations
  run: |
    clang-tidy src/*.cpp 2>&1 | \
    sed -E 's/^(.+):([0-9]+):([0-9]+): (warning|error): (.+)/::error file=\1,line=\2,col=\3::\5/'

Das erzeugt Inline-Annotationen:

src/geometry.cpp
Line 42: ⚠️ warning: variable 'x' is not initialized [cppcoreguidelines-init-variables]

SonarQube/SonarCloud: Quality Gates

Für größere Projekte bieten dedizierte Plattformen Dashboards:

Metrik Beschreibung Typischer Schwellwert
Code Coverage Prozentsatz des durch Tests ausgeführten Codes > 80%
Duplizierte Zeilen Copy-Paste-Codeblöcke < 3%
Code Smells Wartbarkeitsprobleme A-Bewertung (beste)
Bugs Wahrscheinliche Laufzeitfehler 0 neue Bugs
Schwachstellen Sicherheitsprobleme 0 neue Schwachstellen

Codecov: Coverage-Tracking

- name: Generate coverage report
  run: |
    cmake --build build --target coverage
    # Generiert lcov.info

- name: Upload to Codecov
  uses: codecov/codecov-action@v4
  with:
    files: build/coverage/lcov.info
    fail_ci_if_error: true

Codecov zeigt:

GitHub-Statusprüfungen: Das Endergebnis

All diese Werkzeuge können Status an GitHub melden:

Pull Request #42: Add intersection calculation
┌────────────────────────────────────────────┐
│ ✅ build (ubuntu-latest)          2m 34s   │
│ ✅ clang-tidy                     1m 12s   │
│ ✅ clang-format                   0m 08s   │
│ ✅ tests (ubuntu-latest)          0m 45s   │
│ ✅ codecov/patch                  80.5%    │
│ ✅ codecov/project                85.2%    │
└────────────────────────────────────────────┘
All checks have passed

Vergleich: Python vs C++ Monitoring

Aspekt Python C++
Linting Ruff (schnell, integriert) clang-tidy (umfassend)
Formatierung Ruff format clang-format
Typprüfung Pyright Compiler (eingebaut)
Coverage pytest-cov gcov / llvm-cov
CI-Integration GitHub Actions + Codecov GitHub Actions + Codecov

6.4 Compiler-Warnungen als Analyse

Der C++-Compiler selbst ist Ihr erster statischer Analysator. Verschiedene Compiler haben unterschiedliche Warnungsoptionen.

6.5 Compiler-Warnungsoptionen

GCC:

# Grundlegende Warnungen (empfohlenes Minimum)
g++ -Wall -Wextra -c main.cpp

# Pedantisch (strikte Standardkonformität)
g++ -Wall -Wextra -Wpedantic -c main.cpp

# Warnungen als Fehler (CI-Konfiguration)
g++ -Wall -Wextra -Wpedantic -Werror -c main.cpp

Clang:

# Empfohlene Clang-Warnungen
clang++ -Wall -Wextra -Wpedantic -c main.cpp

MSVC (Windows):

# Warnungsstufe 4 (höchste empfohlene)
cl /W4 /WX main.cpp

6.6 Zusammenfassung Compiler-Warnungen

Zweck GCC Clang MSVC
Standard-Warnungen -Wall -Wextra -Wall -Wextra /W4
Strikte Standards -Wpedantic -Wpedantic /permissive-
Warnungen als Fehler -Werror -Werror /WX

6.7 CMake-Konfiguration für Compiler-Warnungen

# CMakeLists.txt
if(MSVC)
    add_compile_options(/W4 /WX)
else()
    add_compile_options(-Wall -Wextra -Wpedantic -Werror)
endif()

7. Test-Frameworks für C++

In Python verwenden Sie pytest. In C++ ist das Äquivalent Google Test (gtest). So wie Ruff der De-facto-Python-Linter ist, ist Google Test das De-facto-C++-Test-Framework.

7.1 Wie Google Test sich zu pytest verhält

Betrachte Google Test als “pytest für C++”:

Aspekt Python (pytest) C++ (Google Test)
Zweck Tests ausführen, Fehler melden Tests ausführen, Fehler melden
Installation uv add pytest --dev CMake FetchContent
Konfigurationsdatei pyproject.toml CMakeLists.txt
Tests ausführen pytest ctest
Ausführliche Ausgabe pytest -v ctest --output-on-failure
Spezifischen Test ausführen pytest -k "test_name" ctest -R "test_name"
Maintainer Community Google

Google Test ist:

7.2 Installations-Vergleich

Python: Als Dev-Dependency in pyproject.toml hinzufügen:

uv add pytest --dev

C++: In CMakeLists.txt mit FetchContent hinzufügen:

# CMakeLists.txt
include(FetchContent)
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0
)
# Für Windows: Verhindert Überschreiben der Compiler/Linker-Einstellungen des Elternprojekts
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(tests
    tests/test_geometry.cpp
)
target_link_libraries(tests PRIVATE
    geometry_core
    GTest::gtest_main
)

include(GoogleTest)
gtest_discover_tests(tests)

Wichtige Erkenntnis: Beide verwenden ein “Dependencies deklarieren, Werkzeug lädt sie herunter”-Muster. uv sync lädt pytest herunter; cmake lädt Google Test herunter.

7.3 Teststruktur-Vergleich

Side-by-Side-Vergleich äquivalenter Tests:

Python (tests/test_geometry.py):

import pytest
from road_profile_viewer.geometry import calculate_ray_line

def test_calculate_ray_line_basic():
    """Test basic ray calculation."""
    result = calculate_ray_line(45.0, 0.0, 2.0, 80.0)

    assert len(result["x"]) == 2
    assert len(result["y"]) == 2

def test_calculate_ray_line_horizontal():
    """Test horizontal ray (0 degrees)."""
    result = calculate_ray_line(0.0, 0.0, 2.0, 80.0)

    # Horizontal ray: y stays constant
    assert result["y"][0] == result["y"][1]

C++ (tests/test_geometry.cpp):

#include <gtest/gtest.h>
#include "geometry.hpp"

namespace rpv = road_profile_viewer;

// Test basic ray calculation
TEST(GeometryTest, CalculateRayLineBasic) {
    auto result = rpv::calculate_ray_line(45.0, 0.0, 2.0, 80.0);

    EXPECT_EQ(result.x.size(), 2);
    EXPECT_EQ(result.y.size(), 2);
}

// Test horizontal ray (0 degrees)
TEST(GeometryTest, CalculateRayLineHorizontal) {
    auto result = rpv::calculate_ray_line(0.0, 0.0, 2.0, 80.0);

    // Horizontal ray: y stays constant
    EXPECT_DOUBLE_EQ(result.y[0], result.y[1]);
}

Muster-Mapping:

7.4 Assertions und Expectations

pytest Google Test Verhalten bei Fehlschlag
assert x == y EXPECT_EQ(x, y) Test fortsetzen
assert x == y (kritisch) ASSERT_EQ(x, y) Test sofort stoppen
assert x != y EXPECT_NE(x, y) Test fortsetzen
assert x < y EXPECT_LT(x, y) Test fortsetzen
assert x > y EXPECT_GT(x, y) Test fortsetzen
assert abs(x - y) < 0.001 EXPECT_NEAR(x, y, 0.001) Test fortsetzen
assert x == pytest.approx(y) EXPECT_DOUBLE_EQ(x, y) Test fortsetzen
assert x is None EXPECT_FALSE(x.has_value()) Test fortsetzen
assert x is not None ASSERT_TRUE(x.has_value()) Stoppen wenn None (kann nicht fortsetzen)

EXPECT vs ASSERT - Der zentrale Unterschied:

In pytest stoppt jedes assert den Test bei Fehlschlag. Google Test gibt Ihnen eine Wahl:

TEST(GeometryTest, FindIntersection) {
    auto result = rpv::find_intersection(x_road, y_road, 10.0);

    // ASSERT: Muss bestehen, bevor wir auf result->x zugreifen können
    ASSERT_TRUE(result.has_value());  // Stoppen wenn None

    // EXPECT: Kann mehrere Eigenschaften prüfen
    EXPECT_GT(result->x, 0.0);        // Fortsetzen auch bei Fehlschlag
    EXPECT_GT(result->distance, 0.0); // Läuft trotzdem
}

7.5 Test-Fixtures-Vergleich

Sowohl pytest als auch Google Test unterstützen Fixtures für gemeinsames Setup.

Python (pytest Fixtures):

import pytest
from road_profile_viewer.geometry import find_intersection

@pytest.fixture
def road_profile():
    """Shared test data."""
    return {
        "x": [0, 10, 20, 30, 40],
        "y": [0, 2, 4, 3, 5]
    }

def test_find_intersection_exists(road_profile):
    result = find_intersection(
        road_profile["x"],
        road_profile["y"],
        angle=10.0
    )

    assert result is not None
    assert result["distance"] > 0

def test_find_intersection_no_hit(road_profile):
    # Ray pointing up, never hits road
    result = find_intersection(
        road_profile["x"],
        road_profile["y"],
        angle=-45.0  # Pointing upward
    )

    assert result is None

C++ (Google Test Fixtures):

class GeometryTest : public ::testing::Test {
protected:
    void SetUp() override {
        // Shared test data (like @pytest.fixture)
        x_road = {0, 10, 20, 30, 40};
        y_road = {0, 2, 4, 3, 5};
    }

    // Fixture data available to all tests
    std::vector<double> x_road;
    std::vector<double> y_road;
};

// Use TEST_F (F = Fixture) instead of TEST
TEST_F(GeometryTest, FindIntersectionExists) {
    auto result = rpv::find_intersection(
        x_road, y_road, 10.0, 0.0, 10.0
    );

    ASSERT_TRUE(result.has_value());
    EXPECT_GT(result->distance, 0.0);
}

TEST_F(GeometryTest, FindIntersectionNoHit) {
    // Ray pointing up, never hits road
    auto result = rpv::find_intersection(
        x_road, y_road, -45.0, 0.0, 10.0
    );

    EXPECT_FALSE(result.has_value());
}

Muster-Mapping:

7.6 Parametrisierte Tests-Vergleich

Beide Frameworks unterstützen das Ausführen desselben Tests mit verschiedenen Eingaben.

Python (@pytest.mark.parametrize):

import pytest

@pytest.mark.parametrize("angle,expected_hits", [
    (10.0, True),    # Shallow angle, hits road
    (45.0, True),    # Steep angle, hits road
    (-10.0, False),  # Upward angle, misses
    (90.0, False),   # Vertical, edge case
])
def test_intersection_angles(road_profile, angle, expected_hits):
    result = find_intersection(
        road_profile["x"],
        road_profile["y"],
        angle=angle
    )

    assert (result is not None) == expected_hits

C++ (Google Test INSTANTIATE_TEST_SUITE_P):

struct AngleTestCase {
    double angle;
    bool expected_hits;
};

class AngleTest : public ::testing::TestWithParam<AngleTestCase> {
protected:
    void SetUp() override {
        x_road = {0, 10, 20, 30, 40};
        y_road = {0, 2, 4, 3, 5};
    }
    std::vector<double> x_road;
    std::vector<double> y_road;
};

TEST_P(AngleTest, IntersectionAngles) {
    auto [angle, expected_hits] = GetParam();

    auto result = rpv::find_intersection(
        x_road, y_road, angle, 0.0, 10.0
    );

    EXPECT_EQ(result.has_value(), expected_hits);
}

INSTANTIATE_TEST_SUITE_P(
    GeometryAngles,
    AngleTest,
    ::testing::Values(
        AngleTestCase{10.0, true},    // Shallow angle
        AngleTestCase{45.0, true},    // Steep angle
        AngleTestCase{-10.0, false},  // Upward angle
        AngleTestCase{90.0, false}    // Vertical
    )
);

Wichtige Erkenntnis: Das Konzept ist identisch—dieselbe Testlogik mit verschiedenen Daten ausführen. pytest ist kompakter; Google Test ist expliziter.

7.7 Tests ausführen - Vergleich

Aktion Python (pytest) C++ (ctest/gtest)
Alle Tests ausführen pytest ctest --test-dir build
Ausführliche Ausgabe pytest -v ctest --output-on-failure
Nach Namensmuster ausführen pytest -k "ray" ctest -R "ray"
Spezifische Datei ausführen pytest tests/test_geometry.py ./build/tests --gtest_filter="Geometry*"
Bei erstem Fehlschlag stoppen pytest -x ctest --stop-on-failure
Parallele Ausführung pytest -n auto ctest -j$(nproc)

Build- und Ausführungs-Workflow:

# Python
uv run pytest -v

# C++
cmake --build build
ctest --test-dir build --output-on-failure

# Oder das Test-Executable direkt ausführen für mehr Details
./build/tests

7.8 Test-Ausgabe-Vergleich

pytest-Ausgabe:

========================= test session starts ==========================
collected 4 items

tests/test_geometry.py::test_calculate_ray_line_basic PASSED     [ 25%]
tests/test_geometry.py::test_calculate_ray_line_horizontal PASSED [ 50%]
tests/test_geometry.py::test_find_intersection_exists PASSED      [ 75%]
tests/test_geometry.py::test_find_intersection_no_hit PASSED      [100%]

========================== 4 passed in 0.12s ===========================

Google Test-Ausgabe:

[==========] Running 4 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 4 tests from GeometryTest
[ RUN      ] GeometryTest.CalculateRayLineBasic
[       OK ] GeometryTest.CalculateRayLineBasic (0 ms)
[ RUN      ] GeometryTest.CalculateRayLineHorizontal
[       OK ] GeometryTest.CalculateRayLineHorizontal (0 ms)
[ RUN      ] GeometryTest.FindIntersectionExists
[       OK ] GeometryTest.FindIntersectionExists (0 ms)
[ RUN      ] GeometryTest.FindIntersectionNoHit
[       OK ] GeometryTest.FindIntersectionNoHit (0 ms)
[----------] 4 tests from GeometryTest (0 ms total)

[==========] 4 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 4 tests.

Das Ausgabeformat unterscheidet sich, aber die Information ist dieselbe: Welche Tests liefen, welche bestanden, welche fehlschlugen, und wie lange es dauerte.


8. Coverage-Reports für C++

In Python verwenden Sie pytest-cov, um die Testabdeckung zu messen. In C++ sind die entsprechenden Werkzeuge gcov (GCC) und llvm-cov (Clang). Der Arbeitsablauf ist derselbe: Code instrumentieren, Tests ausführen, Report generieren.

8.1 Wie C++-Coverage mit pytest-cov zusammenhängt

Aspekt Python (pytest-cov) C++ (gcov/llvm-cov)
Installation uv add pytest-cov --dev In GCC/Clang eingebaut
Coverage aktivieren pytest --cov=src CMake: -DENABLE_COVERAGE=ON
Mit Coverage ausführen pytest --cov=src ctest (nach Coverage-Build)
Terminal-Report --cov-report=term gcov oder llvm-cov report
HTML-Report --cov-report=html genhtml (lcov) oder llvm-cov show
CI-Upload Codecov Action Codecov Action (dieselbe!)

8.2 Coverage-Workflow-Vergleich

Python-Workflow:

# Install
uv add pytest-cov --dev

# Run tests with coverage
uv run pytest --cov=src --cov-report=term --cov-report=html

# View HTML report
open htmlcov/index.html

C++-Workflow:

# Configure with coverage enabled
cmake -B build -DENABLE_COVERAGE=ON

# Build
cmake --build build

# Run tests (this generates coverage data)
ctest --test-dir build

# Generate HTML report
lcov --capture --directory build --output-file coverage.info
genhtml coverage.info --output-directory htmlcov

# View HTML report
open htmlcov/index.html

Wichtige Erkenntnis: Die Schritte sind konzeptionell identisch:

  1. Coverage-Instrumentierung aktivieren
  2. Tests ausführen
  3. Menschenlesbaren Report generieren

8.3 CMake-Coverage-Konfiguration

Fügen Sie dies zu Ihrer CMakeLists.txt hinzu:

# CMakeLists.txt
option(ENABLE_COVERAGE "Enable code coverage" OFF)

if(ENABLE_COVERAGE)
    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
        # Add coverage flags (like pytest --cov)
        add_compile_options(--coverage -O0 -g)
        add_link_options(--coverage)
    endif()
endif()

Vergleich mit pyproject.toml:

# pyproject.toml (Python)
[tool.pytest.ini_options]
addopts = "--cov=src --cov-report=term"

Beide konfigurieren Coverage in der Build-Konfigurationsdatei des Projekts.

8.4 gcov (GCC) vs llvm-cov (Clang)

Aspekt gcov (GCC) llvm-cov (Clang)
Compiler GCC (g++) Clang (clang++)
Coverage-Flags --coverage -fprofile-instr-generate -fcoverage-mapping
Rohdaten-Format .gcda, .gcno .profraw
HTML-Generator lcov + genhtml llvm-cov show --format=html
CI-Kompatibilität Codecov, Coveralls Codecov, Coveralls

gcov-Workflow (häufiger unter Linux):

# Build with GCC coverage
cmake -B build -DCMAKE_CXX_COMPILER=g++ -DENABLE_COVERAGE=ON
cmake --build build

# Run tests
ctest --test-dir build

# Generate lcov report
lcov --capture --directory build --output-file coverage.info
lcov --remove coverage.info '/usr/*' '*/tests/*' --output-file coverage.info

# Generate HTML
genhtml coverage.info --output-directory htmlcov

llvm-cov-Workflow (bessere Source-Annotation):

# Build with Clang coverage
cmake -B build -DCMAKE_CXX_COMPILER=clang++ -DENABLE_COVERAGE=ON
cmake --build build

# Run tests (generates .profraw)
ctest --test-dir build

# Merge profile data
llvm-profdata merge -sparse default.profraw -o coverage.profdata

# Generate report
llvm-cov show ./build/tests -instr-profile=coverage.profdata --format=html > htmlcov/index.html

8.5 Coverage-Report-Vergleich

pytest-cov Terminal-Ausgabe:

---------- coverage: platform linux, python 3.12.0 ----------
Name                              Stmts   Miss  Cover
-----------------------------------------------------
src/road_profile_viewer/__init__.py     2      0   100%
src/road_profile_viewer/geometry.py    45      3    93%
src/road_profile_viewer/visualization.py   78     12    85%
-----------------------------------------------------
TOTAL                               125     15    88%

gcov/lcov Terminal-Ausgabe:

Reading tracefile coverage.info
Summary coverage rate:
  lines......: 88.0% (110 of 125 lines)
  functions..: 100.0% (12 of 12 functions)
  branches...: 75.0% (30 of 40 branches)

Beide zeigen dieselbe Schlüsselmetrik: Welcher Prozentsatz deines Codes wurde von Tests ausgeführt.

8.6 CI-Integration-Vergleich

Python CI (.github/workflows/python-ci.yml):

- name: Run tests with coverage
  run: uv run pytest --cov=src --cov-report=xml

- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v4
  with:
    files: coverage.xml
    fail_ci_if_error: true

C++ CI (.github/workflows/cpp-ci.yml):

- name: Configure with coverage
  run: cmake -B build -DENABLE_COVERAGE=ON

- name: Build
  run: cmake --build build

- name: Run tests
  run: ctest --test-dir build

- name: Generate coverage report
  run: |
    lcov --capture --directory build --output-file coverage.info
    lcov --remove coverage.info '/usr/*' '*/tests/*' -o coverage.info

- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v4
  with:
    files: coverage.info
    fail_ci_if_error: true

Wichtige Erkenntnis: Beide laden zum selben Codecov-Service hoch. Ihre Python- und C++-Coverage-Reports erscheinen auf demselben Dashboard, was es einfach macht, die gesamte Projektgesundheit zu verfolgen.

8.7 Coverage-Schwellenwerte

Python (in pyproject.toml oder pytest.ini):

[tool.coverage.report]
fail_under = 80

C++ (im CI-Workflow):

- name: Check coverage threshold
  run: |
    COVERAGE=$(lcov --summary coverage.info | grep "lines" | grep -oP '\d+\.\d+')
    if (( $(echo "$COVERAGE < 80" | bc -l) )); then
      echo "Coverage $COVERAGE% is below 80% threshold"
      exit 1
    fi

Oder verwende den eingebauten Schwellenwert von Codecov:

# codecov.yml (same for Python and C++)
coverage:
  status:
    project:
      default:
        target: 80%
        threshold: 1%

8.8 Zusammenfassung des Coverage-Workflows

Coverage: Gleiches Konzept, unterschiedliche Werkzeuge
Python
uv add pytest-cov
pytest --cov
coverage.xml
📊
Codecov
C++
cmake -DCOVERAGE=ON
ctest
lcov→ coverage.info

9. Vergleich: Python vs. C++ Quality-Tools

Nachdem wir nun Code-Qualität, Testing und Coverage für beide Ökosysteme behandelt haben, fassen wir alles zusammen. Dieser umfassende Vergleich zeigt, wie sich die Disziplin, die Sie in Python gelernt haben, direkt auf C++ übertragen lässt.

9.1 Werkzeug-Vergleich

Funktion Python C++
Style Guide PEP 8 Google C++ Style Guide
Formatter Ruff format, Black clang-format
Linter Ruff check, Pylint clang-tidy
Type Checker Pyright, mypy Compiler (eingebaut)
Config-Datei pyproject.toml .clang-format + .clang-tidy
Import-Sortierung isort (Teil von Ruff) clang-format IncludeCategories
Pre-Commit-Hook ruff check --fix && ruff format clang-format -i && clang-tidy

9.2 Workflow-Vergleich

Workflow-Schritt Python C++
Alle Dateien formatieren ruff format . find . -name '*.cpp' | xargs clang-format -i
Formatierung prüfen (CI) ruff format --check . clang-format --dry-run --Werror
Linter ausführen ruff check . clang-tidy -p build src/*.cpp
Linter-Probleme beheben ruff check --fix . clang-tidy -p build --fix src/*.cpp
Type-Check pyright . (Der Compiler macht das automatisch)
IDE-Integration VS Code Python Extension VS Code C/C++ Extension, clangd

9.3 Konfigurations-Philosophie

Aspekt Python C++
Konfigurations-Ort Eine Datei: pyproject.toml Separate Dateien pro Werkzeug
Konfigurations-Format TOML YAML (clang-Tools)
Basis-Style PEP 8 (impliziter Standard) BasedOnStyle: Google (explizit)
Regel-Auswahl select = ["E", "W", "F"] Checks: bugprone-*, performance-*
Regeln deaktivieren ignore = ["E501"] -readability-magic-numbers

9.4 CI/CD-Integration

CI-Schritt Python (GitHub Actions) C++ (GitHub Actions)
Setup uses: astral-sh/setup-uv@v4 sudo apt install clang-format clang-tidy
Format-Check uv run ruff format --check . clang-format --dry-run --Werror src/*.cpp
Lint-Check uv run ruff check . clang-tidy -p build src/*.cpp
Type-Check uv run pyright (Teil von cmake --build)
Bei Fehler beenden Automatisch (non-zero exit) --warnings-as-errors='*'

9.5 Testing-Vergleich

Aspekt Python (pytest) C++ (Google Test)
Installieren uv add pytest --dev CMake FetchContent
Tests ausführen pytest ctest --test-dir build
Verbose pytest -v ctest --output-on-failure
Tests filtern pytest -k "ray" ctest -R "ray"
Assertions assert x == y EXPECT_EQ(x, y)
Fixtures @pytest.fixture class : public ::testing::Test
Parametrisieren @pytest.mark.parametrize INSTANTIATE_TEST_SUITE_P

9.6 Coverage-Vergleich

Aspekt Python (pytest-cov) C++ (gcov/llvm-cov)
Installieren uv add pytest-cov --dev In GCC/Clang eingebaut
Aktivieren pytest --cov=src cmake -DENABLE_COVERAGE=ON
HTML-Report --cov-report=html genhtml coverage.info
CI-Upload codecov/codecov-action@v4 codecov/codecov-action@v4
Schwellenwert fail_under = 80 codecov.yml: target: 80%

9.7 Die wichtigste Erkenntnis

┌─────────────────────────────────────────────────────────────────────┐
│                    DIESELBE DISZIPLIN, ANDERE WERKZEUGE             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  CODE-QUALITÄT:                                                     │
│  Python:   pyproject.toml  →  Ruff format  →  Ruff check  →  CI     │
│  C++:      .clang-format   →  clang-format →  clang-tidy  →  CI     │
│                                                                     │
│  TESTING:                                                           │
│  Python:   pytest          →  assert x == y  →  @pytest.fixture     │
│  C++:      ctest           →  EXPECT_EQ(x,y) →  ::testing::Test     │
│                                                                     │
│  COVERAGE:                                                          │
│  Python:   pytest --cov    →  htmlcov/       →  Codecov             │
│  C++:      gcov/llvm-cov   →  genhtml        →  Codecov             │
│                                                                     │
│  Die Werkzeuge sind unterschiedlich, aber der Workflow ist          │
│  identisch:                                                         │
│  1. Einmal im Projekt-Root konfigurieren                            │
│  2. Automatisch ausführen (pre-commit, CI)                          │
│  3. Konsistenter, hochwertiger, gut getesteter Code                 │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Was Sie in Python gelernt haben, überträgt sich direkt auf C++. Dasselbe mentale Modell — Werkzeuge konfigurieren, automatisch durchsetzen, gründlich testen, Coverage messen — gilt in beiden Ökosystemen. Sie lernen nicht etwas Neues; Sie wenden vertraute Konzepte mit anderer Syntax an.


10. Zusammenfassung

In dieser Vorlesung haben Sie gelernt, wie Sie dieselbe Qualitätsdisziplin auf C++ anwenden, die Sie bereits für Python verwenden:

10.1 Was Sie gelernt haben

Code-Formatierung:

Statische Analyse:

Unit-Testing:

Coverage:

Wichtige Erkenntnis:

Die Werkzeuge sind unterschiedlich, aber die Disziplin ist identisch:
Konfigurieren → Durchsetzen → Testen → Messen → Wiederholen

11. Reflexionsfragen

Bevor Sie zu Teil 3 übergehen, überlegen Sie sich diese Fragen:

  1. Konfiguration: Wie würden Sie clang-format einrichten, um den bestehenden Code-Style Ihres Teams zu entsprechen?

  2. Warnungen als Fehler: Was sind die Vor- und Nachteile der Verwendung von -Werror in Produktions- vs. Entwicklungs-Builds?

  3. Test-Design: Wann würden Sie EXPECT_* vs. ASSERT_* in Google Test verwenden? Geben Sie ein Beispiel für jeden.

  4. Coverage-Lücken: Wenn eine Funktion 100% Zeilenabdeckung hat, aber immer noch einen Bug enthält, was könnte fehlen? Wie hilft Branch-Coverage?

  5. Werkzeug-Vergleich: Sie treten einem neuen Team bei, das C++ verwendet. Wie würden Sie den Zweck von clang-tidy einem Python-Entwickler erklären, der Ruff kennt?


12. Was kommt als Nächstes

Teil 3: Praktische Implementierung wird Ihnen zeigen, wie Sie Python und C++ in einem einzigen Projekt kombinieren:

Am Ende von Teil 3 wird Ihr Road Profile Viewer C++-Implementierungen haben, die:

┌─────────────────────────────────────────────────────────────────────┐
│                         WAS IN TEIL 3 KOMMT                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Python Code                                      C++ Code          │
│  ┌────────────────────┐                    ┌────────────────────┐   │
│  │ calculate_ray_line │                    │ calculate_ray_line │   │
│  │ find_intersection  │ ──  pybind11 ──>   │ find_intersection  │   │
│  │ (easy to read)     │                    │ (fast to run)      │   │
│  └────────────────────┘                    └────────────────────┘   │
│                                                                     │
│  Ihre Python-App ruft C++-Funktionen auf, als wären sie natives     │
│  Python                                                             │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
© 2026 Dominik Mueller   •  Powered by Soopr   •  Theme  Moonwalk