#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; }