Chaque nouvelle Pull Request (PR) doit adhérer aux règles suivantes pour être acceptée :
-
Conventions de Codage : Respectez les conventions de codage énoncées ci-dessous.
-
Absence de Warnings : Assurez-vous que le code ajouté ne génère pas de warnings lors de la compilation. Traitez tout avertissement émis par le compilateur avant de soumettre la PR.
Le langage C++ est reconnu pour sa richesse et sa puissance, mais cette complexité peut parfois rendre la collaboration difficile. L'utilisation de bonnes pratiques de codage en C++ offre de nombreux avantages, notamment :
-
Cohérence : en adoptant des conventions de codage cohérentes, nous facilitons la lecture du code pour l'ensemble de l'équipe de développement.
-
Maintenabilité : des pratiques de codage bien établies rendent le code plus facile à comprendre, à maintenir et à améliorer au fil du temps.
-
Sécurité : en suivant des bonnes pratiques, nous contribuons à la réduction des erreurs et des vulnérabilités potentielles dans notre code.
-
Performance : Certaines approches de codage peuvent influencer de manière favorable les performances du programme final. Il est essentiel de garder à l'esprit que le code sera exécuté sur un ESP32.
- Nommer les variables de manière explicite et :
- utilisez
pMaVariable
pour indiquer que c'est un pointeur. - utilisez
sMaVariableStatique
pour indiquer que c'est une variable statique. - utilisez
spMonPointeurStatique
pour indiquer que c'est un pointeur statique.
int* pMaVariable;
static sMaVariableStatique = 42;
static int* spMonPointeurStatique;
- Commencez le nom des classes par un "C" majuscule.
- Commencez le nom de la variable par un
m_
pour indiquer que cette variable est membre. ⚠️ N'oubliez pas : par défaut dans une classe, tout est en privé.
class CApp {
private:
int m_id = 0;
int* m_pId;
static int m_sCount = 42;
static int* m_spData;
};
- Assurez-vous de déclarer le destructeur de la classe de base comme virtuel si vous prévoyez d'utiliser l'héritage polymorphique.
class CBase {
public:
virtual ~CBase() = default;
};
class CDerived : public CBase {
public:
~CDerived() override { /* Destructeur de la classe dérivée */ }
};
- Privilégiez les Casts C++ :
Utilisez les casts C++ (
static_cast
etdynamic_cast
) plutôt que les anciens style casts (C-style casts). Les casts C++ fournissent une sémantique plus claire et permettent une vérification de type plus robuste.
// Utilisation de static_cast pour des conversions simples
float floatValue = static_cast<float>(intValue);
- Utilisez
dynamic_cast
pour effectuer des casts dynamiques, principalement dans le contexte de la programmation orientée objet avec héritage polymorphique.
class CBase {
// ...
};
class CDerived : public CBase {
// ...
};
CBase* pBasePtr = new CDerived;
CDerived* pDerivedPtr = dynamic_cast<CDerived*>(pBasePtr);
- Évitez l'utilisation des anciens style casts
((type)value)
. Les casts C++ offrent une meilleure expressivité et une sécurité accrue.
// Évitez l'utilisation des anciens style casts
float floatValue = (float)intValue; // À éviter
- Limitez l'utilisation de
reinterpret_cast
aux situations où une conversion entre types pointeurs ou entre pointeur et entier est absolument nécessaire. Cela peut introduire des comportements indéfinis et doit être utilisé avec prudence.
// Évitez autant que possible l'utilisation de reinterpret_cast
int* pValue = reinterpret_cast<int*>(pSomeVoidPointer);
- Évitez autant que possible l'utilisation de
const_cast
. Cherchez des alternatives pour éviter des modifications non intentionnelles de la constance.
// Évitez autant que possible l'utilisation de const_cast
const int constValue = 42;
int& nonConstRef = const_cast<int&>(constValue); // À éviter
- Commencez le nom des structures par un "S" majuscule.
- Utilisez les structures uniquement si elles ne contiennent pas de méthodes.
⚠️ N'oubliez pas : par défaut dans une structure, tout est en publique.
- Commencez le nom des énumérations par un "E" une majuscule.
enum EColor {
Red,
Green,
Blue
};
- Évitez les raw pointers :
- Utilisez des smart pointers plutôt que des raw pointers, sauf dans des cas extrêmes.
#include <memory>
std::unique_ptr<int> myUniquePtr = std::make_unique<int>(42);
std::shared_ptr<int> mySharedPtr = std::make_shared<int>(42);
Lorsque vous avez la possibilité de choisir entre les références et les pointeurs, optez pour les références. Les références offrent une syntaxe plus claire et réduisent le risque d'erreurs liées à la gestion de la mémoire.
#include <iostream>
#include <string>
// Fonction utilisant une référence
void displayName(const std::string& value) {
std::cout << "Hello world, " << value << " !" << std::endl;
}
int main() {
// Variable entière
std::string name = "Paxo-OS 8";
// Appel de la fonction en passant la variable par référence
displayName(name);
return 0;
}
- Utilisez le mot-clé
const
pour déclarer des variables qui ne doivent pas être modifiées après leur initialisation.
const int constantValue = 42;
- Utilisez
const
pour indiquer si une méthode modifie ou non l'état de l'objet. Les méthodesconst
ne modifient pas l'état de l'objet et devraient être marquées comme telles.
class CMyClass {
public:
int getValue() const // Méthode constante
{
return m_id;
}
void setValue(int newId) // Méthode non-constante
{
m_id = newId;
}
private:
int m_id = 0;
};
- Pointeurs const et objets const
const int* pImmutableValue; // Pointeur vers une valeur immuable
int const* pAlsoImmutable; // Équivalent
int const value = 42; // Objet immuable
- Préférez
constexpr
aux macros (#define
) : - Utilisez
constexpr
pour définir des constantes au lieu de macros chaque fois que possible. - Les constantes déclarées avec
constexpr
sont plus sûres en termes de portée et de type.
// Utilisation de constexpr
constexpr int maxItems = 100;
// Évitez d'utiliser #define pour définir des constantes
#define MAX_ITEMS 100
- Évitez les macros pour les fonctions :
- Utilisez plutôt des fonctions en ligne (
inline
) ou des fonctions normales, car elles offrent des avantages en termes de sécurité et de débogage.
#define SQUARE(x) ((x) * (x))
// Utilisez une fonction en ligne ou normale
inline int square(int x) {
return x * x;
}
- Utilisez
constexpr
pour les fonctions évaluables à la compilation : - Déclarez vos fonctions comme
constexpr
lorsque cela est possible. Cela permet une évaluation à la compilation lorsque les arguments sont des constantes connues à la compilation.
// Utilisation de constexpr pour une fonction non membre
constexpr int multiply(int a, int b) {
return a * b;
}
// Exemple d'utilisation à la compilation
constexpr int result = multiply(3, 4); // Évalué à la compilation
- Minimisez les copies inutiles dans les méthodes :
- Évitez les copies inutiles d'objets en passant les arguments par référence ou par référence constante plutôt que par valeur. Les copies peuvent entraîner une surcharge inutile sur le système et impacter les performances.
class CDataProcessor {
public:
// Utilisation de référence constante pour minimiser les copies
void processData(const std::vector<int>& data) {
// Traitement des données ici
}
};
- Privilégiez la Stack :
- Favorisez l'utilisation de la stack autant que possible pour allouer des variables locales et temporaires. La stack est plus rapide à accéder et libère automatiquement l'espace lorsqu'une fonction se termine.
void exampleFunction() {
int localVar; // Allocation sur la stack
// ...
}
- Limitation des Allocations Dynamiques :
- Limitez l'utilisation d'allocations dynamiques sur la heap. Les allocations sur la heap peuvent entraîner une fragmentation de la mémoire et sont généralement plus lentes que les allocations sur la stack.
// Éviter autant que possible les allocations sur la heap
int* dynamicValue = new int; // À utiliser avec précaution
The Cherno - C++ : https://www.youtube.com/watch?v=18c3MTX0PK0&list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb