|
|
@@ -0,0 +1,208 @@
|
|
|
+#pragma once
|
|
|
+
|
|
|
+#ifdef __cpp_lib_expected
|
|
|
+#if __cpp_lib_expected >= 202202L
|
|
|
+#include <expected>
|
|
|
+
|
|
|
+namespace jvalidate::detail {
|
|
|
+using std::expected;
|
|
|
+using std::unexpected;
|
|
|
+inline constexpr std::unexpect_t unexpect{};
|
|
|
+}
|
|
|
+#endif
|
|
|
+#else
|
|
|
+#include <initializer_list>
|
|
|
+#include <optional>
|
|
|
+#include <utility>
|
|
|
+#include <variant>
|
|
|
+
|
|
|
+#include <jvalidate/detail/expect.h>
|
|
|
+
|
|
|
+namespace jvalidate::detail {
|
|
|
+inline constexpr struct unexpect_t {
|
|
|
+} unexpect{};
|
|
|
+
|
|
|
+template <typename E> class unexpected {
|
|
|
+public:
|
|
|
+ template <typename Err = E>
|
|
|
+ constexpr explicit unexpected(Err && e) : error_(std::forward<Err>(e)) {}
|
|
|
+ template <typename... Args>
|
|
|
+ constexpr explicit unexpected(std::in_place_t, Args &&... args)
|
|
|
+ : error_{std::forward<Args>(args)...} {}
|
|
|
+ template <typename U, typename... Args>
|
|
|
+ constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args &&... args)
|
|
|
+ : error_{il, std::forward<Args>(args)...} {}
|
|
|
+
|
|
|
+ constexpr const E & error() const & noexcept { return error_; }
|
|
|
+ constexpr E & error() & noexcept { return error_; }
|
|
|
+ constexpr E && error() && noexcept { return std::move(error_); }
|
|
|
+
|
|
|
+ constexpr void swap(unexpected & other) noexcept(std::is_nothrow_swappable_v<E>) {
|
|
|
+ using std::swap;
|
|
|
+ swap(error(), other.error());
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename E2> friend constexpr bool operator==(unexpected & x, unexpected<E2> & y) {
|
|
|
+ return x.error() == y.error();
|
|
|
+ }
|
|
|
+
|
|
|
+ friend constexpr void swap(unexpected & x, unexpected & y) noexcept(noexcept(x.swap(y))) {
|
|
|
+ return x.swap(y);
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ E error_;
|
|
|
+};
|
|
|
+
|
|
|
+template <typename E> unexpected(E) -> unexpected<E>;
|
|
|
+
|
|
|
+template <typename E> class bad_expected_access;
|
|
|
+template <> class bad_expected_access<void> : public std::exception {
|
|
|
+public:
|
|
|
+ char const * what() const noexcept override { return "unexpected"; }
|
|
|
+};
|
|
|
+
|
|
|
+template <typename E> class bad_expected_access : public bad_expected_access<void> {
|
|
|
+public:
|
|
|
+ bad_expected_access(E error) : error_(std::move(error)) {}
|
|
|
+
|
|
|
+ char const * what() const noexcept override { return error_.c_str(); }
|
|
|
+
|
|
|
+ constexpr const E & error() const & noexcept { return error_; }
|
|
|
+ constexpr E & error() & noexcept { return error_; }
|
|
|
+ constexpr E && error() && noexcept { return std::move(error_); }
|
|
|
+
|
|
|
+private:
|
|
|
+ E error_;
|
|
|
+};
|
|
|
+
|
|
|
+template <typename T, typename E> class expected {
|
|
|
+public:
|
|
|
+ constexpr expected() = default;
|
|
|
+
|
|
|
+ template <typename U, typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<const U &, T> && !std::is_convertible_v<const G &, E>)
|
|
|
+ expected(expected<U, G> const & other) {
|
|
|
+ if (other.has_value()) {
|
|
|
+ *this = *other;
|
|
|
+ } else {
|
|
|
+ *this = other.error();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename U, typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<U, T> && !std::is_convertible_v<G, E>)
|
|
|
+ expected(expected<U, G> && other) {
|
|
|
+ if (other.has_value()) {
|
|
|
+ *this = *std::move(other);
|
|
|
+ } else {
|
|
|
+ *this = std::move(other).error();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename U = std::remove_cv_t<T>>
|
|
|
+ constexpr explicit(!std::is_convertible_v<U, T>) expected(U && v)
|
|
|
+ : value_(std::in_place_index<0>, std::forward<T>(v)) {}
|
|
|
+
|
|
|
+ template <typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<const G &, E>) expected(unexpected<G> const & e)
|
|
|
+ : value_(std::in_place_index<1>, e.error()) {}
|
|
|
+
|
|
|
+ template <typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<G, E>) expected(unexpected<G> && e)
|
|
|
+ : value_(std::in_place_index<1>, std::move(e).error()) {}
|
|
|
+
|
|
|
+ constexpr explicit operator bool() const noexcept { return has_value(); }
|
|
|
+ constexpr bool has_value() const noexcept { return value_.index() == 0; }
|
|
|
+
|
|
|
+ constexpr const T * operator->() const noexcept { return std::get_if<0>(&value_); }
|
|
|
+ constexpr T * operator->() noexcept { return std::get_if<0>(&value_); }
|
|
|
+
|
|
|
+ constexpr const T & operator*() const & noexcept { return *std::get_if<0>(&value_); }
|
|
|
+ constexpr T & operator*() & noexcept { return *std::get_if<0>(&value_); }
|
|
|
+ constexpr T && operator*() && noexcept { return std::move(*std::get_if<0>(&value_)); }
|
|
|
+
|
|
|
+ constexpr const T & value() const & noexcept {
|
|
|
+ if (JVALIDATE_LIKELY(has_value())) {
|
|
|
+ return operator*();
|
|
|
+ }
|
|
|
+ throw bad_expected_access(error());
|
|
|
+ }
|
|
|
+
|
|
|
+ constexpr T & value() & noexcept {
|
|
|
+ if (JVALIDATE_LIKELY(has_value())) {
|
|
|
+ return operator*();
|
|
|
+ }
|
|
|
+ throw bad_expected_access(std::as_const(error()));
|
|
|
+ }
|
|
|
+
|
|
|
+ constexpr T && value() && noexcept {
|
|
|
+ if (JVALIDATE_LIKELY(has_value())) {
|
|
|
+ return std::move(*this).operator*();
|
|
|
+ }
|
|
|
+ throw bad_expected_access(std::move(error()));
|
|
|
+ }
|
|
|
+
|
|
|
+ constexpr const E & error() const & noexcept { return *std::get_if<1>(&value_); }
|
|
|
+ constexpr E & error() & noexcept { return *std::get_if<1>(&value_); }
|
|
|
+ constexpr E && error() && noexcept { return std::move(*std::get_if<1>(&value_)); }
|
|
|
+
|
|
|
+private:
|
|
|
+ std::variant<T, E> value_;
|
|
|
+};
|
|
|
+
|
|
|
+template <typename E> class expected<void, E> {
|
|
|
+public:
|
|
|
+ constexpr expected() = default;
|
|
|
+
|
|
|
+ template <typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<const G &, E>)
|
|
|
+ expected(expected<void, G> const & other) {
|
|
|
+ if (not other.has_value()) {
|
|
|
+ *this = other.error();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<G, E>) expected(expected<void, G> && other) {
|
|
|
+ if (not other.has_value()) {
|
|
|
+ *this = std::move(other).error();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<const G &, E>) expected(unexpected<G> const & e)
|
|
|
+ : value_(std::in_place_index<1>, e.error()) {}
|
|
|
+
|
|
|
+ template <typename G>
|
|
|
+ constexpr explicit(!std::is_convertible_v<G, E>) expected(unexpected<G> && e)
|
|
|
+ : value_(std::in_place_index<1>, std::move(e).error()) {}
|
|
|
+
|
|
|
+ constexpr explicit operator bool() const noexcept { return has_value(); }
|
|
|
+ constexpr bool has_value() const noexcept { return not value_.has_value(); }
|
|
|
+
|
|
|
+ constexpr void operator*() const noexcept {}
|
|
|
+
|
|
|
+ void value() const & noexcept {
|
|
|
+ if (JVALIDATE_LIKELY(has_value())) {
|
|
|
+ return operator*();
|
|
|
+ }
|
|
|
+ throw bad_expected_access(error());
|
|
|
+ }
|
|
|
+
|
|
|
+ void value() && noexcept {
|
|
|
+ if (JVALIDATE_LIKELY(has_value())) {
|
|
|
+ return operator*();
|
|
|
+ }
|
|
|
+ throw bad_expected_access(std::move(error()));
|
|
|
+ }
|
|
|
+
|
|
|
+ constexpr const E & error() const & noexcept { return *value_; }
|
|
|
+ constexpr E & error() & noexcept { return *value_; }
|
|
|
+ constexpr E && error() && noexcept { return std::move(*value_); }
|
|
|
+
|
|
|
+private:
|
|
|
+ std::optional<E> value_;
|
|
|
+};
|
|
|
+}
|
|
|
+#endif
|