Aller au contenu

Conventions de codage C++

Norme de codage C++ de l'organisation EIP-MemTide. Ce document est la reference unique et complete pour tous les depots C++ de l'organisation.

Le style est inspire des conventions C++ modernes d'Epic Games.


Portee

Cette norme s'applique a tout code C++ produit ou maintenu par l'organisation EIP-MemTide, sans exception. Elle couvre le nommage, les standards techniques, les commentaires, l'architecture de fichiers, le formatage et l'integration CMake.

Tout contributeur — membre ou externe — doit se conformer a ces regles avant de soumettre du code.


1. Langue

Tout le code, les commentaires, les noms de variables, les noms de fonctions, les chaines visibles par l'utilisateur (logs, erreurs, sortie console) et la documentation doivent etre rediges en anglais americain (U.S. English). Aucune exception.


2. Conventions de nommage

2.1 PascalCase partout

Convention : PascalCase pour tous les identifiants.

Obligatoire pour les classes, fonctions, methodes, variables, parametres, dossiers et fichiers. Premiere lettre en majuscule, sans underscores entre les mots.

class MyClass;
void CalculateTransform();
Int32 TotalLeaves;

2.2 Classes template

Convention : Prefixe T + PascalCase

template <typename ObjectType>
class TAttribute;

2.3 Enums

Convention : Prefixe E + PascalCase, valeurs avec prefixe court majuscule + underscore

enum class EColorBits { ECB_Red, ECB_Green, ECB_Blue };
  • Les valeurs d'enum utilisent un prefixe court en majuscules derive du nom de l'enum, suivi d'un underscore.
  • C'est une exception intentionnelle a la regle PascalCase sans underscore.

2.4 Variables booleennes

Convention : Prefixe b + PascalCase

bool bPendingDestruction;
bool bHasValidConnection;

2.5 Types et variables

Convention : Noms (substantifs). Pas de noms a une lettre.

/* Mauvais */  for (Int32 I = 0; I < MaxIterations; ++I) { ... }
/* Bon */      for (Int32 IterationIdx = 0; IterationIdx < MaxIterations; ++IterationIdx) { ... }

2.6 Methodes

Convention : Verbes decrivant l'effet ou la valeur de retour.

/* Mauvais */  void Calculation();       bool Ready() const;
/* Bon */      void CalculateTransform(); bool IsHardwareReady() const;

2.7 Macros

Convention : Prefixe projet + SCREAMING_SNAKE_CASE. Chaque projet definit son propre prefixe court en majuscules.

#define MYPROJ_PLATFORM_LINUX
#define MYPROJ_LOG_ERROR(...)

2.8 Tableau recapitulatif

Element Convention Exemple
Classes, fonctions, methodes, variables PascalCase MyClass, CalculateTransform()
Classes template T + PascalCase TAttribute
Enums E + PascalCase EColorBits
Valeurs d'enum Prefixe majuscule + _ + PascalCase ECB_Red
Booleens b + PascalCase bPendingDestruction
Macros C++ Prefixe projet + SCREAMING_SNAKE_CASE MYPROJ_PLATFORM_LINUX
Dossiers PascalCase MySubModule
Fichiers PascalCase MyClass.hpp

2.9 Exception — code kernel (BPF)

Les fichiers de code kernel-space (.bpf.c, .bpf.h) suivent les conventions du noyau Linux (snake_case) au lieu de PascalCase. Ceci est impose par le style de codage du noyau et les contraintes de la toolchain BPF. Cette exception ne doit jamais etre etendue au code userspace.


3. Standards techniques

3.1 Scoping explicite

Regle : using namespace ... est strictement interdit. Utiliser les namespaces explicites pour prevenir les collisions de symboles (void MyNamespace::MyClass::Method()).

3.2 Pointeurs

Regle : Utiliser nullptr, jamais NULL. Le macro C NULL n'est pas autorise.

3.3 Typage explicite

Regle : Eviter auto. Le type doit etre visible a la lecture, sans tooltips IDE.

Exceptions autorisees : - Liaison d'une lambda a une variable. - Variables d'iterateur dont le type est verbeux et nuit a la lisibilite.

/* Interdit */   auto Result = CalculateOffset(Base, Size);
/* Autorise */   auto OnComplete = [this](Int32 Status) { HandleCompletion(Status); };
/* Autorise */   auto Iterator = MyContainer.begin();

3.4 Gestion memoire

Regle : new/delete manuels interdits — utiliser RAII et les pointeurs intelligents.

Situation Outil
Propriete exclusive std::unique_ptr
Propriete partagee std::shared_ptr
Reference non-proprietaire std::weak_ptr ou pointeur brut
Allocation sur la pile Variables locales, std::array
/* Interdit */  MyClass *Object = new MyClass(); delete Object;
/* Bon */       std::unique_ptr<MyClass> Object = std::make_unique<MyClass>();

3.5 Const-correctness

const et constexpr sont autant de la documentation que des directives compilateur. Principe directeur : transmettre l'information au compilateur le plus tot possible.

Regles : - Passer les arguments par pointeur ou reference const s'ils ne sont pas modifies. - Marquer les methodes const si elles ne modifient pas l'objet. - Utiliser const pour l'iteration sur les conteneurs si la boucle ne modifie pas le conteneur.

void SomeMutatingOperation(MyResult& OutResult, const std::vector<Int32>& InArray)
{
    /* InArray ne sera pas modifie, mais OutResult le sera */
}

void MyClass::SomeNonMutatingOperation() const
{
    /* Cette methode ne modifie pas l'objet */
}

Ne jamais utiliser const sur un type de retour (inhibe la move semantics). Ne s'applique qu'au type de retour lui-meme, pas au type cible d'un pointeur ou reference retourne.

/* Mauvais */  const std::vector<std::string> GetNames();
/* Bon */      const std::vector<std::string>& GetNames();

3.6 Constexpr et consteval

Regle : Preferer constexpr a const pour les valeurs connues a la compilation.

/* Mauvais */    const Int32 MaxPoolSize = 1024;
/* Bon */        constexpr Int32 MaxPoolSize = 1024;
  • Marquer les fonctions constexpr quand leur logique permet l'evaluation a la compilation.
constexpr Size AlignTo(Size Value, Size Alignment)
{
    return (Value + Alignment - 1) & ~(Alignment - 1);
}
static_assert(AlignTo(13, 8) == 16);
  • Utiliser if constexpr pour le branchement compile-time dans les templates au lieu de SFINAE ou du tag dispatch.
template <typename T>
void Process(T Value)
{
    if constexpr (std::is_integral_v<T>)
        ProcessInteger(Value);
    else
        ProcessFloat(Value);
}
  • consteval peut etre utilise pour garantir l'evaluation a la compilation quand aucun fallback runtime ne doit exister (ex. : layout memoire, generation de hash statique).
consteval Size ComputeBlockSize(Size PageSize, Size HeaderSize)
{
    return PageSize - HeaderSize;
}

4. Limites de complexite

Metrique Limite
Lignes par fonction 100 max
Statements par fonction 50 max
Parametres par fonction 6 max

Si une fonction depasse ces limites, elle doit etre decomposee en sous-fonctions.


5. Commentaires

5.1 Regles generales

  • Tous les commentaires doivent etre en anglais.
  • Commentaires sobres — concis, factuels, sans verbiage inutile.
  • Pas d'emojis, d'ASCII art ou d'elements decoratifs. Ton professionnel en permanence.
  • Ecrire du code auto-documentant :
/* Mauvais */  t = s + l - b;
/* Bon */      TotalLeaves = SmallLeaves + LargeLeaves - SmallAndLargeLeaves;
  • Ecrire des commentaires utiles. Expliquer le pourquoi, pas le comment.
  • Refactoriser plutot qu'expliquer. Ne pas sur-commenter du code mal ecrit ; le reecrire.
  • Ne pas contredire le code. Mettre a jour les commentaires quand la logique change.
  • Les commentaires doivent etre concis (une ligne max). Ne pas repeter ce que la signature dit deja. Pas de commentaire vaut mieux qu'un commentaire redondant.

5.2 Standard Doxygen

Convention : Style triple-slash /// exclusivement. Les autres formats (/** */) sont interdits.

/// @brief Checks if the hardware layer is initialized and ready.
/// @param TimeoutMs Maximum wait time in milliseconds.
/// @return True if the hardware is ready to receive commands.
bool IsHardwareReady(Int32 TimeoutMs) noexcept;

Tags Doxygen courants :

Tag Usage
@brief Description courte (obligatoire)
@details Description longue (optionnel)
@param Description d'un parametre
@return Description de la valeur de retour
@tparam Description d'un parametre template
@note Remarque importante
@warning Avertissement

6. Conventions de fichiers

6.1 Extensions

Extension Usage
.hpp Declarations uniquement. Pas de logique.
.inl Implementations de templates.
.cpp Implementations non-template.

6.2 Protection des headers

Regle : #pragma once en haut de chaque header. Les gardes #ifndef/#define classiques ne sont pas autorisees.

6.3 Architecture de fichiers

Les repertoires Include/ et Source/ sont symetriques : chaque module possede un sous-dossier dans les deux arborescences, et les noms doivent correspondre.

Include/MyModule/MyClass.hpp          Declarations
Source/MyModule/CMakeLists.txt        Build definition
Source/MyModule/MyClass.cpp           Implementations

Header (Include/MyModule/MyClass.hpp) :

#pragma once

namespace MyModule
{
    class MyClass
    {
    public:
        void DoWork();
    private:
        Int32 WorkCount;
    };
}

Implementation (Source/MyModule/MyClass.cpp) :

#include "MyModule/MyClass.hpp"

void MyModule::MyClass::DoWork()
{
    ...
}

Template (.inl) — le fichier .inl est inclus a la fin du .hpp correspondant :

// Include/MyModule/TContainer.hpp
#pragma once

namespace MyModule
{
    template <typename T>
    class TContainer
    {
    public:
        void Add(const T& Item);
    };
}

#include "MyModule/TContainer.inl"

7. Architecture et CMake

7.1 Structure modulaire

Chaque module est une bibliotheque statique et contient un CMakeLists.txt a sa racine dans Source/.

project_add_module(MyModule)

# Optionnel : dependances
target_link_libraries(MyModule PUBLIC MyDependency)

La macro project_add_module (ou equivalent specifique au projet) collecte les sources dans Source/, expose les headers de Include/ et cree une cible statique linkable par les autres modules.

7.2 Dependances

Les modules declarent leurs dependances via target_link_libraries. Les dependances transitives sont gerees par CMake.

target_link_libraries(MyModuleB PUBLIC MyModuleA)

8. Formatage

8.1 Regles de base

Regle Valeur
Indentation 4 espaces (pas de tabulations)
Largeur maximale 120 colonnes
Style d'accolades Allman
Liaison des pointeurs A droite (int *Ptr)
Espaces dans les parentheses Non
Espace avant parentheses de fonction Non

8.2 Style Allman

Les accolades ouvrantes sont toujours sur une nouvelle ligne, alignees avec le bloc parent.

if (bIsReady)
{
    Initialize();
}
else
{
    WaitForReady();
}

class MyClass
{
public:
    void DoWork();
private:
    Int32 ItemCount;
};

8.3 Application automatique

Le formatage est applique par clang-format. Toujours formater le code avant de committer. Les hooks de pre-commit bloquent le code non conforme.


9. Patrons interdits — resume

Patron Alternative
using namespace ... Namespaces explicites
NULL nullptr
auto (sauf exceptions) Type explicite
/** */ Doxygen /// exclusivement
const sur type de retour Retourner par valeur ou reference const
new/delete manuels std::unique_ptr, std::shared_ptr
#ifndef guards #pragma once
Commentaires en francais Anglais U.S. uniquement
Emojis, ASCII art Ton professionnel

10. Checklist avant soumission

Avant chaque pull request, verifier :

  • Le code compile sans warnings (-Wall -Wextra -Wpedantic)
  • Le formatage est conforme (clang-format)
  • L'analyse statique passe (clang-tidy)
  • Pas de new/delete manuels — RAII et pointeurs intelligents
  • Fonctions < 100 lignes, < 6 parametres
  • Tests unitaires ajoutes/mis a jour
  • Commentaires Doxygen /// sur les nouvelles API publiques
  • Tous les commentaires et le code en anglais
  • Pas d'auto sauf exceptions autorisees
  • Const/constexpr correctness respectee