La gestion des erreurs est un aspect important de la programmation, et différents langages apportent des solutions variées pour la rendre plus sûre et plus expressive. Du côté de Rust, nous pouvons utiliser Result
. En C++23, le type std::expected
a été introduit pour améliorer la gestion des erreurs en fournissant une alternative aux exceptions. Cet article explorera comment utiliser std::expected
.
std::expected en C++23
Le type std::expected
représente une valeur qui peut contenir soit un résultat réussi, soit une erreur. Il est défini dans l’en-tête <expected>
et peut être utilisé pour simplifier la gestion des erreurs sans utiliser les exceptions.
Voici une fonction qui lit un fichier et renvoie soit le contenu du fichier sous forme de chaîne, soit une erreur.
#include <expected> #include <fstream> #include <iostream> #include <string> std::expected<std::string, std::string> readFile(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { return std::unexpected("Could not open file"); } std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); if (file.fail()) { return std::unexpected("Error reading file"); } return content; } int main() { auto result = readFile("example.txt"); if (result) { std::cout << "File content:\n" << result.value() << std::endl; } else { std::cerr << "Error: " << result.error() << std::endl; } return 0; }
Dans cet exemple, la fonction readFile
retourne un std::expected<std::string, std::string>
. Si la lecture du fichier réussit, le contenu est renvoyé. En cas d’échec, une chaîne décrivant l’erreur est renvoyée.
Détails de l’utilisation de std::expected
- Création et Utilisation
std::expected<T, E>
oùT
est le type de la valeur attendue etE
est le type de l’erreur.- Retournez directement une valeur
T
valide ou utilisezstd::unexpected
pour retourner une erreur.
- Accès aux Valeurs
value()
: Récupère la valeur si elle est présente, lève une exception sinon.error()
: Récupère l’erreur si elle est présente, undefined behavior sinon.- Méthodes utilitaires:
has_value()
,operator bool()
pour vérifier la présence d’une valeur avec un simple if comme dans l’exemple.
- Propagation des Erreurs
- Similaire aux exceptions, mais de manière explicite. Les fonctions peuvent retourner
std::expected
pour propager les erreurs.
- Similaire aux exceptions, mais de manière explicite. Les fonctions peuvent retourner
Avantages de std::expected
- Lisibilité et Simplicité: Le code devient plus lisible car la gestion des erreurs est explicite.
- Pas d’Exception: Évite les problèmes liés aux exceptions, notamment le coût de la gestion des exceptions et les complications des contrôles d’exceptions.
- Retour Multiple: Permet de retourner soit une valeur réussie soit une erreur en une seule structure.
Comparaison avec les Méthodes Traditionnelles de Gestion des Erreurs en C++
Avant std::expected
, la gestion des erreurs en C++ se faisait principalement via des exceptions ou des codes de retour.
- Exceptions
- Avantages: Séparation claire du code de gestion des erreurs et du code fonctionnel.
- Inconvénients: Potentiel de complexité accrue, coût en performance, et gestion difficile des ressources.
- Codes de retour
- Avantages: Simplicité et absence de surcharge.
- Inconvénients: Problèmes de lisibilité et de maintenabilité, car la vérification des erreurs peut être négligée.
Avec Exceptions:
#include <fstream> #include <iostream> #include <stdexcept> #include <string> std::string readFileWithException(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { throw std::runtime_error("Could not open file"); } std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); if (file.fail()) { throw std::runtime_error("Error reading file"); } return content; } int main() { try { std::string content = readFileWithException("example.txt"); std::cout << "File content:\n" << content << std::endl; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; }
Avec un code de retour
#include <fstream> #include <iostream> #include <string> // Codes de retour enum class ErrorCode { SUCCESS, FILE_NOT_FOUND, READ_ERROR }; ErrorCode readFile(const std::string& filename, std::string& content) { std::ifstream file(filename); if (!file.is_open()) { return ErrorCode::FILE_NOT_FOUND; } content.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); if (file.fail()) { return ErrorCode::READ_ERROR; } return ErrorCode::SUCCESS; } int main() { std::string content; ErrorCode result = readFile("example.txt", content); switch (result) { case ErrorCode::SUCCESS: std::cout << "File content:\n" << content << std::endl; break; case ErrorCode::FILE_NOT_FOUND: std::cerr << "Error: Could not open file" << std::endl; break; case ErrorCode::READ_ERROR: std::cerr << "Error: Error reading file" << std::endl; break; } return 0; }
Conclusion
L’introduction de std::expected
en C++23 représente une amélioration significative pour les développeurs cherchant à écrire du code plus sûr, lisible et maintenable. En offrant une alternative aux exceptions pour la gestion des erreurs, std::expected
permet un contrôle explicite et prévisible, rendant le code plus robuste et plus facile à comprendre.
Adopter std::expected
dans vos projets peut réduire les bugs liés à une mauvaise gestion des erreurs et simplifier le flux de votre code, tout en améliorant la qualité globale de votre codebase C++.