aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-05-16 10:41:36 +0200
committerAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-05-16 10:42:10 +0200
commit9c8c33ab0b2138b45cbfe29f4be235f631730826 (patch)
treee14881ea59b50e42445a502589fb1b9cf5c8d46e /lib
parent3fcc0c5acb160364e819688cc67a8aaf814fafef (diff)
downloadlibquotient-9c8c33ab0b2138b45cbfe29f4be235f631730826.tar.gz
libquotient-9c8c33ab0b2138b45cbfe29f4be235f631730826.zip
Expected<>
This is a minimal implementation along the lines of `std::expected<>` introduced in C++23; once compilers catch up with C++23 support, it may become simply a typedef of std::expected. There are no tests as yet; but the following commits will introduce QOlmExpected that would replace the current `std::variant<T, QOlmError>` pattern used throughout `QOlm*` classes, automatically pulling Expected under the coverage of `QOlm*` unit tests.
Diffstat (limited to 'lib')
-rw-r--r--lib/expected.h74
1 files changed, 74 insertions, 0 deletions
diff --git a/lib/expected.h b/lib/expected.h
new file mode 100644
index 00000000..c8b8fd64
--- /dev/null
+++ b/lib/expected.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <variant>
+
+namespace Quotient {
+
+//! \brief A minimal subset of std::expected from C++23
+template <typename T, typename E,
+ std::enable_if_t<!std::is_same_v<T, E>, bool> = true>
+class Expected {
+private:
+ template <typename X>
+ using enable_if_constructible_t = std::enable_if_t<
+ std::is_constructible_v<T, X> || std::is_constructible_v<E, X>>;
+
+public:
+ using value_type = T;
+ using error_type = E;
+
+ Expected() = default;
+ explicit Expected(const Expected&) = default;
+ explicit Expected(Expected&&) noexcept = default;
+
+ template <typename X, typename = enable_if_constructible_t<X>>
+ Expected(X&& x)
+ : data(std::forward<X>(x))
+ {}
+
+ Expected& operator=(const Expected&) = default;
+ Expected& operator=(Expected&&) noexcept = default;
+
+ template <typename X, typename = enable_if_constructible_t<X>>
+ Expected& operator=(X&& x)
+ {
+ data = std::forward<X>(x);
+ return *this;
+ }
+
+ bool has_value() const { return std::holds_alternative<T>(data); }
+ explicit operator bool() const { return has_value(); }
+
+ const value_type& value() const& { return std::get<T>(data); }
+ value_type& value() & { return std::get<T>(data); }
+ value_type value() && { return std::get<T>(std::move(data)); }
+
+ const value_type& operator*() const& { return value(); }
+ value_type& operator*() & { return value(); }
+
+ const value_type* operator->() const& { return std::get_if<T>(&data); }
+ value_type* operator->() & { return std::get_if<T>(&data); }
+
+ template <class U>
+ T value_or(U&& fallback) const&
+ {
+ if (has_value())
+ return value();
+ return std::forward<U>(fallback);
+ }
+ template <class U>
+ T value_or(U&& fallback) &&
+ {
+ if (has_value())
+ return value();
+ return std::forward<U>(fallback);
+ }
+
+ const E& error() const& { return std::get<E>(data); }
+ E& error() & { return std::get<E>(data); }
+
+private:
+ std::variant<T, E> data;
+};
+
+} // namespace Quotient