Libftpp
A modern C++ library
Libftpp Documentation

Libftpp Banner

Libftpp 📚

C++17 Documentation GoogleTest

👀 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

make

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 :

Pool<MyClass> pool(100); // Pré-alloue 100 objets
auto* obj = pool.acquire(arg1, arg2); // Construction avec arguments
pool.release(obj); // Libération pour réutilisation
Pool of reusable memory.
Definition: pool.hpp:37

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 :

DataBuffer buffer;
// Sérialisation
buffer << 42 << 3.14f << std::string("Hello");
// Désérialisation (ordre important!)
int value;
float pi;
std::string text;
buffer >> value >> pi >> text;
// AccĂšs bas niveau
buffer.appendBytes(data, size);
auto raw = buffer.getBytes();
A simple LIFO data buffer for serialization and deserialization for simple data types and std::string...
Definition: data_buffer.hpp:37

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 :

Thread worker([]() {
// Code exécuté dans le thread
processData();
});
worker.join(); // Attendre la fin
Simple Thread Wrapper.
Definition: thread.hpp:36

🔐 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 :

// Thread producteur
queue.push(task);
// Thread consommateur
Task task = queue.pop(); // Bloque si vide
Thread-Safe Queue.

đŸ–šïž 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 :

ThreadSafeIOStream tsout(std::cout);
// Dans différents threads
tsout << "Thread 1: " << data << std::endl;
tsout << "Thread 2: " << other << std::endl;
// Garantit que chaque ligne est atomique
Thread-Safe I/O Stream.

⚙ 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 :

// Ajouter des tùches qui s'exécutent en boucle
worker.addTask("heartbeat", []() {
sendHeartbeat();
});
worker.addTask("monitor", []() {
checkSystemHealth();
});
// Supprimer une tĂąche
worker.removeTask("heartbeat");
// ArrĂȘt automatique Ă  la destruction
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;
// Thread producteur
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // Réveille un thread en attente
// Thread consommateur
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; }); // Attend que ready soit true
}

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 :

// Condition pour réveiller les workers
cv.wait(lock, [this] { return stop || !jobs.empty(); });
// Se rĂ©veille quand il y a du travail OU qu'on demande l'arrĂȘt

🏭 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 :

WorkerPool pool(4); // 4 threads workers
pool.addJob([]() {
// Votre tĂąche ici
processData();
});
// Le destructeur attend que tous les jobs se terminent
Worker Pool for Concurrent Job Execution.
Definition: worker_pool.hpp:44

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 :

Message msg(MESSAGE_LOGIN);
msg << userId << username << password;
// CÎté réception
int userId;
string username, password;
msg >> userId >> username >> password;
Class representing a structured message for network communication.
Definition: message.hpp:47

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 :

Server server;
server.start(8080);
// Définir une action pour un type de message
server.defineAction(MSG_CHAT, [](long long clientId, const Message& msg) {
std::string content;
msg >> content;
// Broadcast Ă  tous les clients
server.sendToAll(createChatMessage(content));
});
server.update(); // Traite les événements réseau
Basic TCP server class using POSIX sockets and select() for multi-client communication.
Definition: server.hpp:62
void defineAction(const Message::Type &messageType, const std::function< void(long long &clientID, const Message &msg)> &action)
Definition: server.cpp:163
void sendToAll(const Message &message)
Definition: server.cpp:154
void start(const size_t &port=0)
Definition: server.cpp:12
void update()
Definition: server.cpp:170

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;
});
Message loginMsg(MSG_LOGIN);
loginMsg << "username" << "password";
client.send(loginMsg);
client.update(); // Traite les messages entrants
Basic TCP client class using POSIX sockets and select() for network communication.
Definition: client.hpp:63

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>
class IVector2 {
T x, y;
public:
IVector2(T x = 0, T y = 0);
IVector2 operator+(const IVector2& other) const;
IVector2 operator-(const IVector2& other) const;
IVector2 operator*(T scalar) const;
T dot(const IVector2& other) const;
T length() const;
IVector2 normalized() const;
T distance(const IVector2& other) const;
};
IVector2 operator*(const IVector2 &other) const
Definition: ivector2.hpp:26
IVector2 operator-(const IVector2 &other) const
Definition: ivector2.hpp:21
TType x
Definition: ivector2.hpp:10
float length() const
Definition: ivector2.hpp:62
IVector2 operator+(const IVector2 &other) const
Definition: ivector2.hpp:16
TType y
Definition: ivector2.hpp:11
IVector2()
Definition: ivector2.hpp:13
float dot()
Definition: ivector2.hpp:77

IVector3 - Vecteur 3D :

template <typename T>
class IVector3 {
T x, y, z;
public:
IVector3(T x = 0, T y = 0, T z = 0);
IVector3 operator+(const IVector3& other) const;
IVector3 cross(const IVector3& other) const; // Produit vectoriel
T dot(const IVector3& other) const;
T length() const;
IVector3 normalized() const;
};
TType z
Definition: ivector3.hpp:12
IVector3 operator+(const IVector3 &other) const
Definition: ivector3.hpp:17
float length() const
Definition: ivector3.hpp:66
float dot()
Definition: ivector3.hpp:82
IVector3 cross(const IVector3 &other) const
Definition: ivector3.hpp:99
TType y
Definition: ivector3.hpp:11
TType x
Definition: ivector3.hpp:10
IVector3()
Definition: ivector3.hpp:14

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 :

PerlinNoise2D perlin(seed);
// Générer une heightmap pour un terrain
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float noise = perlin.noise(x * 0.1f, y * 0.1f);
// noise est entre -1.0 et 1.0
heightmap[y][x] = (noise + 1.0f) * 0.5f; // Normaliser Ă  [0, 1]
}
}
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 :

// Générer des positions de spawning
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 :

Chronometre chrono;
chrono.start();
// Code Ă  mesurer
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...
Definition: chronometre.hpp:27
void start()
Start the chronometre.
Definition: chronometre.cpp:17

📝 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 :

// Configuration
// Logging
Logger::instance().log(LogLevel::INFO, "Application started");
Logger::instance().log(LogLevel::ERROR, "Connection failed");
// Console uniquement
void setLogLevel(LogLevel level)
Definition: logger.cpp:31
void logConsole(LogLevel level, const std::string &message)
Log a message to the console if the log level is sufficient.
Definition: logger.cpp:127
void log(LogLevel level, const std::string &message)
Log a message to the log file if the log level is sufficient.
Definition: logger.cpp:113
static Logger & instance()
Definition: logger.cpp:25
void setOutputFile(const std::string &filename)
Definition: logger.cpp:36

🔄 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 :

RingBuffer<int> buffer(1000); // Capacité de 1000 éléments
buffer.push(42);
int value = buffer.pop();
if (buffer.isFull()) {
// Les nouvelles données écrasent les anciennes
}
RingBuffer class for managing a circular buffer of bytes. It provides methods to push and pop bytes,...
Definition: ring_buffer.hpp:43

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.addChild(child1, "Grand Child");
// Parcours
tree.traverseDepthFirst([](const std::string& data) {
std::cout << data << std::endl;
});
N ary Tree class.
Definition: n_ary_tree.hpp:91
Node< TType > * addChild(Node< TType > *parent, const TType &value)
Definition: n_ary_tree.hpp:109

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>
class IObserver {
public:
virtual void update(const T& newValue) = 0;
};
virtual void update(const TType &newValue)=0

Utilisation :

class MyObserver : public IObserver<int> {
public:
void update(const int& value) override {
std::cout << "Nouvelle valeur: " << value << std::endl;
}
};
MyObserver observer;
health.subscribe(&observer);
health.set(95); // Déclenche observer.update(95)
health = 90; // Via operator=, déclenche aussi
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.

db.instantiate("localhost", 5432);
auto* manager = db.instance();
Singleton Design Pattern.
Definition: singleton.hpp:38
static TType * instance()
Definition: singleton.hpp:47
static void instantiate(TArgs... p_args)
Definition: singleton.hpp:56

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 :

  1. Observer classique : Interface générique pour implémenter vos propres observateurs
  2. 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 :

// Interface observer
template <typename T>
class IObserver {
public:
virtual void update(const T& data) = 0;
virtual ~IObserver() = default;
};
// Sujet observable
class Subject {
std::vector<IObserver<int>*> observers;
public:
void attach(IObserver<int>* obs) { observers.push_back(obs); }
void detach(IObserver<int>* obs) {
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;
stats.subscribe(&display);
stats.set(newStats); // Notifie automatiquement display
void set(TType newValue)
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 :

class Memento {
friend class Originator;
private:
std::string state;
Memento(const std::string& s) : state(s) {}
public:
// Pas d'accÚs direct à l'état
};
class Originator {
std::string state;
public:
Memento save() {
return Memento(state);
}
void restore(const Memento& m) {
state = m.state;
}
void setState(const std::string& s) { state = s; }
};
// Utilisation
Originator obj;
obj.setState("State1");
Memento backup = obj.save();
obj.setState("State2");
obj.restore(backup); // Retour Ă  State1
Memento Design Pattern.
Definition: memento.hpp:49

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 :

// Interface d'état
class State {
public:
virtual void enter() = 0;
virtual void execute() = 0;
virtual void exit() = 0;
virtual ~State() = default;
};
// États concrets
class IdleState : public State {
public:
void enter() override { std::cout << "Entering Idle\n"; }
void execute() override { /* Comportement idle */ }
void exit() override { std::cout << "Exiting Idle\n"; }
};
class RunningState : public State {
public:
void enter() override { std::cout << "Start running\n"; }
void execute() override { /* Comportement running */ }
void exit() override { std::cout << "Stop running\n"; }
};
// Machine à états
class StateMachine {
State* currentState;
public:
StateMachine(State* initial) : currentState(initial) {
currentState->enter();
}
void transition(State* newState) {
currentState->exit();
currentState = newState;
currentState->enter();
}
void update() {
currentState->execute();
}
};
// Usage
IdleState idle;
RunningState running;
StateMachine fsm(&idle);
fsm.update(); // Execute idle behavior
fsm.transition(&running); // Change to running
fsm.update(); // Execute running behavior
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(); // implémentée par les sous-classes
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; // C++17 fold expression
}
// Usage
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...); // Placement new

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.

void func() noexcept; // Garantie de ne pas lancer d'exception

Pour les constructeurs/destructeurs qui ne font rien dedans on peut les indiquer = default

class MyClass {
public:
MyClass() = default; // Constructeur par défaut sans exception
~MyClass() = default; // Destructeur par défaut sans exception
};

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; // Interdit la copie
NonCopyable& operator=(const NonCopyable&) = delete; // Interdit la copie
};

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) {
DataBuffer buffer;
// Sérialisation
buffer << 42 << 3.14f << std::string("test");
// Désérialisation
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");
}