En-tête unique std :: implémentation attendue en utilisant C++20.
La bibliothèque met en œuvre ces 2 propositions :
Cette implémentation fournit ce qui est attendu dans l'espace de noms rd. Ainsi, pour toute utilisation de std::expected, utilisez rd::expected.
std::expected est un mécanisme de gestion des erreurs. Ceci est très similaire au std::result de rust. std::expected contient essentiellement un résultat ou une erreur.
Il y a probablement 2 raisons pour ne pas "toujours" utiliser les exceptions :
Lors de l'écriture d'une bibliothèque, cela pose un gros problème de gestion des erreurs. Dans la bibliothèque, si nous choisissons de lever une exception en cas d'erreur, nous faisons le choix pour l'utilisateur de la bibliothèque. Une condition d'erreur dans la bibliothèque peut ou non être une situation exceptionnelle dans le contexte de l'application qui utilise cette bibliothèque.
std::expected fournit une alternative standard pour les exceptions qui peut être utilisée pour les conditions d'erreur qui ne sont pas exceptionnelles dans le contexte de l'application.
Tout en disant cela, il convient de noter que les exceptions sont idéales en cas de conditions de panique. Et il existe de nombreuses situations de panique évidentes, pour lesquelles seules des exceptions devraient être utilisées.
error_code était le mécanisme de gestion des erreurs le plus simple. Historiquement, error_code est un entier simple, ce qui n'est pas adapté à un contexte d'erreur riche.
De nos jours, l'en-tête C++ <system_error> fournit un code d'erreur plus riche. Cependant, error_code conduit à la monopolisation du canal de retour. Et donc la valeur doit être émise avec un autre canal.
Cela vous oblige également à écrire une condition if après chaque appel de fonction.
std::expected garantit qu'il ne sera jamais vide. c'est-à-dire qu'il contiendrait une valeur ou une erreur à tout moment. Cela le rend vraiment facile à utiliser.
auto get_file_handle (std::string_view path) -> rd::expected<file, fail_reason>;
auto read_file (file f) -> rd::expected<std::string, fail_reason>;
auto to_int (std::string str) -> rd::expected<int, fail_reason>;
auto increment ( int x) -> int;
auto read_int_with_increment (std::string_view path) -> rd::expected<int, fail_reason>{
return get_file_handle (path)
. and_then (read_file)
. and_then (to_int)
. transform (increment);
}
template < typename T, typename E>
class expected
T, E ne peuvent pas être un type de référence, car ils ne sont pas non plus pris en charge sur papier.
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template < class U >
using rebind = expected<U, error_type>;
Constructeur par défaut :
constexpr expected ();
Constructeur de copie :
constexpr expected (expected const &);
Déplacer le constructeur :
constexpr expected (expected &&);
Constructeur attendu :
U devrait être convertible en T et G devrait être convertible en E.
template < class U , class G >
constexpr expected (expected<U, G> const &);
template < class U , class G >
constexpr expected (expected<U, G> &&);
Constructeur de valeur :
Constructeur pour <T , E> attendu qui accepte la valeur pour initialiser T (valeur faisant partie de l'attendu).
template < class U = T>
constexpr expected (U&& v);
Constructeur inattendu :
Constructeur pour attendu<T, E> qui accepte l'instance rd::unexpected pour initialiser E (partie erreur de attendu).
template < class G >
constexpr expected (unexpected<G> const &);
template < class G >
constexpr expected (unexpected<G> &&);
Constructeur in_place_t :
Constructeur pour Expected<T, E> qui accepte la balise in_place et les arguments (à transmettre) pour initialiser T.
template < class ... Args>
constexpr expected (std:: in_place_t , Args&&... args);
template < class U , class ... Args>
constexpr expected (std:: in_place_t , std::initializer_list<U>, Args&&... args);
constructeur inattendu_t :
Constructeur pour attendu<T, E> qui accepte la balise rd::unexpect et les arguments (à transmettre) pour initialiser E.
template < class ... Args>
constexpr expected (rd:: unexpect_t , Args&&... args);
template < class G , class ... Args>
constexpr expected (rd:: unexpect_t , std::initializer_list<G>, Args&&... args);
devoir de copie :
constexpr expected& operator =(expected const &);
affectation de déplacement :
constexpr expected& operator =(expected &&);
Affectation de valeur :
Attribue une valeur à attendu
template < class U = T>
constexpr expected& operator =(U&& rhs);
rd::affectation inattendue :
Attribue l'erreur à attendue
template < class G >
constexpr expected& operator =(unexpected<G> const &);
template < class G >
constexpr expected& operator =(unexpected<G> &&);
mettre en place :
Accepte les arguments du constructeur pour la nouvelle valeur attendue et lui attribue.
template < class ... Args>
constexpr T& emplace (Args&&... args);
template < class U , class ... Args>
constexpr T& emplace (std::initializer_list<U>, Args&&...);
échanger la fonction membre et la fonction ami
// For accessing T's members
// precondition: has_value() == true
constexpr T const * operator ->() const noexcept ;
// precondition: has_value() == true
constexpr T* operator ->() noexcept ;
// Getting reference to T
// precondition: has_value() == true
constexpr T const & operator *() const & noexcept ;
// precondition: has_value() == true
constexpr T& operator *() & noexcept ;
// precondition: has_value() == true
constexpr T&& operator *() && noexcept ;
// precondition: has_value() == true
constexpr T const && operator *() const && noexcept ;
// Query if value exists
constexpr explicit operator bool () const noexcept ;
constexpr bool has_value () const noexcept ;
// Get value, if not exists throws exception rd::bad_expected_access(error())
constexpr T const & value () const &;
constexpr T& value () &;
constexpr T&& value() &&;
constexpr T const && value() const &&;
// Get error, (undefined behavior if error not exists)
constexpr E const & error () const &;
constexpr E& error () &;
constexpr E&& error() &&;
constexpr E const && error() const &&;
// Get the value, if value doesn't exist return v
template < class U >
constexpr T value_or (U&& v) const &;
template < class U >
constexpr T value_or (U&& v) &&;
et puis:
F doit être invocable avec le type de valeur attendu et doit renvoyer attendu dont le type d'erreur doit être E. Il renvoie l'erreur du courant attendu, si l'erreur est présente, autre renvoie le résultat de l'invocation de f avec la valeur.
template < class F >
constexpr auto and_then (F&& f) &;
template < class F >
constexpr auto and_then (F&& f) &&;
template < class F >
constexpr auto and_then (F&& f) const &;
template < class F >
constexpr auto and_then (F&& f) const &&;
ou bien :
F doit être invocable avec le type d’erreur attendu et doit renvoyer attendu dont le type de valeur doit être T.
Si invoke_result_t<F, E>
est une spécialisation attendue (dont le type de valeur doit être le même que T), alors si une valeur est présente, la valeur est renvoyée enveloppée dans Invoquer_result_t de F, sinon renvoie le résultat de l'appel de f.
Si invoke_result_t<F, E>
est nul, alors, si une erreur est présente, alors F est invoqué avec une erreur. Le courant attendu est renvoyé comme résultat.
template < class F >
constexpr auto or_else (F&& f) &;
template < class F >
constexpr auto or_else (F&& f) &&;
template < class F >
constexpr auto or_else (F&& f) const &;
template < class F >
constexpr auto or_else (F&& f) const &&;
transformer:
F doit être invocable avec le type de valeur du courant attendu. Si le courant attendu contient une erreur, le résultat attendu contient cette erreur, sinon il contient le résultat de l'invocation de la valeur attendue actuelle avec F.
template < class F >
constexpr auto transform (F&& f) &;
template < class F >
constexpr auto transform (F&& f) &&;
template < class F >
constexpr auto transform (F&& f) const &;
template < class F >
constexpr auto transform (F&& f) const &&;
erreur_de transformation :
F doit être invocable avec le type d’erreur de courant attendu. Si le courant attendu contient une valeur, le résultat attendu contient cette valeur, sinon il contient le résultat de l'invocation de l'erreur attendue actuelle avec F.
template < class F >
constexpr auto transform_error (F&& f) &;
template < class F >
constexpr auto transform_error (F&& f) &&;
template < class F >
constexpr auto transform_error (F&& f) const &;
template < class F >
constexpr auto transform_error (F&& f) const &&;
template < class U , class G >
constexpr friend bool operator ==(expected const &, expected<U, G> const &);
template < class U >
constexpr friend bool operator ==(expected const &, U const &);
template < class G >
constexpr friend bool operator ==(expected const &, rd::unexpected<G> const &);
template < typename T>
class unexpected
using value_type = T
Constructeur de copie :
constexpr unexpected (unexpected const &);
Déplacer le constructeur :
constexpr unexpected (unexpected&&);
Constructeur in_place_t :
Il accepte une balise std::in_place et construit T avec args.
template < typename ...Args>
constexpr unexpected (std:: in_place_t , Args&&... args);
template < typename U, typename ...Args>
constexpr unexpected (std:: in_place_t , std::initializer_list<U>, Args&&... args);
constructeur de valeur :
Construit T avec err.
template < typename Err>
constexpr unexpected (Err&& err);
// value(): gets the stored T value
constexpr T & value () &;
constexpr T const & value () const &;
constexpr T && value() &&;
constexpr T const && value() const &&;
template < class E2 >
friend constexpr bool operator ==(unexpected const & x, unexpected< E2 > const & y);
bad_expected_access dérive d’une exception. Stocke la valeur d'erreur.
template < class E >
class bad_expected_access ;
E& error () & noexcept ;
E const & error () const & noexcept ;
E&& error() && noexcept ;
E const && error() const && noexcept ;
Il s'agit simplement d'un type de balise, pour signifier une erreur de construction attendue.
La bibliothèque expose également une variable inattendue de type rd::unexpect_t.
inline constexpr unexpect_t unexpect{};