diff --git a/include/blt/std/utility.h b/include/blt/std/utility.h index 8f2cfe8..eb4b72a 100644 --- a/include/blt/std/utility.h +++ b/include/blt/std/utility.h @@ -21,7 +21,8 @@ #include #include -#include +#include +#include #if defined(__GNUC__) @@ -211,118 +212,255 @@ namespace blt } }; - template - class expected + struct unexpect_t + { + explicit unexpect_t() = default; + }; + + inline constexpr unexpect_t unexpect{}; + + template + using remove_cvref_t = std::remove_reference_t>; + + template + class unexpected { private: - std::optional t; - std::optional e; + E e; public: - constexpr expected() noexcept: t(T()) + constexpr unexpected(const unexpected&) = default; + + constexpr unexpected(unexpected&&) = default; + + template, unexpected> && !std::is_same_v, std::in_place_t> && + std::is_constructible_v, bool> = true> + constexpr explicit unexpected(Err&& e): e(std::forward(e)) {} - template, bool> = true> - constexpr explicit expected(U&& t): t(std::forward(t)) + template, bool> = true> + constexpr explicit unexpected(std::in_place_t, Args&& ... args): e(std::forward(args)...) {} - template, bool> = true> - constexpr explicit expected(U&& e): e(std::forward(e)) + template&, Args...>, bool> = true> + constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args&& ... args): e(il, std::forward(args)...) {} -// template, bool> = true> -// constexpr expected(std::initializer_list t): t(std::move(*t.begin())) -// {} -// -// template, bool> = true> -// constexpr expected(std::initializer_list e): e(std::move(*e.begin())) -// {} + constexpr const E& error() const& noexcept + { + return e; + } - template && std::is_convertible_v, bool> = true> + constexpr E& error()& noexcept + { + return e; + } + + constexpr const E&& error() const&& noexcept + { + return e; + } + + constexpr E&& error()&& noexcept + { + return e; + } + + constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) + { + std::swap(error(), other.error()); + } + + template + inline friend constexpr bool operator==(const unexpected& x, const unexpected & y) + { + return x.error() == y.error(); + } + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) + {} + }; + + template + unexpected(E) -> unexpected; + + template + class bad_expected_access : public std::exception + { + private: + E e; + public: + explicit bad_expected_access(E e): e(std::move(e)) + {} + + const E& error() const& noexcept + { return e; } + + E& error()& noexcept + { return e; } + + const E&& error() const&& noexcept + { return e; } + + E&& error()&& noexcept + { return e; } + + [[nodiscard]] const char* what() const noexcept override + { return "blt::expected does not contain a value!"; } + + }; + + template> + class expected + { + protected: + std::variant v; + public: + template, bool> = true> + constexpr expected() noexcept: v(T()) + {} + + constexpr expected(const expected& copy) = delete; + + constexpr expected(expected&& move) noexcept : v(move ? std::move(*move) : std::move(move.error())) + {} + + /* + * (4)...(5) + */ + + template constexpr explicit expected(const expected& other) - { - if (other.has_value()) - t = other.value(); - else - e = other.error(); - } + {} - template && std::is_convertible_v, bool> = true> + template constexpr explicit expected(expected&& other) - { - if (other.has_value()) - t = other.value(); - else - e = other.error(); - } - - constexpr expected(const T& t): t(t) {} - constexpr expected(T&& t): t(std::move(t)) + + /* + * (6) + */ + + template, bool> = true> + constexpr explicit expected(U&& v) {} - constexpr expected(const E& e): e(e) + template, bool> = true> + constexpr expected(U&& v) {} - constexpr expected(E&& e): e(std::move(e)) + /* + * (7) + */ + + template, bool> = true> + constexpr explicit expected(const unexpected& e) {} - constexpr expected(const expected& copy) = default; + template, bool> = true> + constexpr expected(const unexpected& e) + {} - constexpr expected(expected&& move) = default; + /* + * (8) + */ - expected& operator=(const expected& copy) = default; + template, bool> = true> + constexpr explicit expected(const unexpected& e) + {} + + template, bool> = true> + constexpr expected(const unexpected& e) + {} + + /* + * (9)...(13) + */ + template + constexpr explicit expected(std::in_place_t, Args&& ... args) + {} + + template + constexpr explicit expected(std::in_place_t, std::initializer_list il, Args&& ... args) + {} + + template + constexpr explicit expected(std::in_place_t) noexcept + {} + + template + constexpr explicit expected(unexpect_t, Args&& ... args) + {} + + template + constexpr explicit expected(unexpect_t, std::initializer_list il, Args&& ... args) + {} + + expected& operator=(const expected& copy) = delete; expected& operator=(expected&& move) = default; [[nodiscard]] constexpr explicit operator bool() const noexcept { - return t.has_value(); + return std::holds_alternative(v); } [[nodiscard]] constexpr inline bool has_value() const noexcept { - return t.has_value(); + return std::holds_alternative(v); } constexpr T& value()& { - return t.value(); + if (*this) + return std::get(v); + else + throw bad_expected_access(std::as_const(error())); } constexpr const T& value() const& { - return t.value(); + if (*this) + return std::get(v); + else + throw bad_expected_access(std::as_const(error())); } constexpr T&& value()&& { - return t.value(); + if (*this) + return std::get(v); + else + throw bad_expected_access(std::move(error())); } constexpr const T&& value() const&& { - return t.value(); + if (*this) + return std::get(v); + else + throw bad_expected_access(std::move(error())); } constexpr const E& error() const& noexcept { - return e.value(); + return std::get(v); } constexpr E& error()& noexcept { - return e.value(); + return std::get(v); } constexpr const E&& error() const&& noexcept { - return e.value(); + return std::get(v); } constexpr E&& error()&& noexcept { - return e.value(); + return std::get(v); } template && std::is_copy_constructible_v, bool> = true> @@ -339,34 +477,46 @@ namespace blt constexpr inline const T* operator->() const noexcept { - return &t.value(); + return &std::get(v); } constexpr inline T* operator->() noexcept { - return &t.value(); + return &std::get(v); } constexpr inline const T& operator*() const& noexcept { - return t.value(); + return std::get(v); } constexpr inline T& operator*()& noexcept { - return t.value(); + return std::get(v); } constexpr inline const T&& operator*() const&& noexcept { - return t.value(); + return std::move(std::get(v)); } constexpr inline T&& operator*()&& noexcept { - return std::move(t.value()); + return std::move(std::get(v)); } }; + + template + class expected : expected + { + public: + using expected::expected; + + constexpr expected(const expected& copy): expected::v(copy ? *copy : copy.error()) + {} + + expected& operator=(const expected& copy) = default; + }; //#define BLT_LAMBDA(type, var, code) [](const type& var) -> auto { return code; } //#define BLT_LAMBDA(var, code) [](var) -> auto { return code; }