瀏覽代碼

refactor: std::expected compat

Sam Jaffe 3 月之前
父節點
當前提交
56b3271b58
共有 1 個文件被更改,包括 208 次插入0 次删除
  1. 208 0
      include/jvalidate/compat/expected.h

+ 208 - 0
include/jvalidate/compat/expected.h

@@ -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