diff --git a/src/expected.h b/src/expected.h new file mode 100644 index 0000000..d3f896d --- /dev/null +++ b/src/expected.h @@ -0,0 +1,98 @@ +#ifndef EXPECTED_H +#define EXPECTED_H + +#include +#include +#include +#include +#include +#include + +struct BadExpectedAccess : std::exception { + const char* what() const noexcept override { + return "bad access to `Expected` without expected value"; + } +}; + +class Unexpected { + public: + explicit Unexpected(std::string_view err_msg) : err_msg_(err_msg) {} + + std::string_view error() const noexcept { return err_msg_; } + + private: + std::string err_msg_; +}; + +template +class Expected { + static_assert(!std::is_reference_v, + "Expected does not support reference types"); + + public: + Expected() + requires(std::is_default_constructible_v) + : val_(std::in_place_index<0>) {} + + Expected(const T& val) : val_(val) {} + Expected(T&& val) : val_(std::move(val)) {} + + Expected(const Unexpected& err) : val_(err) {} + Expected(Unexpected&& err) : val_(std::move(err)) {} + + bool has_value() const noexcept { return val_.index() == 0; } + + explicit operator bool() const noexcept { return has_value(); } + + T& value() & { + if (!has_value()) throw BadExpectedAccess(); + return std::get<0>(val_); + } + + const T& value() const& { + if (!has_value()) throw BadExpectedAccess(); + return std::get<0>(val_); + } + + T&& value() && { + if (!has_value()) throw BadExpectedAccess(); + return std::move(std::get<0>(val_)); + } + + const T&& value() const&& { + if (!has_value()) throw BadExpectedAccess(); + return std::move(std::get<0>(val_)); + } + + std::string_view error() const noexcept { + return has_value() ? "" : std::get<1>(val_).error(); + } + + private: + std::variant val_; +}; + +template <> +class Expected { + public: + Expected() {} + + Expected(const Unexpected& err) : err_(err) {} + Expected(Unexpected&& err) : err_(std::move(err)) {} + + bool has_value() const noexcept { return !err_.has_value(); } + explicit operator bool() const noexcept { return has_value(); } + + void value() const { + if (!has_value()) throw BadExpectedAccess(); + } + + std::string_view error() const noexcept { + return err_.has_value() ? err_.value().error() : ""; + } + + private: + std::optional err_; +}; + +#endif // EXPECTED_H