Skip to content

Latest commit

 

History

History
588 lines (433 loc) · 26.8 KB

File metadata and controls

588 lines (433 loc) · 26.8 KB

🔝 Retour au Sommaire

47.3.2 — Intégration clang-format

Chapitre 47 : Collaboration et Maintenance · Section 47.3 : Configuration pre-commit
Niveau : Expert
Prérequis : Section 47.3.1 (.pre-commit-config.yaml), section 32.3 (clang-format — formatage automatique)


Introduction

La sous-section 47.3.1 a intégré clang-format dans le fichier .pre-commit-config.yaml avec quelques lignes de configuration. Mais derrière ces lignes se cache un outil d'une profondeur considérable : clang-format expose plus de 150 options de style, et un .clang-format mal conçu peut produire des résultats surprenants sur du code C++ moderne (concepts, ranges, structured bindings, lambdas imbriquées).

Cette sous-section couvre tout ce qu'il faut savoir pour que l'intégration de clang-format dans les pre-commit hooks soit robuste, déterministe et acceptée par l'équipe : la construction du fichier .clang-format, la gestion des cas particuliers, les mécanismes d'échappement, et les stratégies pour introduire le formatage sur un projet existant sans provoquer de révolte.


Le fichier .clang-format

Le fichier .clang-format est un fichier YAML placé à la racine du projet. clang-format remonte l'arborescence à partir du fichier source pour trouver le .clang-format le plus proche. C'est pourquoi un seul fichier à la racine suffit dans la majorité des cas.

Partir d'un style de base

Plutôt que de configurer les 150+ options individuellement, clang-format propose des styles de base prédéfinis. Le choix du style de base est la décision la plus structurante :

Style de base Caractéristiques Utilisé par
LLVM Indentation 2, accolades attachées, colonnes 80 Projets LLVM/Clang
Google Indentation 2, accolades attachées, colonnes 80 Google (C++, Java, etc.)
Chromium Proche de Google, quelques variations Projet Chromium
Mozilla Indentation 2, accolades Allman pour fonctions Firefox
WebKit Indentation 4, accolades attachées WebKit/Safari
Microsoft Indentation 4, accolades Allman Projets Microsoft
GNU Indentation 2, accolades GNU (indentées) Projets GNU

Le style de base définit les valeurs par défaut de toutes les options. Vous ne surchargez ensuite que ce qui diffère de vos conventions :

# .clang-format
---
BasedOnStyle: LLVM
# ... surcharges ci-dessous

Recommandation pragmatique : choisissez le style de base le plus proche de votre code existant. Un reformatage massif est mieux accepté s'il ne bouleverse que quelques aspects du style plutôt que l'intégralité. Si le projet n'a pas de style établi, LLVM et Google sont les deux choix les plus courants dans l'écosystème C++.

Configuration de référence pour un projet C++ moderne

Voici un .clang-format complet, commenté, adapté au C++ moderne (C++20/23/26) et au travail en équipe. Il est conçu comme point de départ — chaque option est expliquée pour faciliter les ajustements :

# .clang-format
# ═══════════════════════════════════════════════════════════════
# Configuration clang-format — Projet C++ moderne (C++20/23/26)
# Requiert : clang-format 19+
# ═══════════════════════════════════════════════════════════════
---
Language: Cpp  
BasedOnStyle: LLVM  

# ── Indentation ──────────────────────────────────────────────
IndentWidth: 4  
TabWidth: 4  
UseTab: Never  
IndentCaseLabels: true  
IndentPPDirectives: BeforeHash  
NamespaceIndentation: None  
IndentRequiresClause: true           # C++20 requires  
IndentExternBlock: NoIndent  

# ── Largeur et wrapping ─────────────────────────────────────
ColumnLimit: 100  
ContinuationIndentWidth: 4  

# ── Accolades ────────────────────────────────────────────────
BreakBeforeBraces: Custom  
BraceWrapping:  
  AfterCaseLabel: false
  AfterClass: false
  AfterControlStatement: Never
  AfterEnum: false
  AfterFunction: false
  AfterNamespace: false
  AfterStruct: false
  AfterUnion: false
  AfterExternBlock: false
  BeforeCatch: false
  BeforeElse: false
  BeforeLambdaBody: false
  BeforeWhile: false
  IndentBraces: false
  SplitEmptyFunction: false
  SplitEmptyRecord: false
  SplitEmptyNamespace: false

# ── Espacement ───────────────────────────────────────────────
SpaceAfterCStyleCast: false  
SpaceAfterLogicalNot: false  
SpaceAfterTemplateKeyword: true  
SpaceBeforeAssignmentOperators: true  
SpaceBeforeCpp11BracedList: false  
SpaceBeforeCtorInitializerColon: true  
SpaceBeforeInheritanceColon: true  
SpaceBeforeParens: ControlStatements  
SpaceBeforeRangeBasedForLoopColon: true  
SpaceBeforeSquareBrackets: false  
SpaceInEmptyBlock: false  
SpacesInAngles: Never  
SpacesInParens: Never  

# ── Alignement ───────────────────────────────────────────────
AlignAfterOpenBracket: Align  
AlignConsecutiveAssignments: None  
AlignConsecutiveDeclarations: None  
AlignConsecutiveMacros: Consecutive  
AlignEscapedNewlines: Left  
AlignOperands: Align  
AlignTrailingComments:  
  Kind: Always
  OverEmptyLines: 1

# ── Arguments et paramètres ──────────────────────────────────
AllowAllArgumentsOnNextLine: true  
AllowAllParametersOfDeclarationOnNextLine: true  
BinPackArguments: true  
BinPackParameters: true  
AllowShortBlocksOnASingleLine: Empty  
AllowShortCaseLabelsOnASingleLine: false  
AllowShortEnumsOnASingleLine: true  
AllowShortFunctionsOnASingleLine: Inline  
AllowShortIfStatementsOnASingleLine: Never  
AllowShortLambdasOnASingleLine: Inline  
AllowShortLoopsOnASingleLine: false  

# ── Includes ─────────────────────────────────────────────────
SortIncludes: CaseSensitive  
IncludeBlocks: Regroup  
IncludeCategories:  
  # 1. Header associé (convention : même nom que le .cpp)
  - Regex: '^"[^/]*\.h(pp)?"'
    Priority: 1
    SortPriority: 1
  # 2. Headers du projet
  - Regex: '^"'
    Priority: 2
    SortPriority: 2
  # 3. Headers tiers (entre < >)
  - Regex: '^<[a-z]'
    Priority: 3
    SortPriority: 3
  # 4. Headers système / STL
  - Regex: '^<'
    Priority: 4
    SortPriority: 4

# ── C++ moderne ──────────────────────────────────────────────
Standard: Latest  
RequiresClausePosition: OwnLine      # C++20 requires sur sa propre ligne  
RequiresExpressionIndentation: OuterScope  
BreakBeforeConceptDeclarations: Always  
LambdaBodyIndentation: Signature  
PackConstructorInitializers: NextLine  
InsertBraces: false                   # Ne pas insérer automatiquement  
InsertNewlineAtEOF: true  
SeparateDefinitionBlocks: Leave  
EmptyLineAfterAccessModifier: Never  
EmptyLineBeforeAccessModifier: LogicalBlock  
IntegerLiteralSeparator:  
  Binary: 4
  Decimal: 3
  Hex: 2

# ── Pénalités (contrôle du line-breaking) ────────────────────
PenaltyBreakAssignment: 25  
PenaltyBreakBeforeFirstCallParameter: 19  
PenaltyBreakComment: 300  
PenaltyBreakFirstLessLess: 120  
PenaltyBreakString: 1000  
PenaltyExcessCharacter: 1000000  
PenaltyReturnTypeOnItsOwnLine: 200  
PenaltyBreakOpenParenthesis: 0  
PenaltyBreakTemplateDeclaration: 10  

# ── Divers ───────────────────────────────────────────────────
AccessModifierOffset: -4  
CompactNamespaces: false  
Cpp11BracedListStyle: true  
DerivePointerAlignment: false  
PointerAlignment: Left               # int* ptr (pas int *ptr ni int * ptr)  
ReferenceAlignment: Pointer  
FixNamespaceComments: true  
MaxEmptyLinesToKeep: 1  
ReflowComments: true  
QualifierAlignment: Leave            # Ne pas réordonner const/volatile  
RemoveSemicolon: false  
...

Options clés expliquées

ColumnLimit: 100 — Le choix de la largeur de ligne est probablement la décision la plus débattue. 80 colonnes est le défaut historique ; 100-120 est devenu le standard de facto pour le C++ moderne, où les noms qualifiés (std::unordered_map<std::string, std::vector<int>>) et les signatures avec concepts consomment beaucoup de caractères. À 80 colonnes, le wrapping excessif nuit à la lisibilité. À 120+, les diffs côte à côte deviennent difficiles à lire. 100 est un compromis éprouvé.

PointerAlignment: Leftint* ptr plutôt que int *ptr. C'est le style prédominant en C++ moderne (cohérent avec la pensée "le type est int*"), même si le C et certains styles anciens préfèrent l'alignement à droite (cohérent avec la grammaire du langage). L'important est la cohérence — le choix importe moins que l'uniformité.

SortIncludes: CaseSensitive et IncludeCategories — Le tri automatique des #include est l'une des fonctionnalités les plus utiles de clang-format pour la collaboration. Sans tri, les includes s'accumulent dans un ordre aléatoire qui varie d'un développeur à l'autre, provoquant des conflits de merge artificiels. Le regroupement par catégories (header associé, projet, tiers, système) améliore la lisibilité et rend les dépendances explicites.

L'ordre header associé en premier est une convention de Google (et de nombreux projets) qui a un bénéfice technique : si connection_pool.h oublie un include nécessaire, connection_pool.cpp échouera à la compilation car le header est inclus avant tout autre. Cela force les headers à être self-contained.

RequiresClausePosition: OwnLine — Place les clauses requires C++20 sur leur propre ligne. C'est le style recommandé pour la lisibilité, surtout avec des contraintes complexes :

// Avec OwnLine
template<typename T>
    requires std::integral<T> && std::is_signed_v<T>
auto process(T value) -> T;

// Sans (attaché) — difficile à lire avec des contraintes longues
template<typename T> requires std::integral<T> && std::is_signed_v<T>  
auto process(T value) -> T;  

BreakBeforeConceptDeclarations: Always — Sépare visuellement les déclarations de concepts du code environnant, ce qui améliore la navigation dans les fichiers contenant plusieurs concepts.

IntegerLiteralSeparator — Active les séparateurs dans les littéraux numériques pour la lisibilité. clang-format 19+ peut insérer automatiquement des ' (digit separators C++14) dans les constantes : 1'000'000 plutôt que 1000000, 0xFF'FF plutôt que 0xFFFF. C'est une fonctionnalité optionnelle — retirez ce bloc si vous ne souhaitez pas de réécriture des littéraux.

QualifierAlignment: Leave — Ne réordonne pas les qualificateurs (const, volatile). Le débat const int vs int const (east const vs west const) est interminable. Leave respecte le choix du développeur. Si votre équipe a une convention, utilisez Left (west const : const int) ou Right (east const : int const).

Section Pénalités — Les pénalités contrôlent les décisions de clang-format quand une ligne dépasse la limite de colonnes. Chaque option assigne un "coût" à un type de coupure. clang-format choisit la coupure avec le coût total minimal. Les valeurs ci-dessus favorisent la coupure après les parenthèses ouvrantes et avant les déclarations de templates, tout en évitant de couper les chaînes de caractères et les commentaires. Ajuster ces pénalités est rarement nécessaire au début, mais c'est le levier pour corriger les cas de wrapping aberrant que vous rencontrerez à l'usage.


Gestion du tri des includes

Le tri automatique des #include par clang-format est puissant mais nécessite de la vigilance dans certains cas.

Quand le tri est bénéfique

Dans la grande majorité des fichiers, le tri des includes est sans effet secondaire et très bénéfique :

// Avant (ordre aléatoire, dépendances masquées)
#include <vector>
#include "config.h"
#include <string>
#include "network/connection_pool.h"
#include <memory>
#include "utils/logger.h"

// Après clang-format (regroupé et trié)
#include "network/connection_pool.h"  // Header associé (priorité 1)

#include "config.h"                    // Headers projet (priorité 2)
#include "utils/logger.h"

#include <memory>                      // Headers STL (priorité 4)
#include <string>
#include <vector>

Quand le tri peut casser la compilation

Dans de rares cas, l'ordre des includes est sémantiquement significatif. Cela arrive quand un header dépend d'un #define défini avant l'include, ou quand un header pollue le namespace et doit être inclus après les autres :

// L'ordre est intentionnel ici — NE PAS TRIER
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include <spdlog/spdlog.h>

// Ou dans du code legacy / interop C
#include <sys/types.h>    // Doit être avant sys/socket.h sur certains systèmes
#include <sys/socket.h>

Pour ces cas, clang-format fournit un mécanisme de protection :

// clang-format off
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include <spdlog/spdlog.h>
// clang-format on

#include "my_module.h"    // Le tri reprend normalement ici

#include <string>
#include <vector>

Alternativement, un commentaire vide entre deux blocs d'includes empêche clang-format de les fusionner et de les trier ensemble :

#include <sys/types.h>
#include <sys/socket.h>

// ↑ Ce commentaire (ou une ligne vide) crée une séparation
// clang-format ne trie pas entre les deux groupes

#include <string>
#include <vector>

Mécanismes d'échappement : clang-format off/on

Aucun outil de formatage n'est parfait. Il existe des cas où clang-format produit un résultat moins lisible que le formatage manuel. Les directives // clang-format off et // clang-format on permettent de désactiver ponctuellement le formatage :

Cas d'usage légitimes

Tableaux de données alignés manuellement — Le formatage tabulaire est souvent plus lisible que le wrapping automatique :

// clang-format off
constexpr std::array<KeyMapping, 6> key_mappings = {{
    { Key::Up,     Action::MoveUp,      "Move up"      },
    { Key::Down,   Action::MoveDown,    "Move down"    },
    { Key::Left,   Action::MoveLeft,    "Move left"    },
    { Key::Right,  Action::MoveRight,   "Move right"   },
    { Key::Space,  Action::Jump,        "Jump"         },
    { Key::Escape, Action::OpenMenu,    "Open menu"    },
}};
// clang-format on

Sans les directives, clang-format casserait l'alignement des colonnes.

Macros préprocesseur complexesclang-format ne comprend pas la sémantique des macros et peut mal formater les continuations \ :

// clang-format off
#define REGISTER_TEST(name, func, timeout)   \
    static auto test_##name = TestRegistry   \
        ::instance()                         \
        .add(#name, func, timeout)
// clang-format on

DSL (Domain-Specific Languages) embarqués — Certaines bibliothèques utilisent des macros ou des operator overloads pour créer des mini-DSL dont le formatage intentionnel est significatif :

// clang-format off
auto pipeline = source
    | views::filter([](auto x) { return x > 0; })
    | views::transform([](auto x) { return x * 2; })
    | views::take(10);
// clang-format on

Cet exemple est de moins en moins nécessaire avec les versions récentes de clang-format (18+) qui gèrent mieux le formatage des pipelines ranges C++20.

Règles d'usage

L'abus de // clang-format off est un signal d'alarme. Si plus de 5 % des fichiers contiennent des désactivations, c'est un indicateur que le .clang-format est mal configuré ou que le style choisi est trop éloigné des conventions naturelles de l'équipe. Ajustez la configuration plutôt que de multiplier les échappements.

Quelques principes pour une utilisation saine :

  • Toujours refermer : chaque // clang-format off doit avoir son // clang-format on. Un off sans on désactive le formatage jusqu'à la fin du fichier.
  • Scope minimal : n'encadrez que les lignes qui en ont besoin, pas des fonctions entières.
  • Documenter la raison : un commentaire sur la même ligne que le off aide les futurs lecteurs :
// clang-format off — alignement tabulaire intentionnel
constexpr auto table = ...;
// clang-format on

Gestion des attributs et annotations C++

Le C++ moderne utilise de plus en plus d'attributs ([[nodiscard]], [[maybe_unused]], [[deprecated("...")]]). clang-format les gère bien dans la plupart des cas, mais quelques options méritent attention :

AttributeMacros — Si votre projet définit des macros qui se comportent comme des attributs (annotation de visibilité, export DLL, instrumentation), déclarez-les pour que clang-format les traite correctement :

# Dans .clang-format
AttributeMacros:
  - MY_EXPORT
  - MY_DEPRECATED
  - BENCHMARK_ATTR

Sans cette déclaration, clang-format peut confondre ces macros avec des appels de fonction et insérer des espaces ou des retours à la ligne inappropriés.

StatementAttributeLikeMacros et TypenameMacros — Même logique pour les macros qui simulent des statements ou des noms de type :

StatementAttributeLikeMacros:
  - Q_EMIT
  - Q_SIGNAL
  - Q_SLOT

TypenameMacros:
  - STACK_OF
  - LIST_ENTRY

Ces déclarations sont surtout nécessaires dans les projets utilisant Qt, OpenSSL ou d'autres bibliothèques C à forte densité de macros.


Interaction avec l'éditeur et clangd

Le fichier .clang-format est utilisé non seulement par le hook pre-commit, mais aussi par l'éditeur en temps réel. La plupart des IDE et éditeurs modernes appliquent clang-format au moment de la sauvegarde ou du formatage manuel.

VS Code

L'extension C/C++ (ms-vscode.cpptools) ou clangd utilise automatiquement le .clang-format du projet. Activez le formatage à la sauvegarde dans les settings du projet :

// .vscode/settings.json
{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd",
    "C_Cpp.formatting": "clangFormat",
    "C_Cpp.clang_format_style": "file"
}

Avec cette configuration, le développeur n'a même plus besoin du hook pre-commit pour le formatage — le fichier est formaté à chaque sauvegarde. Le hook pre-commit reste utile comme filet de sécurité (l'éditeur peut être mal configuré, ou le développeur peut utiliser un autre éditeur).

CLion

CLion intègre clang-format nativement. Activez-le dans Settings → Editor → Code Style → C/C++ → Enable ClangFormat. CLion détecte automatiquement le .clang-format à la racine du projet.

Vim/Neovim

Avec le plugin vim-clang-format ou via clangd (LSP natif) :

" .vimrc ou init.vim
let g:clang_format#detect_style_file = 1  
autocmd BufWritePre *.cpp,*.h,*.hpp :ClangFormat  

L'effet vertueux

Quand l'éditeur formate à la sauvegarde et que le hook pre-commit vérifie le formatage, le développeur n'est pratiquement jamais bloqué par le hook. Le formatage devient invisible — il se produit automatiquement, en continu, sans intervention consciente. C'est l'état souhaité : la conformité de style ne coûte zéro effort mental.


Vérification sans correction (mode CI)

En CI, on ne veut pas que clang-format modifie les fichiers — on veut qu'il vérifie que le formatage est correct et échoue si ce n'est pas le cas. L'approche diffère selon la méthode :

Via pre-commit en CI

# CI pipeline
pre-commit run clang-format --all-files

Le hook pre-commit utilise -i (in-place) par défaut. En CI, cela signifie que si un fichier est mal formaté, le hook le modifie et retourne un code d'erreur. Le pipeline échoue, ce qui est le comportement souhaité. Les fichiers modifiés ne sont pas commités (l'environnement CI est éphémère).

Via clang-format directement

Pour une vérification sans modification dans un script CI personnalisé :

# Vérifie que tous les fichiers sont correctement formatés
# --dry-run : n'écrit pas les modifications
# --Werror : retourne un code d'erreur si une modification serait nécessaire
find src/ include/ -name '*.cpp' -o -name '*.hpp' -o -name '*.h' \
    | xargs clang-format --dry-run --Werror --style=file

Afficher le diff des corrections nécessaires

Pour aider le développeur à comprendre ce qui doit être corrigé :

# Affiche le diff entre le fichier actuel et le fichier formaté
find src/ include/ -name '*.cpp' -o -name '*.hpp' -o -name '*.h' \
    | xargs -I{} bash -c 'diff <(cat "{}") <(clang-format --style=file "{}") && true || echo "→ {}"'

Évolution du .clang-format dans le temps

Ajouter de nouvelles options

Quand une nouvelle version de clang-format introduit une option utile, ajoutez-la au .clang-format dans un commit dédié :

# 1. Ajouter l'option
vim .clang-format

# 2. Reformater tout le projet avec la nouvelle option
clang-format -i $(find src/ include/ -name '*.cpp' -o -name '*.hpp' -o -name '*.h')

# 3. Commiter le changement de config + le reformatage ensemble
git add .clang-format src/ include/  
git commit -m "style: enable IntegerLiteralSeparator in clang-format  

Adds digit separators to integer literals for readability.  
Requires clang-format 19+."  

# 4. Ajouter ce commit à .git-blame-ignore-revs
echo "" >> .git-blame-ignore-revs  
echo "# Enable IntegerLiteralSeparator" >> .git-blame-ignore-revs  
echo "$(git rev-parse HEAD)" >> .git-blame-ignore-revs  
git add .git-blame-ignore-revs  
git commit -m "chore: update git-blame-ignore-revs"  

Montée de version de clang-format

Quand l'équipe passe d'une version majeure à une autre (par exemple de 18 à 19), le formatage de certains fichiers peut changer même sans modification du .clang-format. Procédez de la même manière : un commit de reformatage dédié + ajout à .git-blame-ignore-revs.

Testez toujours la nouvelle version sur l'ensemble du projet avant de la déployer :

# Comparer le formatage entre deux versions
clang-format-18 --style=file src/core/engine.cpp > /tmp/v18.cpp  
clang-format-19 --style=file src/core/engine.cpp > /tmp/v19.cpp  
diff /tmp/v18.cpp /tmp/v19.cpp  

Si les différences sont acceptables, procédez à la migration. Si certains changements sont indésirables, ajoutez des options explicites au .clang-format pour préserver le comportement précédent (les nouvelles versions ajoutent des options, elles ne suppriment pas les anciennes).


Générer un .clang-format à partir du code existant

Si votre projet a un style existant non formalisé, clang-format peut générer un .clang-format qui s'en approche :

# Demander à clang-format de deviner le style d'un fichier
clang-format --dump-config --style=file src/core/engine.cpp > .clang-format.draft

Si aucun .clang-format n'existe encore, la commande suivante génère un fichier basé sur un style prédéfini :

# Générer le .clang-format pour le style Google
clang-format --dump-config --style=Google > .clang-format

Cela produit un fichier exhaustif avec toutes les options explicitées. Vous pouvez ensuite le simplifier en ne gardant que les options qui diffèrent du BasedOnStyle choisi.

Un outil interactif utile est le Clang-Format Configurator — une interface web qui permet de prévisualiser l'effet de chaque option sur un extrait de code. C'est particulièrement utile pour les options de pénalité et de wrapping, dont l'effet n'est pas toujours intuitif à partir de la documentation seule.


Erreurs fréquentes et solutions

"Unknown key 'RequiresClausePosition'" (ou toute option non reconnue)

La version de clang-format installée est trop ancienne pour l'option utilisée. Chaque option a une version minimale requise :

Option Version minimale
RequiresClausePosition 15
RequiresExpressionIndentation 16
InsertNewlineAtEOF 16
BreakBeforeConceptDeclarations 12
IntegerLiteralSeparator 16
AlignTrailingComments (struct) 16

Solution : mettez à jour clang-format ou retirez les options non supportées. Le hook de vérification de version présenté en section 47.3 détecte ce problème avant qu'il ne se manifeste.

Formatage différent entre développeurs

Deux développeurs obtiennent un formatage différent sur le même fichier. Causes possibles :

  1. Versions différentes de clang-format — Vérifiez avec clang-format --version. C'est la cause la plus fréquente.
  2. Plusieurs .clang-format dans l'arborescence — Un .clang-format dans un sous-répertoire prend la priorité sur celui de la racine. Vérifiez qu'il n'y en a pas d'autre.
  3. .clang-format non versionné — Un développeur a un .clang-format local modifié qu'il n'a pas commité.

Reformatage en boucle

Un fichier est reformaté à chaque commit, même si rien d'autre n'a changé. Cause habituelle : une option de clang-format produit un résultat non idempotent sur certaines constructions syntaxiques (rare mais possible, surtout avec les macros complexes). Solution : isolez le code concerné avec // clang-format off et signalez le bug upstream si applicable.

SortIncludes casse la compilation

Un #include a été déplacé par le tri et le fichier ne compile plus. Cause : dépendance d'ordre non explicite entre les includes (un header qui nécessite un #define préalable ou un autre include). Solution : utilisez // clang-format off autour du bloc d'includes concerné et commentez la dépendance.


Résumé

L'intégration de clang-format dans les pre-commit hooks repose sur trois piliers : un fichier .clang-format bien conçu et versionné, une version d'outil homogène dans l'équipe, et un formatage à la sauvegarde dans l'éditeur qui rend le hook quasi transparent. Le .clang-format proposé dans cette section couvre les constructions C++ modernes (concepts, requires, ranges, structured bindings) et constitue un point de départ solide. Adaptez-le aux conventions de votre équipe, et ajoutez chaque commit de reformatage à .git-blame-ignore-revs pour préserver un historique Git exploitable.


⏭️ Intégration clang-tidy