Implementação std::expected de cabeçalho único usando C++20.
A biblioteca implementa estas 2 propostas:
Esta implementação fornece o esperado no namespace rd. Portanto, para qualquer uso de std::expected, use rd::expected.
std::expected é um mecanismo para tratamento de erros. Isso é muito semelhante ao std::result da ferrugem. std::expected basicamente contém um Resultado ou Erro.
Provavelmente existem 2 razões para não "sempre" usar exceções:
Ao escrever qualquer biblioteca, isso representa um grande problema para o tratamento de erros. Na biblioteca, se optarmos por lançar uma exceção em condição de erro, estaremos fazendo a escolha pelo usuário da biblioteca. A condição de erro na biblioteca pode ou não ser uma situação excepcional no contexto da aplicação que utiliza essa biblioteca.
std::expected fornece uma alternativa padrão para exceção que pode ser usada para condições de erro que não são excepcionais no contexto da aplicação.
Ao dizer isso, deve-se notar que as exceções são ótimas para condições de pânico. E existem muitas condições óbvias de pânico, para as quais apenas devem ser utilizadas excepções.
error_code costumava ser o mecanismo de tratamento de erros mais simples. Historicamente, error_code é um número inteiro simples, que não é bom para um contexto de erro rico.
Hoje em dia, o cabeçalho C++ <system_error> fornece um código de erro mais rico. No entanto, error_code leva à monopolização do canal de retorno. E, portanto, o valor precisa ser enviado para algum outro canal.
Também força você a escrever a condição if após cada chamada de função.
std::expected garante que nunca ficará vazio. ou seja, conteria valor ou erro a qualquer momento. Isso torna muito 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 não podem ser um tipo de referência, pois também não são suportados em papel.
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template < class U >
using rebind = expected<U, error_type>;
Construtor padrão:
constexpr expected ();
Copiar construtor:
constexpr expected (expected const &);
Mover construtor:
constexpr expected (expected &&);
Construtor esperado:
U deve ser conversível em T e G deve ser conversível em E.
template < class U , class G >
constexpr expected (expected<U, G> const &);
template < class U , class G >
constexpr expected (expected<U, G> &&);
Construtor de valor:
Construtor para esperado <T , E> que aceita valor para inicializar T (valor parte do esperado).
template < class U = T>
constexpr expected (U&& v);
Construtor inesperado:
Construtor para esperado<T, E> que aceita instância rd::unexpected para inicializar E (parte de erro do esperado).
template < class G >
constexpr expected (unexpected<G> const &);
template < class G >
constexpr expected (unexpected<G> &&);
Construtor in_place_t:
Construtor para esperado<T, E> que aceita tag in_place e argumentos (a serem encaminhados) 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);
Construtor inesperado_t:
Construtor para esperado<T, E> que aceita tag rd::unexpect e argumentos (a serem encaminhados) 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);
tarefa de cópia:
constexpr expected& operator =(expected const &);
mover tarefa:
constexpr expected& operator =(expected &&);
Atribuição de valor:
Atribui valor ao esperado
template < class U = T>
constexpr expected& operator =(U&& rhs);
rd::atribuição inesperada:
Atribui erro ao esperado
template < class G >
constexpr expected& operator =(unexpected<G> const &);
template < class G >
constexpr expected& operator =(unexpected<G> &&);
colocar:
Aceita argumentos para construir um novo valor para o esperado e atribuir a ele.
template < class ... Args>
constexpr T& emplace (Args&&... args);
template < class U , class ... Args>
constexpr T& emplace (std::initializer_list<U>, Args&&...);
trocar função de membro e função 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) &&;
e então:
F deve ser invocável com tipo de valor esperado e deve retornar esperado cujo tipo de erro deve ser E. Retorna erro do esperado atual, se houver erro, other retorna o resultado da invocação de f com 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 &&;
ou_senão:
F deve ser invocável com tipo de erro esperado e deve retornar esperado cujo tipo de valor deve ser T.
Se invoke_result_t<F, E>
for qualquer especialização esperada (cujo tipo de valor deve ser igual a T), então, se um valor estiver lá, o valor será retornado agrupado em invocar_result_t de F, caso contrário, retornará o resultado da invocação de f.
Se invoke_result_t<F, E>
for nulo, então, se houver erro, F será invocado com erro. A corrente esperada é retornada 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 deve ser invocável com tipo de valor atual esperado. Se o esperado atual contém erro, o esperado resultante contém esse erro, caso contrário, contém o resultado da invocação do valor esperado atual com 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 &&;
erro_transformação:
F deve ser invocável com o tipo de erro atual esperado. Se o esperado atual contém o valor, o esperado resultante contém esse valor, caso contrário, contém o resultado da invocação do erro esperado atual com 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 construtor:
constexpr unexpected (unexpected const &);
Mover construtor:
constexpr unexpected (unexpected&&);
Construtor in_place_t:
Ele aceita uma tag std::in_place e constrói T com 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);
construtor de valor:
Constrói T com 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 da exceção. Armazena o valor do erro.
template < class E >
class bad_expected_access ;
E& error () & noexcept ;
E const & error () const & noexcept ;
E&& error() && noexcept ;
E const && error() const && noexcept ;
Este é apenas um tipo de tag, para indicar erro de construção esperado.
A biblioteca também expõe uma variável inesperada do tipo rd::unexpect_t.
inline constexpr unexpect_t unexpect{};