aboutsummaryrefslogtreecommitdiffstats
path: root/util/generic/maybe.h
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/generic/maybe.h
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/generic/maybe.h')
-rw-r--r--util/generic/maybe.h722
1 files changed, 722 insertions, 0 deletions
diff --git a/util/generic/maybe.h b/util/generic/maybe.h
new file mode 100644
index 0000000000..34d21aebcd
--- /dev/null
+++ b/util/generic/maybe.h
@@ -0,0 +1,722 @@
+#pragma once
+
+#include <utility>
+
+#include "maybe_traits.h"
+#include "yexception.h"
+
+#include <util/system/align.h>
+#include <util/stream/output.h>
+#include <util/ysaveload.h>
+
+namespace NMaybe {
+ struct TPolicyUndefinedExcept {
+ [[noreturn]] static void OnEmpty(const std::type_info& valueTypeInfo);
+ };
+
+ struct TPolicyUndefinedFail {
+ [[noreturn]] static void OnEmpty(const std::type_info& valueTypeInfo);
+ };
+}
+
+struct TNothing {
+ explicit constexpr TNothing(int) noexcept {
+ }
+};
+
+constexpr TNothing NothingObject{0};
+
+constexpr TNothing Nothing() noexcept {
+ return NothingObject;
+}
+
+constexpr bool operator==(TNothing, TNothing) noexcept {
+ return true;
+}
+
+template <class T, class Policy /*= ::NMaybe::TPolicyUndefinedExcept*/>
+class TMaybe: private TMaybeBase<T> {
+public:
+ using TInPlace = NMaybe::TInPlace;
+
+private:
+ static_assert(!std::is_same<std::remove_cv_t<T>, TNothing>::value,
+ "Instantiation of TMaybe with a TNothing type is ill-formed");
+ static_assert(!std::is_same<std::remove_cv_t<T>, TInPlace>::value,
+ "Instantiation of TMaybe with a TInPlace type is ill-formed");
+ static_assert(!std::is_reference<T>::value,
+ "Instantiation of TMaybe with reference type is ill-formed");
+ static_assert(std::is_destructible<T>::value,
+ "Instantiation of TMaybe with non-destructible type is ill-formed");
+
+ template <class U>
+ struct TConstructibleFromMaybeSomehow {
+ public:
+ static constexpr bool value = std::is_constructible<T, TMaybe<U, Policy>&>::value ||
+ std::is_constructible<T, const TMaybe<U, Policy>&>::value ||
+ std::is_constructible<T, TMaybe<U, Policy>&&>::value ||
+ std::is_constructible<T, const TMaybe<U, Policy>&&>::value ||
+ std::is_convertible<TMaybe<U, Policy>&, T>::value ||
+ std::is_convertible<const TMaybe<U, Policy>&, T>::value ||
+ std::is_convertible<TMaybe<U, Policy>&&, T>::value ||
+ std::is_convertible<const TMaybe<U, Policy>&&, T>::value;
+ };
+
+ template <class U>
+ struct TAssignableFromMaybeSomehow {
+ public:
+ static constexpr bool value = TConstructibleFromMaybeSomehow<U>::value ||
+ std::is_assignable<T&, TMaybe<U, Policy>&>::value ||
+ std::is_assignable<T&, const TMaybe<U, Policy>&>::value ||
+ std::is_assignable<T&, TMaybe<U, Policy>&&>::value ||
+ std::is_assignable<T&, const TMaybe<U, Policy>&&>::value;
+ };
+
+ template <class U>
+ struct TImplicitCopyCtor {
+ public:
+ static constexpr bool value = std::is_constructible<T, const U&>::value &&
+ std::is_convertible<const U&, T>::value &&
+ !TConstructibleFromMaybeSomehow<U>::value;
+ };
+
+ template <class U>
+ struct TExplicitCopyCtor {
+ public:
+ static constexpr bool value = std::is_constructible<T, const U&>::value &&
+ !std::is_convertible<const U&, T>::value &&
+ !TConstructibleFromMaybeSomehow<U>::value;
+ };
+
+ template <class U>
+ struct TImplicitMoveCtor {
+ public:
+ static constexpr bool value = std::is_constructible<T, U&&>::value &&
+ std::is_convertible<U&&, T>::value &&
+ !TConstructibleFromMaybeSomehow<U>::value;
+ };
+
+ template <class U>
+ struct TExplicitMoveCtor {
+ public:
+ static constexpr bool value = std::is_constructible<T, U&&>::value &&
+ !std::is_convertible<U&&, T>::value &&
+ !TConstructibleFromMaybeSomehow<U>::value;
+ };
+
+ template <class U>
+ struct TCopyAssignable {
+ public:
+ static constexpr bool value = std::is_constructible<T, const U&>::value &&
+ std::is_assignable<T&, const U&>::value &&
+ !TAssignableFromMaybeSomehow<U>::value;
+ };
+
+ template <class U>
+ struct TMoveAssignable {
+ public:
+ static constexpr bool value = std::is_constructible<T, U&&>::value &&
+ std::is_assignable<T&, U&&>::value &&
+ !TAssignableFromMaybeSomehow<U>::value;
+ };
+
+ template <class U>
+ struct TImplicitAnyCtor {
+ public:
+ using UDec = std::decay_t<U>;
+
+ static constexpr bool value = std::is_constructible<T, U>::value &&
+ std::is_convertible<U, T>::value &&
+ !std::is_same<UDec, TInPlace>::value &&
+ !std::is_same<UDec, TMaybe>::value;
+ };
+
+ template <class U>
+ struct TExplicitAnyCtor {
+ public:
+ using UDec = std::decay_t<U>;
+ static constexpr bool value = std::is_constructible<T, U>::value &&
+ !std::is_convertible<U, T>::value &&
+ !std::is_same<UDec, TInPlace>::value &&
+ !std::is_same<UDec, TMaybe>::value;
+ };
+
+ template <class U>
+ struct TAssignableFromAny {
+ public:
+ using UDec = std::decay_t<U>;
+ static constexpr bool value = !std::is_same<UDec, TMaybe>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<T&, U>::value &&
+ (!std::is_scalar<T>::value || !std::is_same<UDec, T>::value);
+ };
+
+ using TBase = TMaybeBase<T>;
+
+public:
+ using value_type = T;
+ using TValueType = value_type;
+
+ TMaybe() noexcept = default;
+
+ constexpr TMaybe(const TMaybe&) = default;
+ constexpr TMaybe(TMaybe&&) = default;
+
+ template <class... Args>
+ constexpr explicit TMaybe(TInPlace, Args&&... args)
+ : TBase(TInPlace{}, std::forward<Args>(args)...)
+ {
+ }
+
+ template <class U, class... TArgs>
+ constexpr explicit TMaybe(TInPlace, std::initializer_list<U> il, TArgs&&... args)
+ : TBase(TInPlace{}, il, std::forward<TArgs>(args)...)
+ {
+ }
+
+ constexpr TMaybe(TNothing) noexcept {
+ }
+
+ template <class U, class = std::enable_if_t<TImplicitCopyCtor<U>::value>>
+ TMaybe(const TMaybe<U, Policy>& right) {
+ if (right.Defined()) {
+ new (Data()) T(right.GetRef());
+ this->Defined_ = true;
+ }
+ }
+
+ template <class U, std::enable_if_t<TExplicitCopyCtor<U>::value, bool> = false>
+ explicit TMaybe(const TMaybe<U, Policy>& right) {
+ if (right.Defined()) {
+ new (Data()) T(right.GetRef());
+ this->Defined_ = true;
+ }
+ }
+
+ template <class U, class = std::enable_if_t<TImplicitMoveCtor<U>::value>>
+ TMaybe(TMaybe<U, Policy>&& right) noexcept(std::is_nothrow_constructible<T, U&&>::value) {
+ if (right.Defined()) {
+ new (Data()) T(std::move(right.GetRef()));
+ this->Defined_ = true;
+ }
+ }
+
+ template <class U, std::enable_if_t<TExplicitMoveCtor<U>::value, bool> = false>
+ explicit TMaybe(TMaybe<U, Policy>&& right) noexcept(std::is_nothrow_constructible<T, U&&>::value) {
+ if (right.Defined()) {
+ new (Data()) T(std::move(right.GetRef()));
+ this->Defined_ = true;
+ }
+ }
+
+ template <class U = T, class = std::enable_if_t<TImplicitAnyCtor<U>::value>>
+ constexpr TMaybe(U&& right)
+ : TBase(TInPlace{}, std::forward<U>(right))
+ {
+ }
+
+ template <class U = T, std::enable_if_t<TExplicitAnyCtor<U>::value, bool> = false>
+ constexpr explicit TMaybe(U&& right)
+ : TBase(TInPlace{}, std::forward<U>(right))
+ {
+ }
+
+ ~TMaybe() = default;
+
+ constexpr TMaybe& operator=(const TMaybe&) = default;
+ constexpr TMaybe& operator=(TMaybe&&) = default;
+
+ TMaybe& operator=(TNothing) noexcept {
+ Clear();
+ return *this;
+ }
+
+ template <class U = T>
+ std::enable_if_t<TAssignableFromAny<U>::value, TMaybe&> operator=(U&& right) {
+ if (Defined()) {
+ *Data() = std::forward<U>(right);
+ } else {
+ Init(std::forward<U>(right));
+ }
+ return *this;
+ }
+
+ template <class U>
+ std::enable_if_t<TCopyAssignable<U>::value,
+ TMaybe&>
+ operator=(const TMaybe<U, Policy>& right) {
+ if (right.Defined()) {
+ if (Defined()) {
+ *Data() = right.GetRef();
+ } else {
+ Init(right.GetRef());
+ }
+ } else {
+ Clear();
+ }
+
+ return *this;
+ }
+
+ template <class U>
+ std::enable_if_t<TMoveAssignable<U>::value,
+ TMaybe&>
+ operator=(TMaybe<U, Policy>&& right) noexcept(
+ std::is_nothrow_assignable<T&, U&&>::value&& std::is_nothrow_constructible<T, U&&>::value)
+ {
+ if (right.Defined()) {
+ if (Defined()) {
+ *Data() = std::move(right.GetRef());
+ } else {
+ Init(std::move(right.GetRef()));
+ }
+ } else {
+ Clear();
+ }
+
+ return *this;
+ }
+
+ template <typename... Args>
+ T& ConstructInPlace(Args&&... args) {
+ Clear();
+ Init(std::forward<Args>(args)...);
+ return *Data();
+ }
+
+ void Clear() noexcept {
+ if (Defined()) {
+ this->Defined_ = false;
+ Data()->~T();
+ }
+ }
+
+ constexpr bool Defined() const noexcept {
+ return this->Defined_;
+ }
+
+ Y_PURE_FUNCTION constexpr bool Empty() const noexcept {
+ return !Defined();
+ }
+
+ void CheckDefined() const {
+ if (Y_UNLIKELY(!Defined())) {
+ Policy::OnEmpty(typeid(TValueType));
+ }
+ }
+
+ const T* Get() const noexcept {
+ return Defined() ? Data() : nullptr;
+ }
+
+ T* Get() noexcept {
+ return Defined() ? Data() : nullptr;
+ }
+
+ constexpr const T& GetRef() const& {
+ CheckDefined();
+
+ return *Data();
+ }
+
+ constexpr T& GetRef() & {
+ CheckDefined();
+
+ return *Data();
+ }
+
+ constexpr const T&& GetRef() const&& {
+ CheckDefined();
+
+ return std::move(*Data());
+ }
+
+ constexpr T&& GetRef() && {
+ CheckDefined();
+
+ return std::move(*Data());
+ }
+
+ constexpr const T& operator*() const& {
+ return GetRef();
+ }
+
+ constexpr T& operator*() & {
+ return GetRef();
+ }
+
+ constexpr const T&& operator*() const&& {
+ return std::move(GetRef());
+ }
+
+ constexpr T&& operator*() && {
+ return std::move(GetRef());
+ }
+
+ constexpr const T* operator->() const {
+ return &GetRef();
+ }
+
+ constexpr T* operator->() {
+ return &GetRef();
+ }
+
+ constexpr const T& GetOrElse(const T& elseValue) const {
+ return Defined() ? *Data() : elseValue;
+ }
+
+ constexpr T& GetOrElse(T& elseValue) {
+ return Defined() ? *Data() : elseValue;
+ }
+
+ constexpr const TMaybe& OrElse(const TMaybe& elseValue) const noexcept {
+ return Defined() ? *this : elseValue;
+ }
+
+ constexpr TMaybe& OrElse(TMaybe& elseValue) {
+ return Defined() ? *this : elseValue;
+ }
+
+ template <typename U>
+ TMaybe<U, Policy> Cast() const {
+ return Defined() ? TMaybe<U, Policy>(*Data()) : TMaybe<U, Policy>();
+ }
+
+ constexpr explicit operator bool() const noexcept {
+ return Defined();
+ }
+
+ void Save(IOutputStream* out) const {
+ const bool defined = Defined();
+
+ ::Save<bool>(out, defined);
+
+ if (defined) {
+ ::Save(out, *Data());
+ }
+ }
+
+ void Load(IInputStream* in) {
+ bool defined;
+
+ ::Load(in, defined);
+
+ if (defined) {
+ if (!Defined()) {
+ ConstructInPlace();
+ }
+
+ ::Load(in, *Data());
+ } else {
+ Clear();
+ }
+ }
+
+ void Swap(TMaybe& other) {
+ if (this->Defined_ == other.Defined_) {
+ if (this->Defined_) {
+ ::DoSwap(this->Data_, other.Data_);
+ }
+ } else {
+ if (this->Defined_) {
+ other.Init(std::move(this->Data_));
+ this->Clear();
+ } else {
+ this->Init(std::move(other.Data_));
+ other.Clear();
+ }
+ }
+ }
+
+ void swap(TMaybe& other) {
+ Swap(other);
+ }
+
+private:
+ constexpr const T* Data() const noexcept {
+ return std::addressof(this->Data_);
+ }
+
+ constexpr T* Data() noexcept {
+ return std::addressof(this->Data_);
+ }
+
+ template <typename... Args>
+ void Init(Args&&... args) {
+ new (Data()) T(std::forward<Args>(args)...);
+ this->Defined_ = true;
+ }
+};
+
+template <class T>
+using TMaybeFail = TMaybe<T, NMaybe::TPolicyUndefinedFail>;
+
+template <class T, class TPolicy = ::NMaybe::TPolicyUndefinedExcept>
+constexpr TMaybe<std::decay_t<T>, TPolicy> MakeMaybe(T&& value) {
+ return TMaybe<std::decay_t<T>, TPolicy>(std::forward<T>(value));
+}
+
+template <class T, class... TArgs>
+constexpr TMaybe<T> MakeMaybe(TArgs&&... args) {
+ return TMaybe<T>(typename TMaybe<T>::TInPlace{}, std::forward<TArgs>(args)...);
+}
+
+template <class T, class U, class... TArgs>
+constexpr TMaybe<T> MakeMaybe(std::initializer_list<U> il, TArgs&&... args) {
+ return TMaybe<T>(typename TMaybe<T>::TInPlace{}, il, std::forward<TArgs>(args)...);
+}
+
+template <class T, class TPolicy>
+void Swap(TMaybe<T, TPolicy>& lhs, TMaybe<T, TPolicy>& rhs) {
+ lhs.Swap(rhs);
+}
+
+template <class T, class TPolicy>
+void swap(TMaybe<T, TPolicy>& lhs, TMaybe<T, TPolicy>& rhs) {
+ lhs.Swap(rhs);
+}
+
+template <typename T, class TPolicy>
+struct THash<TMaybe<T, TPolicy>> {
+ constexpr size_t operator()(const TMaybe<T, TPolicy>& data) const {
+ return (data.Defined()) ? THash<T>()(data.GetRef()) : 42;
+ }
+};
+
+// Comparisons between TMaybe
+template <class T, class TPolicy>
+constexpr bool operator==(const ::TMaybe<T, TPolicy>& left, const ::TMaybe<T, TPolicy>& right) {
+ return (static_cast<bool>(left) != static_cast<bool>(right))
+ ? false
+ : (
+ !static_cast<bool>(left)
+ ? true
+ : *left == *right);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator!=(const TMaybe<T, TPolicy>& left, const TMaybe<T, TPolicy>& right) {
+ return !(left == right);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<(const TMaybe<T, TPolicy>& left, const TMaybe<T, TPolicy>& right) {
+ return (!static_cast<bool>(right))
+ ? false
+ : (
+ !static_cast<bool>(left)
+ ? true
+ : (*left < *right));
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>(const TMaybe<T, TPolicy>& left, const TMaybe<T, TPolicy>& right) {
+ return right < left;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<=(const TMaybe<T, TPolicy>& left, const TMaybe<T, TPolicy>& right) {
+ return !(right < left);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>=(const TMaybe<T, TPolicy>& left, const TMaybe<T, TPolicy>& right) {
+ return !(left < right);
+}
+
+// Comparisons with TNothing
+template <class T, class TPolicy>
+constexpr bool operator==(const TMaybe<T, TPolicy>& left, TNothing) noexcept {
+ return !static_cast<bool>(left);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator==(TNothing, const TMaybe<T, TPolicy>& right) noexcept {
+ return !static_cast<bool>(right);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator!=(const TMaybe<T, TPolicy>& left, TNothing) noexcept {
+ return static_cast<bool>(left);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator!=(TNothing, const TMaybe<T, TPolicy>& right) noexcept {
+ return static_cast<bool>(right);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<(const TMaybe<T, TPolicy>&, TNothing) noexcept {
+ return false;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<(TNothing, const TMaybe<T, TPolicy>& right) noexcept {
+ return static_cast<bool>(right);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<=(const TMaybe<T, TPolicy>& left, TNothing) noexcept {
+ return !static_cast<bool>(left);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<=(TNothing, const TMaybe<T, TPolicy>&) noexcept {
+ return true;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>(const TMaybe<T, TPolicy>& left, TNothing) noexcept {
+ return static_cast<bool>(left);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>(TNothing, const TMaybe<T, TPolicy>&) noexcept {
+ return false;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>=(const TMaybe<T, TPolicy>&, TNothing) noexcept {
+ return true;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>=(TNothing, const TMaybe<T, TPolicy>& right) noexcept {
+ return !static_cast<bool>(right);
+}
+
+// Comparisons with T
+
+template <class T, class TPolicy>
+constexpr bool operator==(const TMaybe<T, TPolicy>& maybe, const T& value) {
+ return static_cast<bool>(maybe) ? *maybe == value : false;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator==(const T& value, const TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? *maybe == value : false;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator!=(const TMaybe<T, TPolicy>& maybe, const T& value) {
+ return static_cast<bool>(maybe) ? !(*maybe == value) : true;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator!=(const T& value, const TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? !(*maybe == value) : true;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<(const TMaybe<T, TPolicy>& maybe, const T& value) {
+ return static_cast<bool>(maybe) ? std::less<T>{}(*maybe, value) : true;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<(const T& value, const TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? std::less<T>{}(value, *maybe) : false;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<=(const TMaybe<T, TPolicy>& maybe, const T& value) {
+ return !(maybe > value);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator<=(const T& value, const TMaybe<T, TPolicy>& maybe) {
+ return !(value > maybe);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>(const TMaybe<T, TPolicy>& maybe, const T& value) {
+ return static_cast<bool>(maybe) ? value < maybe : false;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>(const T& value, const TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? maybe < value : true;
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>=(const TMaybe<T, TPolicy>& maybe, const T& value) {
+ return !(maybe < value);
+}
+
+template <class T, class TPolicy>
+constexpr bool operator>=(const T& value, const TMaybe<T, TPolicy>& maybe) {
+ return !(value < maybe);
+}
+
+// Comparison with values convertible to T
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator==(const ::TMaybe<T, TPolicy>& maybe, const U& value) {
+ return static_cast<bool>(maybe) ? *maybe == value : false;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator==(const U& value, const ::TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? *maybe == value : false;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator!=(const TMaybe<T, TPolicy>& maybe, const U& value) {
+ return static_cast<bool>(maybe) ? !(*maybe == value) : true;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator!=(const U& value, const TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? !(*maybe == value) : true;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator<(const TMaybe<T, TPolicy>& maybe, const U& value) {
+ return static_cast<bool>(maybe) ? std::less<T>{}(*maybe, value) : true;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator<(const U& value, const TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? std::less<T>{}(value, *maybe) : false;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator<=(const TMaybe<T, TPolicy>& maybe, const U& value) {
+ return !(maybe > value);
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator<=(const U& value, const TMaybe<T, TPolicy>& maybe) {
+ return !(value > maybe);
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator>(const TMaybe<T, TPolicy>& maybe, const U& value) {
+ return static_cast<bool>(maybe) ? value < maybe : false;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator>(const U& value, const TMaybe<T, TPolicy>& maybe) {
+ return static_cast<bool>(maybe) ? maybe < value : true;
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator>=(const TMaybe<T, TPolicy>& maybe, const U& value) {
+ return !(maybe < value);
+}
+
+template <class T, class TPolicy, class U, std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>
+constexpr bool operator>=(const U& value, const TMaybe<T, TPolicy>& maybe) {
+ return !(value < maybe);
+}
+
+class IOutputStream;
+
+template <class T, class TPolicy>
+inline IOutputStream& operator<<(IOutputStream& out, const TMaybe<T, TPolicy>& maybe) {
+ if (maybe.Defined()) {
+ out << *maybe;
+ } else {
+ out << TStringBuf("(empty maybe)");
+ }
+ return out;
+}