#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