/* Option type implementation. Inspired by libc++. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OSTD_MAYBE_HH #define OSTD_MAYBE_HH #include "ostd/types.hh" #include "ostd/type_traits.hh" #include "ostd/memory.hh" #include "ostd/utility.hh" #include "ostd/initializer_list.hh" #include "ostd/functional.hh" namespace ostd { struct InPlace {}; constexpr InPlace in_place = InPlace(); struct Nothing { explicit constexpr Nothing(int) {} }; constexpr Nothing nothing = Nothing(0); namespace detail { template> class MaybeStorage { protected: using Value = T; union { char p_null_state; Value p_value; }; bool p_engaged = false; constexpr MaybeStorage(): p_null_state('\0') {} MaybeStorage(const MaybeStorage &v): p_engaged(v.p_engaged) { if (p_engaged) ::new(address_of(p_value)) Value(v.p_value); } MaybeStorage(MaybeStorage &&v): p_engaged(v.p_engaged) { if (p_engaged) ::new(address_of(p_value)) Value(move(v.p_value)); } constexpr MaybeStorage(const Value &v): p_value(v), p_engaged(true) {} constexpr MaybeStorage(Value &&v): p_value(move(v)), p_engaged(true) {} template constexpr MaybeStorage(InPlace, A &&...args): p_value(forward(args)...), p_engaged(true) {} ~MaybeStorage() { if (p_engaged) p_value.~Value(); } }; template class MaybeStorage { protected: using Value = T; union { char p_null_state; Value p_value; }; bool p_engaged = false; constexpr MaybeStorage(): p_null_state('\0') {} MaybeStorage(const MaybeStorage &v): p_engaged(v.p_engaged) { if (p_engaged) ::new(address_of(p_value)) Value(v.p_value); } MaybeStorage(MaybeStorage &&v): p_engaged(v.p_engaged) { if (p_engaged) ::new(address_of(p_value)) Value(move(v.p_value)); } constexpr MaybeStorage(const Value &v): p_value(v), p_engaged(true) {} constexpr MaybeStorage(Value &&v): p_value(move(v)), p_engaged(true) {} template constexpr MaybeStorage(InPlace, A &&...args): p_value(forward(args)...), p_engaged(true) {} }; } template class Maybe: private detail::MaybeStorage { using Base = detail::MaybeStorage; public: using Value = T; static_assert(!IsReference, "Initialization of Maybe with a reference type is not allowed."); static_assert(!IsSame, InPlace>, "Initialization of Maybe with InPlace is not allowed."); static_assert(!IsSame, Nothing>, "Initialization of Maybe with Nothing is not allowed."); static_assert(IsObject, "Initialization of Maybe with non-object type is not allowed."); static_assert(IsDestructible, "Initialization of Maybe with a non-destructible object is not allowed."); constexpr Maybe() {} Maybe(const Maybe &) = default; Maybe(Maybe &&) = default; constexpr Maybe(Nothing) {} constexpr Maybe(const Value &v): Base(v) {} constexpr Maybe(Value &&v): Base(move(v)) {} template>> constexpr explicit Maybe(InPlace, A &&...args): Base(in_place, forward(args)...) {} template &, A...>>> constexpr explicit Maybe(InPlace, std::initializer_list il, A &&...args): Base(in_place, il, forward(args)...) {} ~Maybe() = default; Maybe &operator=(Nothing) { if (this->p_engaged) { this->p_value.~Value(); this->p_engaged = false; } return *this; } Maybe &operator=(const Maybe &v) { if (this->p_engaged == v.p_engaged) { if (this->p_engaged) this->p_value = v.p_value; } else { if (this->p_engaged) this->p_value.~Value(); else ::new(address_of(this->p_value)) Value(v.p_value); this->p_engaged = v.p_engaged; } return *this; } Maybe &operator=(Maybe &&v) { if (this->p_engaged == v.p_engaged) { if (this->p_engaged) this->p_value = move(v.p_value); } else { if (this->p_engaged) this->p_value.~Value(); else { ::new(address_of(this->p_value)) Value(move(v.p_value)); } this->p_engaged = v.p_engaged; } return *this; } template, Value> && IsConstructible && IsAssignable >> Maybe &operator=(U &&v) { if (this->p_engaged) { this->p_value = forward(v); } else { ::new(address_of(this->p_value)) Value(forward(v)); this->p_engaged = true; } return *this; } template>> void emplace(A &&...args) { *this = nothing; ::new(address_of(this->p_value)) Value(forward(args)...); this->p_engaged = true; } template &, A...> >> void emplace(std::initializer_list il, A &&...args) { *this = nothing; ::new(address_of(this->p_value)) Value(il, forward(args)...); this->p_engaged = true; } constexpr const Value *operator->() const { return address_of(this->p_value); } Value *operator->() { return address_of(this->p_value); } constexpr const Value &operator*() const { return this->p_value; } Value &operator*() { return this->p_value; } constexpr explicit operator bool() const { return this->p_engaged; } constexpr const Value &value() const { return this->p_value; } Value &value() { return this->p_value; } template constexpr Value value_or(U &&v) const & { static_assert(IsCopyConstructible, "Maybe::value_or: T must be copy constructible"); static_assert(IsConvertible, "Maybe::value_or: U must be convertible to T"); return this->p_engaged ? this->p_value : Value(forward(v)); } template Value value_or(U &&v) && { static_assert(IsMoveConstructible, "Maybe::value_or: T must be copy constructible"); static_assert(IsConvertible, "Maybe::value_or: U must be convertible to T"); return this->p_engaged ? move(this->p_value) : Value(forward(v)); } void swap(Maybe &v) { if (this->p_engaged == v.p_engaged) { if (this->p_engaged) detail::swap_adl(this->p_value, v.p_value); } else { if (this->p_engaged) { ::new(address_of(v.p_value)) Value(move(this->p_value)); this->p_value.~Value(); } else { ::new(address_of(this->p_value)) Value(move(v.p_value)); v.p_value.~Value(); } detail::swap_adl(this->p_engaged, v.p_engaged); } } Size to_hash() const { return this->p_engaged ? ostd::ToHash()(this->p_value) : 0; } }; /* maybe vs maybe */ template inline constexpr bool operator==(const Maybe &a, const Maybe &b) { return (bool(a) != bool(b)) ? false : (!bool(a) ? true : (*a == *b)); } template inline constexpr bool operator!=(const Maybe &a, const Maybe &b) { return !(a == b); } template inline constexpr bool operator<(const Maybe &a, const Maybe &b) { return !bool(b) ? false : (!bool(a) ? true : (*a < *b)); } template inline constexpr bool operator>(const Maybe &a, const Maybe &b) { return b < a; } template inline constexpr bool operator<=(const Maybe &a, const Maybe &b) { return !(b < a); } template inline constexpr bool operator>=(const Maybe &a, const Maybe &b) { return !(a < b); } /* maybe vs nothing */ template inline constexpr bool operator==(const Maybe &v, Nothing) { return !bool(v); } template inline constexpr bool operator==(Nothing, const Maybe &v) { return !bool(v); } template inline constexpr bool operator!=(const Maybe &v, Nothing) { return bool(v); } template inline constexpr bool operator!=(Nothing, const Maybe &v) { return bool(v); } template inline constexpr bool operator<(const Maybe &, Nothing) { return false; } template inline constexpr bool operator<(Nothing, const Maybe &v) { return bool(v); } template inline constexpr bool operator<=(const Maybe &v, Nothing) { return !bool(v); } template inline constexpr bool operator<=(Nothing, const Maybe &) { return true; } template inline constexpr bool operator>(const Maybe &v, Nothing) { return bool(v); } template inline constexpr bool operator>(Nothing, const Maybe &) { return false; } template inline constexpr bool operator>=(const Maybe &, Nothing) { return true; } template inline constexpr bool operator>=(Nothing, const Maybe &v) { return !bool(v); } /* maybe vs T */ template inline constexpr bool operator==(const Maybe &a, const T &b) { return bool(a) ? (*a == b) : false; } template inline constexpr bool operator==(const T &b, const Maybe &a) { return bool(a) ? (*a == b) : false; } template inline constexpr bool operator!=(const Maybe &a, const T &b) { return bool(a) ? !(*a == b) : true; } template inline constexpr bool operator!=(const T &b, const Maybe &a) { return bool(a) ? !(*a == b) : true; } template inline constexpr bool operator<(const Maybe &a, const T &b) { return bool(a) ? Less()(*a, b) : true; } template inline constexpr bool operator<(const T &b, const Maybe &a) { return bool(a) ? Less()(b, *a) : false; } template inline constexpr bool operator<=(const Maybe &a, const T &b) { return !(a > b); } template inline constexpr bool operator<=(const T &b, const Maybe &a) { return !(b > a); } template inline constexpr bool operator>(const Maybe &a, const T &b) { return bool(a) ? (b < a) : true; } template inline constexpr bool operator>(const T &b, const Maybe &a) { return bool(a) ? (a < b) : true; } template inline constexpr bool operator>=(const Maybe &a, const T &b) { return !(a < b); } template inline constexpr bool operator>=(const T &b, const Maybe &a) { return !(b < a); } /* make maybe */ template inline constexpr Maybe> make_maybe(T &&v) { return Maybe>(forward(v)); } } /* namespace ostd */ #endif