Implementación std::esperada de encabezado único usando C++20.
La biblioteca implementa estas 2 propuestas:
Esta implementación proporciona lo esperado en el espacio de nombres rd. Entonces, para cualquier uso de std::expected, use rd::expected.
std::expected es un mecanismo para el manejo de errores. Esto es muy similar al std::result de Rust. std::expected básicamente contiene uno de Resultado o Error.
Probablemente haya 2 razones para no utilizar "siempre" excepciones:
Al escribir cualquier biblioteca, esto plantea un gran problema para el manejo de errores. En la biblioteca, si elegimos lanzar una excepción en una condición de error, estamos tomando la decisión para el usuario de la biblioteca. La condición de error en la biblioteca puede ser o no una situación excepcional en el contexto de la aplicación que utiliza esa biblioteca.
std::expected proporciona una alternativa estándar para excepción que se puede utilizar para condiciones de error que no son excepcionales en el contexto de la aplicación.
Dicho esto, cabe señalar que las excepciones son excelentes para condiciones de pánico. Y existen muchas condiciones de pánico obvias, para las cuales sólo se deben utilizar excepciones.
error_code solía ser el mecanismo de manejo de errores más simple. Históricamente, error_code es un número entero simple, lo que no es bueno para un contexto de error rico.
Hoy en día, el encabezado de C++ <system_error> proporciona un código de error más completo. Sin embargo, error_code conduce a la monopolización del canal de retorno. Y, por lo tanto, el valor debe emitirse con algún otro canal.
También te obliga a escribir la condición if después de cada llamada a función.
std::expected garantiza que nunca estará vacío. es decir, contendría uno de valor o error en cualquier momento. Esto hace que sea realmente fácil de usar.
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 no pueden ser un tipo de referencia, ya que tampoco se admiten en papel.
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template < class U >
using rebind = expected<U, error_type>;
Constructor predeterminado:
constexpr expected ();
Copiar constructor:
constexpr expected (expected const &);
Mover constructor:
constexpr expected (expected &&);
Constructor esperado:
U debería ser convertible a T y G debería ser convertible a E.
template < class U , class G >
constexpr expected (expected<U, G> const &);
template < class U , class G >
constexpr expected (expected<U, G> &&);
Constructor de valores:
Constructor para <T , E> esperado que acepta valor para inicializar T (parte del valor esperado).
template < class U = T>
constexpr expected (U&& v);
Constructor inesperado:
Constructor para esperado<T, E> que acepta rd::instancia inesperada para inicializar E (parte de error de lo esperado).
template < class G >
constexpr expected (unexpected<G> const &);
template < class G >
constexpr expected (unexpected<G> &&);
constructor in_place_t:
Constructor para esperado<T, E> que acepta etiquetas in_place y argumentos (a reenviar) para inicializar 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);
constructor inesperado_t:
Constructor para esperado<T, E> que acepta rd::etiqueta inesperada y argumentos (a reenviar) para inicializar 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);
tarea de copia:
constexpr expected& operator =(expected const &);
asignación de movimiento:
constexpr expected& operator =(expected &&);
Asignación de valor:
Asigna valor a lo esperado
template < class U = T>
constexpr expected& operator =(U&& rhs);
rd::asignación inesperada:
Asigna error a lo esperado
template < class G >
constexpr expected& operator =(unexpected<G> const &);
template < class G >
constexpr expected& operator =(unexpected<G> &&);
emplazar:
Acepta argumentos para el constructor nuevo valor esperado y se lo asigna.
template < class ... Args>
constexpr T& emplace (Args&&... args);
template < class U , class ... Args>
constexpr T& emplace (std::initializer_list<U>, Args&&...);
intercambiar función de miembro y función de amigo
// 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) &&;
y luego:
F debe ser invocable con el tipo de valor esperado y debe devolver lo esperado cuyo tipo de error debe ser E. Devuelve el error esperado actual; si hay un error, otro devuelve el resultado de invocar f con el valor.
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 &&;
o_else:
F debe ser invocable con el tipo de error esperado y debe devolver lo esperado cuyo tipo de valor debe ser T.
Si invoke_result_t<F, E>
es cualquier especialización esperada (cuyo tipo de valor debe ser el mismo que T), entonces, si hay un valor allí, el valor se devuelve envuelto en invoke_result_t de F; de lo contrario, devuelve el resultado de invocar f.
Si invoke_result_t<F, E>
es nulo, entonces, si hay un error, entonces se invoca F con error. La corriente esperada se devuelve como resultado.
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 &&;
transformar:
F debe ser invocable con el tipo de valor actual esperado. Si lo esperado actual contiene un error, lo esperado resultante contiene ese error; de lo contrario, contiene el resultado de la invocación del valor esperado actual con 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 &&;
transformar_error:
F debe ser invocable con el tipo de error actual esperado. Si el valor esperado actual contiene el valor, el valor esperado resultante contiene ese valor; de lo contrario, contiene el resultado de la invocación del error esperado actual con 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
Copiar constructor:
constexpr unexpected (unexpected const &);
Mover constructor:
constexpr unexpected (unexpected&&);
constructor in_place_t:
Acepta una etiqueta std::in_place y construye T con argumentos.
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);
constructor de valores:
Construye T con 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 deriva de una excepción. Almacena el valor del error.
template < class E >
class bad_expected_access ;
E& error () & noexcept ;
E const & error () const & noexcept ;
E&& error() && noexcept ;
E const && error() const && noexcept ;
Este es solo un tipo de etiqueta, para indicar un error de construcción esperado.
La biblioteca también expone una variable inesperada de tipo rd::unexpect_t.
inline constexpr unexpect_t unexpect{};