diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/generic/maybe.h | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/generic/maybe.h')
-rw-r--r-- | util/generic/maybe.h | 722 |
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; +} |