Libftpp đ

đ Vue d'ensemble
đ Description
libftpp est une librairie C++ moderne explorant les structures de données avancées et les design patterns. Ce projet met l'accent sur les bonnes pratiques du C++ moderne, la gestion efficace de la mémoire et l'implémentation rigoureuse des patterns classiques du génie logiciel.
đ Documentation
Une documentation complÚte générée avec Doxygen est disponible en ligne :
đ libftpp.duckiverse.com
Cette documentation inclut :
- đ API complĂšte de toutes les classes et fonctions
- đĄ Exemples d'utilisation dĂ©taillĂ©s
- đ Diagrammes de classes et relations
- đ Guide d'implĂ©mentation des design patterns
- ⥠Notes sur les performances et bonnes pratiques
đ Installation et compilation
Prérequis
- Compilateur C++17 ou supérieur (g++ recommandé)
- CMake 3.14 ou supérieur (pour les tests GoogleTest)
- Make
- Git
Installation
git clone git@github.com:monsieurCanard/libftpp.git
cd libftpp
Compilation de la librairie
Cette commande compile tous les fichiers sources et génÚre la librairie statique libftpp.a dans le répertoire racine.
Fichiers compilés :
Options de compilation :
-std=c++17 : Standard C++17
-Wall -Wextra -Werror : Tous les warnings activés, traités comme des erreurs
Compilation et lancement des tests Google Test
# Compiler la librairie + les tests GoogleTest
make gtest
# Lancer les tests GoogleTest
make run-gtest
Les tests GoogleTest sont automatiquement téléchargés via CMake et compilés dans le dossier build/.
Tests disponibles :
test_data_buffer.cpp - Tests du buffer de données
test_pool.cpp - Tests du pool mémoire
test_memento.cpp - Tests du pattern Memento
test_observer.cpp - Tests du pattern Observer
test_singleton.cpp - Tests du pattern Singleton
test_state_machine.cpp - Tests de la machine à états
test_message.cpp - Tests du systĂšme de messages
test_thread.cpp - Tests des threads
test_thread_safe_queue.cpp - Tests de la queue thread-safe
test_worker_pool.cpp - Tests du pool de workers
test_persistent_worker.cpp - Tests du worker persistant
test_logger.cpp - Tests du logger
test_chronometre.cpp - Tests du chronomĂštre
test_ring_buffer.cpp - Tests du buffer circulaire
test_n_ary_tree.cpp - Tests de l'arbre n-aire
test_observable_value.cpp - Tests de la valeur observable
test_ivector2.cpp - Tests du vecteur 2D
test_ivector3.cpp - Tests du vecteur 3D
test_perlin_noise.cpp - Tests du bruit de Perlin
test_random_2D_coordinate_generator.cpp - Tests du générateur de coordonnées
Nettoyage
# Nettoyer les fichiers objets
make clean
# Nettoyer complĂštement (objets + librairie + tests + logs)
make fclean
# Recompiler complĂštement
make re
đ Structure du projet
libftpp/
âââ src/
â âââ data_structures/
â â âââ data_buffer/ # SĂ©rialisation/dĂ©sĂ©rialisation de donnĂ©es
â â âââ pool/ # Pool de mĂ©moire avec allocation optimisĂ©e
â âââ design_patterns/
â â âââ memento/ # Sauvegarde et restauration d'Ă©tat
â â âââ observer/ # Notification d'Ă©vĂ©nements
â â âââ singleton/ # Instance unique globale
â â âââ state_machine/ # Machine Ă Ă©tats finis
â âââ mathematics/
â â âââ IVector2/ # Interface vecteur 2D
â â âââ IVector3/ # Interface vecteur 3D
â â âââ perlin_noise/ # GĂ©nĂ©ration de bruit de Perlin
â â âââ random_2D_coordinate_generator/ # GĂ©nĂ©rateur de coordonnĂ©es alĂ©atoires
â âââ network/
â â âââ client/ # Client TCP pour communication rĂ©seau
â â âââ message/ # SystĂšme de messages structurĂ©s
â â âââ server/ # Serveur TCP multi-clients avec select()
â âââ thread/
â â âââ persistent_worker/ # Worker thread persistant
â â âââ thread/ # Wrapper thread avec fonctionnalitĂ©s Ă©tendues
â â âââ thread_safe_iostream/ # IO thread-safe
â â âââ thread_safe_queue/ # Queue thread-safe
â â âââ worker_pool/ # Pool de threads workers
â âââ bonus/
â âââ chronometre/ # Mesure de temps et performance
â âââ logger/ # SystĂšme de logging avancĂ©
â âââ n_ary_tree/ # Arbre n-aire gĂ©nĂ©rique
â âââ observable_value/ # Valeur observable avec pattern Observer
â â âââ IObserver/ # Interface observateur
â âââ ring_buffer/ # Buffer circulaire optimisĂ©
âââ tests/ # Tests unitaires avec GoogleTest
â âââ test_*.cpp # Tests pour chaque composant
â âââ main.cpp # Point d'entrĂ©e des tests
âââ programs_test/ # Programmes d'exemple et de test
â âââ programServer/ # Exemple serveur de test
â âââ programStressTesteur/ # Programme de test de charge
âââ CMakeLists.txt # Configuration CMake
âââ Makefile # Build systĂšme Make
âââ libftpp.hpp # Header unifiĂ© pour toute la librairie
đŻ RĂšgles de codage
- Classes : PascalCase (
MyClass)
- Méthodes : camelCase (
myMethod())
- Fichiers : snake_case (
my_class.hpp)
- Standard : C++11 minimum avec flags
-Wall -Wextra -Werror
- Interdictions : Boost,
*printf(), *alloc(), free()
- Headers : Indépendants avec include guards
- MĂ©moire : Ăviter les fuites, gestion RAII (Resource Acquisition Is Initialization)
đ Classes implĂ©mentĂ©es
đ Structures de donnĂ©es implĂ©mentĂ©es
đŠ Pool de mĂ©moire
Un Pool est un réservoir de mémoire qui pré-alloue des objets pour éviter les allocations/désallocations fréquentes. Améliore significativement les performances pour la création d'objets coûteux.
Caractéristiques :
- Utilise
std::aligned_storage pour l'alignement mémoire (deprecated C++23, utiliser std::aligned_storage_t)
- Templates variadiques pour les constructeurs
- Placement new pour la construction in-place
- Gestion automatique de la mémoire via RAII
API principale :
auto* obj = pool.acquire(arg1, arg2);
pool.release(obj);
Avantages :
- Ăvite la fragmentation mĂ©moire
- Réduction drastique des appels
new/delete
- Performance prévisible pour les systÚmes temps réel
đŸ DataBuffer
SystÚme de sérialisation/désérialisation thread-safe utilisant un buffer interne pour la persistance et le transfert de données.
Fonctionnalités :
- Sérialisation via
reinterpret_cast<unsigned int*> pour copier les octets bruts
- Désérialisation via
memcpy pour restaurer les données typées
- Support des types primitifs et
std::string
- Gestion automatique de la taille et du curseur
- Operators
<< et >> pour une syntaxe intuitive
Utilisation :
buffer << 42 << 3.14f << std::string("Hello");
int value;
float pi;
std::string text;
buffer >> value >> pi >> text;
buffer.appendBytes(data, size);
auto raw = buffer.getBytes();
A simple LIFO data buffer for serialization and deserialization for simple data types and std::string...
Cas d'usage :
- Communication réseau (utilisé par la classe
Message)
- Sauvegarde de jeu
- IPC (Inter-Process Communication)
- Cache de données
đ§” Programmation concurrente et Threading
La librairie fournit plusieurs composants pour la programmation multi-thread sécurisée et performante.
đ§” Thread
Wrapper autour de std::thread avec des fonctionnalités étendues pour faciliter la gestion des threads.
Caractéristiques :
- Encapsulation de
std::thread avec interface simplifiée
- Gestion automatique du cycle de vie
- Support des lambdas et fonctions membres
Utilisation :
processData();
});
worker.join();
đ ThreadSafeQueue
Queue FIFO thread-safe utilisant mutex et condition variables pour la synchronisation.
Fonctionnalités :
- Operations
push() et pop() thread-safe
- Blocage automatique si la queue est vide
- Gestion propre de l'arrĂȘt
Pattern producteur-consommateur :
queue.push(task);
Task task = queue.pop();
đšïž ThreadSafeIOStream
Wrapper thread-safe pour les opérations I/O (cout, cerr, fichiers).
ProblÚme résolu : Les streams C++ standards ne sont pas thread-safe, ce qui peut causer des sorties entrelacées.
Solution :
tsout << "Thread 1: " << data << std::endl;
tsout << "Thread 2: " << other << std::endl;
âïž PersistentWorker
Thread worker qui exécute des tùches nommées en boucle continue avec gestion dynamique.
Architecture :
- Map de tĂąches
unordered_map<string, function<void()>>
- Ajout/suppression dynamique pendant l'exécution
- Pause configurable entre tĂąches (
PAUSE_BT_TASK)
Utilisation :
sendHeartbeat();
});
checkSystemHealth();
});
Persistent worker thread that executes tasks in a continuous loop.
void removeTask(const std::string &name)
void addTask(const std::string &name, const std::function< void()> &jobToExecute)
Cas d'usage :
- Monitoring systĂšme
- Heartbeats réseau
- Nettoyage périodique
- Mise Ă jour de caches
đ (astuce) Condition Variables
Les condition variables (std::condition_variable) permettent la synchronisation entre threads en bloquant un thread jusqu'Ă ce qu'une condition soit remplie.
Principe : Une condition variable est toujours associée à un mutex et permet à un thread d'attendre qu'un autre thread signale un changement d'état.
Utilisation typique :
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; });
}
Méthodes principales :
wait(lock, predicate) : Bloque jusqu'à ce que le prédicat soit vrai
notify_one() : Réveille un thread en attente
notify_all() : Réveille tous les threads en attente
Pattern Worker Pool :
cv.wait(lock, [this] { return stop || !jobs.empty(); });
đ WorkerPool
Un WorkerPool est un pattern de concurrence qui maintient un groupe de threads workers qui exécutent des tùches à partir d'une queue partagée.
Architecture :
- Queue de jobs :
std::queue<std::function<void()>>
- Workers :
std::vector<std::thread> qui exécutent la boucle principale
- Synchronisation :
std::mutex + std::condition_variable
Fonctionnement :
pool.addJob([]() {
processData();
});
Worker Pool for Concurrent Job Execution.
Avantages :
- Ăvite la crĂ©ation/destruction rĂ©pĂ©tĂ©e de threads
- ContrÎle du niveau de parallélisme
- Distribution automatique des tĂąches
- Gestion propre de l'arrĂȘt
Pattern de boucle worker :
@code{cpp}
void WorkerPool::loop() { while (!_stop) { std::function<void()> job; { std::unique_lock<std::mutex> lock(_mtx); _cv.wait(lock, [this] { return _stop || !_jobs.empty(); });
if (_stop || _jobs.empty()) break;
job = _jobs.front(); _jobs.pop(); } job(); // Exécution hors du lock } }
đ Architecture rĂ©seau
đš Message
Classe pour la communication réseau structurée utilisant un protocole de messaging personnalisé.
Format de transfert :
[Type (int)][Taille (size_t)][Données (variable)]
Caractéristiques :
- Sérialisation automatique : Operators
<< et >> pour tous types
- RingBuffer interne : Stockage efficace des données
- Type safety : Chaque message a un type pour le dispatch
- Gestion des strings : Spécialisations pour
std::string
Utilisation :
msg << userId << username << password;
int userId;
string username, password;
msg >> userId >> username >> password;
Class representing a structured message for network communication.
API principale :
operator<<(const T&) : Ajout de données typées
operator>>(T&) : Extraction de données typées
getSerializedData() : Données complÚtes pour transmission
isComplet() : Vérification de l'intégrité du message
đ„ïž Server
Serveur TCP multi-clients utilisant select() pour la gestion asynchrone des connexions.
Architecture :
- Sockets POSIX : API systĂšme pour les communications TCP
- select() : Multiplexage I/O pour gérer plusieurs clients
- Callbacks : Actions définissables par type de message
- Gestion d'état : Tracking des connexions et messages partiels
Fonctionnalités :
std::string content;
msg >> content;
server.
sendToAll(createChatMessage(content));
});
Basic TCP server class using POSIX sockets and select() for multi-client communication.
void defineAction(const Message::Type &messageType, const std::function< void(long long &clientID, const Message &msg)> &action)
void sendToAll(const Message &message)
void start(const size_t &port=0)
Limitations :
- Non thread-safe : Utilisation mono-thread uniquement
- Connexions limitées : Maximum
NB_CONNECTION (256) clients
- Buffer fixe :
READ_BUFFER_SIZE (4096) octets par lecture
đ» Client
Client TCP utilisant les mĂȘmes primitives que le serveur pour la communication.
Fonctionnalités :
- Connexion simple :
connect(address, port)
- Messaging : Envoi/réception de messages structurés
- Callbacks : Actions sur réception de messages
- Gestion d'état : Reconstruction des messages fragmentés
Utilisation :
Client client(
"127.0.0.1", 8080);
client.defineAction(MSG_WELCOME, [](
const Message& msg) {
std::string welcome;
msg >> welcome;
std::cout << welcome << std::endl;
});
loginMsg << "username" << "password";
client.send(loginMsg);
client.update();
Basic TCP client class using POSIX sockets and select() for network communication.
Pattern d'utilisation typique :
@code{cpp}
while (running) { client.update(); // Traite les messages entrants handleInputEvents(); // Interface utilisateur std::this_thread::sleep_for(16ms); // ~60 FPS }
đą MathĂ©matiques
La librairie fournit des outils mathématiques pour les graphiques, jeux et simulations.
đ IVector2 et IVector3
Interfaces pour les vecteurs 2D et 3D avec opérations mathématiques standards.
Opérations supportées :
- Addition, soustraction, multiplication, division
- Produit scalaire (dot product)
- Norme et normalisation
- Distance entre vecteurs
IVector2 - Vecteur 2D :
template <typename T>
public:
T distance(
const IVector2& other)
const;
};
IVector2 operator*(const IVector2 &other) const
IVector2 operator-(const IVector2 &other) const
IVector2 operator+(const IVector2 &other) const
IVector3 - Vecteur 3D :
template <typename T>
public:
};
IVector3 operator+(const IVector3 &other) const
IVector3 cross(const IVector3 &other) const
Cas d'usage :
- Physique et collisions
- Graphiques 2D/3D
- Calculs géométriques
- Déplacements et directions
đ Perlin Noise 2D
Générateur de bruit de Perlin pour créer des textures procédurales naturelles et cohérentes.
Caractéristiques :
- Génération de bruit pseudo-aléatoire mais cohérent
- Interpolation lisse entre valeurs
- Paramétrable (seed, fréquence, amplitude)
- Idéal pour terrains, textures, nuages
Utilisation :
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float noise = perlin.noise(x * 0.1f, y * 0.1f);
heightmap[y][x] = (noise + 1.0f) * 0.5f;
}
}
2D Perlin Noise Generator
Applications :
- Génération de terrains procéduraux
- Textures naturelles (bois, marbre, nuages)
- Animation de particules
- Génération de cartes
đČ Random 2D Coordinate Generator
Générateur de coordonnées aléatoires 2D avec distribution uniforme.
Fonctionnalités :
- Génération de points aléatoires dans un rectangle
- Seed configurable pour reproductibilité
- Distribution uniforme garantie
Utilisation :
for (int i = 0; i < enemyCount; i++) {
auto [x, y] = gen.generate(0, mapWidth, 0, mapHeight);
spawnEnemy(x, y);
}
2D Coordinate-Based Random Number Generator
Cas d'usage :
- Placement d'objets dans un jeu
- Génération de niveaux procéduraux
- Tests avec données aléatoires
- Simulations Monte Carlo
đ Composants Bonus
â±ïž Chronometre
Classe utilitaire pour mesurer précisément le temps d'exécution et les performances.
Fonctionnalités :
- Mesure de temps haute précision avec
std::chrono
- Calcul automatique du temps écoulé
- Support de plusieurs unités (ms, ”s, ns)
Utilisation :
expensiveOperation();
chrono.stop();
std::cout << "Temps: " << chrono.elapsed() << " ms" << std::endl;
Chronometre class for measuring elapsed time between start and end timestamps. It provides methods to...
void start()
Start the chronometre.
đ Logger
SystĂšme de logging thread-safe avec niveaux de log et sortie fichier/console.
Niveaux de log :
DEBUG : Messages de débogage détaillés
INFO : Messages informatifs généraux
WARNING : Avertissements
ERROR : Erreurs critiques
Caractéristiques :
- Singleton pattern pour accĂšs global
- Thread-safe via
ThreadSafeIOStream
- Horodatage automatique
- Filtrage par niveau de log
- Sortie fichier et/ou console
Utilisation :
void setLogLevel(LogLevel level)
void logConsole(LogLevel level, const std::string &message)
Log a message to the console if the log level is sufficient.
void log(LogLevel level, const std::string &message)
Log a message to the log file if the log level is sufficient.
static Logger & instance()
void setOutputFile(const std::string &filename)
đ RingBuffer
Buffer circulaire optimisé avec taille fixe et écrasement automatique des anciennes données.
Avantages :
- Pas de réallocation dynamique
- Performance prévisible O(1)
- Idéal pour les logs, audio, streaming
Utilisation :
buffer.push(42);
int value = buffer.pop();
if (buffer.isFull()) {
}
RingBuffer class for managing a circular buffer of bytes. It provides methods to push and pop bytes,...
Cas d'usage :
- Buffers audio/vidéo
- Logs circulaires
- File d'événements
- Données de capteurs
đł N-ary Tree
Arbre gĂ©nĂ©rique oĂč chaque nĆud peut avoir N enfants (pas limitĂ© Ă 2 comme un arbre binaire).
Caractéristiques :
- Template pour tout type de données
- Parcours en profondeur et largeur
- Insertion/suppression dynamique
- Gestion automatique de la mémoire
Utilisation :
auto* root = tree.createRoot("Root");
auto* child1 = tree.
addChild(root,
"Child 1");
auto* child2 = tree.
addChild(root,
"Child 2");
tree.traverseDepthFirst([](const std::string& data) {
std::cout << data << std::endl;
});
Node< TType > * addChild(Node< TType > *parent, const TType &value)
Cas d'usage :
- SystĂšmes de fichiers
- Organigrammes
- Hiérarchies de scÚnes 3D
- Arbres de décision
đïž ObservableValue
Template qui implémente le pattern Observer pour des valeurs observables avec notification automatique.
Fonctionnalités :
- Notification automatique lors du changement de valeur
- Support de multiples observateurs
- Comparaison pour éviter les notifications inutiles
- Gestion automatique des abonnements
Interface requise :
template <typename T>
public:
virtual void update(
const T& newValue) = 0;
};
virtual void update(const TType &newValue)=0
Utilisation :
public:
void update(
const int& value)
override {
std::cout << "Nouvelle valeur: " << value << std::endl;
}
};
MyObserver observer;
health.subscribe(&observer);
health.set(95);
health = 90;
ObservableValue class template that implements the Observer design pattern. It allows observers to su...
Cas d'usage :
- Reactive programming
- Data binding (UI)
- SystÚmes d'événements
- Game state management
đ Gang of Four â RĂ©sumĂ© simple
| Catégorie | Pattern | Idée en une phrase | Exemple en C++ |
| CrĂ©ation | Singleton | Toujours la mĂȘme instance unique | Un seul Logger ou ConfigManager |
| Factory Method | Choisit quel objet crĂ©er sans dire son type exact | ShapeFactory â retourne Circle ou Square |
| Abstract Factory | CrĂ©e des familles dâobjets compatibles | UIFactory â boutons Windows/Linux |
| Builder | Construit un objet Ă©tape par Ă©tape | HttpRequestBuilder pour configurer une requĂȘte |
| Prototype | Clone un objet existant | Document* copy = doc.clone(); |
| Structure | Adapter | Rend deux interfaces compatibles | LegacyPrinterAdapter pour utiliser une vieille lib |
| Bridge | Sépare abstraction et implémentation | Renderer (OpenGL/DirectX) séparé de Shape |
| Composite | Objets simples et composés traités pareil | File et Directory dans un systÚme de fichiers |
| Decorator | Ajoute des fonctions sans toucher au code | Stream décoré avec BufferedStream |
| Facade | Simplifie un systĂšme complexe | CompilerFacade qui appelle lexer+parser+codegen |
| Flyweight | Partage objets identiques pour économiser mémoire | Character dans un éditeur de texte |
| Proxy | ContrĂŽle lâaccĂšs Ă un objet rĂ©el | ImageProxy qui charge lâimage Ă la demande |
| Comportement | Observer | Un objet prĂ©vient les autres automatiquement | Button â notifie ses listeners |
| Memento | Sauvegarde/restaure un état | Game.save() et Game.load() |
| State | Change le comportement selon lâĂ©tat | TCPConnection en Ă©tat Connected/Closed |
| Chain of Responsibility | Passe une requĂȘte dans une chaĂźne de handlers | Middleware HTTP qui traite ou passe au suivant |
| Command | Action emballée dans un objet | UndoCommand ou MoveCommand dans un éditeur |
| Interpreter | Exécute une mini-grammaire/langage | Calculatrice qui lit 1+2*3 |
| Iterator | Parcourt une collection sans connaĂźtre sa structure | for(auto it = list.begin(); it != list.end(); ++it) |
| Mediator | Un objet central gĂšre la communication | ChatRoom qui relaie les messages |
| Strategy | Choisir un algo interchangeable facilement | sort(data, QuickSortStrategy{}) |
| Template Method | Squelette dâun algo, dĂ©tails dans les sous-classes | Game::play() appelle init(), loop(), end() |
| Visitor | Ajoute une opération sans changer les classes | ASTVisitor pour analyser un arbre syntaxique |
đ Details - Design Patterns (â
= implémentés)
đč CrĂ©ation (Creational Patterns)
â
Singleton
But : Garantit qu'une classe n'a qu'une seule instance et fournit un point d'accĂšs global.
Utilisation : Configuration globale, gestionnaire de ressources, logging.
Implémentation : Utilise static pour partager l'instance entre threads.
Singleton Design Pattern.
static TType * instance()
static void instantiate(TArgs... p_args)
Avantages :
- ContrĂŽle strict de l'instanciation
- AccĂšs global uniforme
Factory Method
But : Définit une interface pour créer un objet, mais laisse les sous-classes décider de la classe instanciée.
Utilisation : Création d'objets sans spécifier leur classe exacte, frameworks extensibles.
Exemple d'usage :
class ShapeFactory {
public:
virtual Shape* createShape() = 0;
};
class CircleFactory : public ShapeFactory {
public:
Shape* createShape() override { return new Circle(); }
};
Abstract Factory
But : Fournit une interface pour créer des familles d'objets liés sans spécifier leurs classes concrÚtes. Utilisation : SystÚmes multi-plateformes, thÚmes d'interface, familles de produits.
Avantages :
- Isole les classes concrĂštes
- Facilite l'échange de familles de produits
- Garantit la cohérence entre produits
Builder
But : Sépare la construction complexe d'un objet de sa représentation finale.
Utilisation : Objets avec de nombreux paramĂštres optionnels, configurations complexes.
Exemple :
class HttpRequestBuilder {
public:
HttpRequestBuilder& setUrl(const std::string& url);
HttpRequestBuilder& setMethod(const std::string& method);
HttpRequestBuilder& addHeader(const std::string& key, const std::string& value);
HttpRequest build();
};
Prototype
But : Crée de nouveaux objets en copiant un prototype existant.
Utilisation : Clonage d'objets complexes, éviter la re-initialisation coûteuse.
đč Structure (Structural Patterns)
Adapter
But : Convertit l'interface d'une classe en une autre attendue par le client.
Utilisation : Intégration de librairies tierces, legacy code, APIs incompatibles.
Types :
- Object Adapter : utilise la composition
- Class Adapter : utilise l'héritage multiple
Bridge
But : Sépare l'abstraction de son implémentation pour les faire évoluer indépendamment.
Utilisation : Ăviter l'explosion combinatoire d'hĂ©ritages, architectures multi-plateformes.
Avantages :
- Découplage abstraction/implémentation
- Extensibilité facilitée
- Masquage des détails d'implémentation
Composite
But : Permet de traiter un groupe d'objets comme un objet unique (structure d'arbre).
Utilisation : SystÚmes de fichiers, interfaces graphiques, menus hiérarchiques.
Caractéristiques :
- Structure récursive
- Traitement uniforme des objets simples et composites
- Facilite l'ajout de nouveaux types de composants
Decorator
But : Ajoute dynamiquement des responsabilités à un objet sans modifier sa classe.
Utilisation : Extensions de fonctionnalités, middleware, streams, GUI.
Avantages :
- Alternative flexible à l'héritage
- Combinaison dynamique de comportements
- Respect du principe Open/Closed
Facade
But : Fournit une interface simplifiée à un ensemble complexe de classes/sous-systÚmes.
Utilisation : APIs simplifiées, masquer la complexité interne, point d'entrée unifié.
Bénéfices :
- Réduction du couplage
- Interface plus simple
- Centralisation des interactions
Flyweight
But : Partage les objets pour économiser mémoire/performances.
Utilisation : Nombreuses instances d'objets similaires (caractĂšres, particules, tuiles).
Concepts clés :
- Ătat intrinsĂšque : partagĂ© entre instances
- Ătat extrinsĂšque : unique Ă chaque contexte
- Factory pour gérer les instances partagées
Proxy
But : Fournit un substitut qui contrÎle l'accÚs à un objet réel.
Utilisation : Lazy loading, contrÎle d'accÚs, cache, logging, réseaux.
Types de proxy :
- Virtual Proxy : création à la demande
- Protection Proxy : contrĂŽle d'accĂšs
- Remote Proxy : représentant local d'un objet distant
đč Comportement (Behavioral Patterns)
â
Observer
But : Notifie automatiquement une liste d'observateurs lors d'un changement d'état.
Utilisation : Interfaces utilisateur, événements systÚme, MVC, reactive programming.
Implémentation dans libftpp : La librairie fournit deux implémentations du pattern Observer :
- Observer classique : Interface générique pour implémenter vos propres observateurs
- ObservableValue : Template spécialisé pour observer les changements de valeurs
Avantages :
- Découplage entre sujet et observateurs
- Communication broadcast
- Support dynamique d'observateurs
- Notification automatique uniquement en cas de changement réel
Implémentation classique :
template <typename T>
public:
virtual void update(
const T& data) = 0;
};
class Subject {
std::vector<IObserver<int>*> observers;
public:
observers.erase(std::remove(observers.begin(), observers.end(), obs));
}
void notify(int data) {
for(
auto* obs : observers) obs->
update(data);
}
};
virtual ~IObserver()=default
ObservableValue (bonus) :
StatsDisplay display;
void subscribe(IObserver< TType > *observer)
â
Memento
But : Sauvegarde/restaure l'état interne d'un objet sans violer l'encapsulation.
Utilisation : Undo/Redo, snapshots, sauvegarde d'état, checkpoints.
Principe : Externalise l'état sans exposer la structure interne.
Structure :
- Originator : crée et utilise les mementos
- Memento : stocke l'état interne
- Caretaker : gĂšre les mementos sans les examiner
Implémentation dans libftpp :
friend class Originator;
private:
std::string state;
Memento(
const std::string& s) : state(s) {}
public:
};
class Originator {
std::string state;
public:
}
state = m.state;
}
void setState(const std::string& s) { state = s; }
};
Originator obj;
obj.setState("State1");
obj.setState("State2");
obj.restore(backup);
Avantages :
- Préserve l'encapsulation
- Simplifie l'implémentation d'undo/redo
- Sauvegarde sans exposer les détails internes
â
State Machine
But : Change le comportement d'un objet selon son état interne.
Utilisation : Parsers, protocoles réseau, jeux (IA), workflows, automates.
Avantages :
- Code plus maintenable pour les logiques d'état complexes
- Ălimination des conditions if/switch complexes
- Ătats explicites et transitions claires
- Comportement polymorphique selon l'état
Implémentation dans libftpp :
class State {
public:
virtual void enter() = 0;
virtual void execute() = 0;
virtual void exit() = 0;
virtual ~State() = default;
};
class IdleState : public State {
public:
void enter() override { std::cout << "Entering Idle\n"; }
void execute() override { }
void exit() override { std::cout << "Exiting Idle\n"; }
};
class RunningState : public State {
public:
void enter() override { std::cout << "Start running\n"; }
void execute() override { }
void exit() override { std::cout << "Stop running\n"; }
};
State* currentState;
public:
currentState->enter();
}
void transition(State* newState) {
currentState->exit();
currentState = newState;
currentState->enter();
}
currentState->execute();
}
};
IdleState idle;
RunningState running;
fsm.update();
fsm.transition(&running);
fsm.update();
State Machine Design Pattern.
Cas d'usage réels :
- IA de jeu (Idle, Patrol, Attack, Flee)
- Connexions réseau (Disconnected, Connecting, Connected)
- UI (Loading, Menu, Playing, Paused)
- Protocoles (Handshake, Data Transfer, Closing)
Chain of Responsibility
But : Passe une requĂȘte Ă travers une chaĂźne d'objets susceptibles de la traiter.
Utilisation : Validation, middleware, gestionnaires d'événements, parsers.
Avantages :
- Découplage émetteur/récepteur
- ChaĂźne configurable dynamiquement
- Responsabilité distribuée
Command
But : Encapsule une requĂȘte dans un objet (permet annulation, historique).
Utilisation : Undo/Redo, macros, queues de commandes, transactions.
Composants :
- Command : interface commune
- ConcreteCommand : implémentation spécifique
- Invoker : lance les commandes
- Receiver : exécute l'action
Interpreter
But : Définit une grammaire et un interprÚte pour exécuter des expressions.
Utilisation : Langages de script, expressions réguliÚres, calculatrices, DSL.
Structure :
- Grammaire en forme d'arbre syntaxique
- Chaque rĂšgle = une classe
- Méthode
interpret() pour l'évaluation
Iterator
But : AccÚs séquentiel aux éléments d'une collection sans exposer sa structure.
Utilisation : Parcours de conteneurs, algorithmes génériques, abstraction des structures.
Bénéfices :
- Interface uniforme pour différentes collections
- Plusieurs itérateurs simultanés
- Découplage algorithme/structure
Mediator
But : Centralise la communication entre plusieurs objets.
Utilisation : Interfaces complexes, réduction du couplage, coordination.
Exemple : ContrÎleur aérien coordonnant les avions sans qu'ils communiquent directement.
Strategy
But : Définit une famille d'algorithmes interchangeables dynamiquement.
Utilisation : Algorithmes de tri, compression, tarification, rendering.
Avantages :
- Ălimination des conditionnelles
- Algorithmes configurables à l'exécution
- Facilite l'ajout de nouveaux algorithmes
Template Method
But : Définit l'ossature d'un algorithme, laisse certaines étapes aux sous-classes.
Utilisation : Frameworks, algorithmes avec variantes, hooks personnalisables.
Structure :
class AbstractAlgorithm {
public:
void templateMethod() {
step1();
step2();
step3();
}
protected:
virtual void step2() = 0;
};
Visitor
But : Sépare un algorithme de la structure sur laquelle il opÚre.
Utilisation : AST, sérialiseurs, processeurs de données, compilateurs.
Avantages :
- Ajouter de nouvelles opérations sans modifier les classes
- Regroupement d'opérations liées
- Utilisation du double dispatch
đ§ Concepts C++ avancĂ©s
Templates variadiques
Permettent de passer un nombre variable d'arguments typés : Ellipsis: ... Si tu mets ... dans une fonction comme ceci:
template <typename... TArgs>
void func(TArgs... args) {
(std::cout << ... << args) << std::endl;
}
func(1, "hello", 3.14);
Cela signifie que tu peux passer n'importe quel nombre d'arguments de n'importe quel type.
Gestion des références et std::forward
T& : référence lvalue uniquement
const T& : référence constante (lvalues + rvalues)
T&& : rvalue reference ou forwarding reference (en template)
std::forward<T>(arg) : préserve la nature de l'argument
Pourquoi std::forward ?
- Ăvite les copies inutiles
- Préserve les optimisations de déplacement (move semantics)
- Permet le perfect forwarding
template <typename... TArgs>
TType* acquire(TArgs&&... args) {
void* memory = pool.allocate();
return new (memory) TType(std::forward<TArgs>(args)...);
}
std::aligned_storage
Template C++11 pour pré-allouer de la mémoire correctement alignée mais deprecated en c++23:
std::aligned_storage<sizeof(T), alignof(T)>::type storage;
T* ptr = reinterpret_cast<T*>(&storage);
new (ptr) T(args...);
Utilité :
- Object pools
- Containers personnalisés
- Ăviter les allocations dynamiques
Default Noexcept Delete
Indique qu'une fonction ne lance pas d'exception, permettant des optimisations.
Pour les constructeurs/destructeurs qui ne font rien dedans on peut les indiquer = default
class MyClass {
public:
MyClass() = default;
~MyClass() = default;
};
Pour les constructeurs/destructeurs, noexcept est souvent implicite.
Si une methode ne doit jamais etre possible on peut aussi place r = delete
class NonCopyable {
public:
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
Liste des exceptions des plus courantes
std::exception
âââ std::logic_error
â âââ std::invalid_argument
â âââ std::domain_error
â âââ std::length_error
â âââ std::out_of_range
âââ std::runtime_error
âââ std::range_error
âââ std::overflow_error
âââ std::underflow_error
đ§Ș Tests
Les tests utilisent GoogleTest (téléchargé automatiquement via CMake) et couvrent l'intégralité des composants de la librairie.
Exécution des tests
# Compiler les tests
make gtest
# Lancer tous les tests GoogleTest
make run-gtest
# Sortie attendue
[==========] Running X tests from Y test suites.
[----------] Global test environment set-up.
...
[----------] Global test environment tear-down
[==========] X tests from Y test suites ran.
[ PASSED ] X tests.
Couverture des tests
Structures de données :
- â
test_data_buffer.cpp - Sérialisation/désérialisation
- â
test_pool.cpp - Allocation/libération mémoire
- â
test_ring_buffer.cpp - Buffer circulaire
Design Patterns :
- â
test_memento.cpp - Sauvegarde/restauration d'état
- â
test_observer.cpp - Pattern Observer classique
- â
test_singleton.cpp - Instance unique
- â
test_state_machine.cpp - Machine à états finis
Threading :
- â
test_thread.cpp - Wrapper de threads
- â
test_thread_safe_queue.cpp - Queue thread-safe
- â
test_thread_safe_iostream.cpp - IO thread-safe
- â
test_worker_pool.cpp - Pool de workers
- â
test_persistent_worker.cpp - Worker persistant
Réseau :
- â
test_message.cpp - SystĂšme de messages
Mathématiques :
- â
test_ivector2.cpp - Vecteurs 2D
- â
test_ivector3.cpp - Vecteurs 3D
- â
test_perlin_noise.cpp - Génération de bruit
- â
test_random_2D_coordinate_generator.cpp - Génération aléatoire
Bonus :
- â
test_logger.cpp - SystĂšme de logging
- â
test_chronometre.cpp - Mesure de temps
- â
test_n_ary_tree.cpp - Arbre n-aire
- â
test_observable_value.cpp - Valeurs observables
Types de tests couverts
Fonctionnalités de base :
- Constructeurs/destructeurs
- Opérations CRUD (Create, Read, Update, Delete)
- Sérialisation/désérialisation
Cas limites :
- Buffers vides/pleins
- Valeurs nulles/invalides
- Dépassements de capacité
Thread safety :
- AccĂšs concurrent
- Conditions de course
- Deadlocks potentiels
Performance :
- Complexité temporelle
- Fuites mémoire (via valgrind)
- Allocations inutiles
Gestion d'erreurs :
- Exceptions attendues
- Ătats invalides
- Ressources épuisées
Structure des tests
tests/ âââ my_google_test/ # Tests GoogleTest (utilisĂ©s) â âââ main.cpp # Point d'entrĂ©e des tests â âââ test_*.cpp # Suites de tests par composant â âââ ... âââ school_test/ # Tests manuels (non utilisĂ©s par make) âââ main_*.cpp # Programmes de test individuels âââ ...
Exemple de test
#include <gtest/gtest.h>
#include "libftpp.hpp"
TEST(DataBufferTest, SerializeAndDeserialize) {
buffer << 42 << 3.14f << std::string("test");
int i;
float f;
std::string s;
buffer >> i >> f >> s;
EXPECT_EQ(i, 42);
EXPECT_FLOAT_EQ(f, 3.14f);
EXPECT_EQ(s, "test");
}