/* 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(MaybeStorage const &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(std::move(v.p_value)); } } constexpr MaybeStorage(Value const &v): p_value(v), p_engaged(true) {} constexpr MaybeStorage(Value &&v): p_value(std::move(v)), p_engaged(true) {} template constexpr MaybeStorage(InPlace, A &&...args): p_value(std::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(MaybeStorage const &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(std::move(v.p_value)); } } constexpr MaybeStorage(Value const &v): p_value(v), p_engaged(true) {} constexpr MaybeStorage(Value &&v): p_value(std::move(v)), p_engaged(true) {} template constexpr MaybeStorage(InPlace, A &&...args): p_value(std::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(Maybe const &) = default; Maybe(Maybe &&) = default; constexpr Maybe(Nothing) {} constexpr Maybe(Value const &v): Base(v) {} constexpr Maybe(Value &&v): Base(std::move(v)) {} template>> constexpr explicit Maybe(InPlace, A &&...args): Base(in_place, std::forward(args)...) {} template &, A...>> > constexpr explicit Maybe(InPlace, std::initializer_list il, A &&...args): Base(in_place, il, std::forward(args)...) {} ~Maybe() = default; Maybe &operator=(Nothing) { if (this->p_engaged) { this->p_value.~Value(); this->p_engaged = false; } return *this; } Maybe &operator=(Maybe const &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 = std::move(v.p_value); } } else { if (this->p_engaged) { this->p_value.~Value(); } else { ::new(address_of(this->p_value)) Value(std::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 = std::forward(v); } else { ::new(address_of(this->p_value)) Value(std::forward(v)); this->p_engaged = true; } return *this; } template>> void emplace(A &&...args) { *this = nothing; ::new(address_of(this->p_value)) Value(std::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, std::forward(args)...); this->p_engaged = true; } constexpr Value const *operator->() const { return address_of(this->p_value); } constexpr Value *operator->() { return address_of(this->p_value); } constexpr Value const &operator*() const & { return this->p_value; } constexpr Value &operator*() & { return this->p_value; } constexpr Value const &&operator*() const && { return std::move(this->p_value); } constexpr Value &&operator*() && { return std::move(this->p_value); } constexpr explicit operator bool() const { return this->p_engaged; } constexpr Value const &value() const & { return this->p_value; } constexpr Value &value() & { return this->p_value; } constexpr Value const &&value() const && { return std::move(this->p_value); } constexpr Value &&value() && { return std::move(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(std::forward(v)); } template constexpr Value value_or(U &&v) && { static_assert( IsMoveConstructible, "Maybe::value_or: T must be move constructible" ); static_assert( IsConvertible, "Maybe::value_or: U must be convertible to T" ); return this->p_engaged ? std::move(this->p_value) : Value(std::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(std::move(this->p_value)); this->p_value.~Value(); } else { ::new(address_of(this->p_value)) Value(std::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==(Maybe const &a, Maybe const &b) { return (bool(a) != bool(b)) ? false : (!bool(a) ? true : (*a == *b)); } template inline constexpr bool operator!=(Maybe const &a, Maybe const &b) { return !(a == b); } template inline constexpr bool operator<(Maybe const &a, Maybe const &b) { return !bool(b) ? false : (!bool(a) ? true : (*a < *b)); } template inline constexpr bool operator>(Maybe const &a, Maybe const &b) { return b < a; } template inline constexpr bool operator<=(Maybe const &a, Maybe const &b) { return !(b < a); } template inline constexpr bool operator>=(Maybe const &a, Maybe const &b) { return !(a < b); } /* maybe vs nothing */ template inline constexpr bool operator==(Maybe const &v, Nothing) { return !bool(v); } template inline constexpr bool operator==(Nothing, Maybe const &v) { return !bool(v); } template inline constexpr bool operator!=(Maybe const &v, Nothing) { return bool(v); } template inline constexpr bool operator!=(Nothing, Maybe const &v) { return bool(v); } template inline constexpr bool operator<(Maybe const &, Nothing) { return false; } template inline constexpr bool operator<(Nothing, Maybe const &v) { return bool(v); } template inline constexpr bool operator<=(Maybe const &v, Nothing) { return !bool(v); } template inline constexpr bool operator<=(Nothing, Maybe const &) { return true; } template inline constexpr bool operator>(Maybe const &v, Nothing) { return bool(v); } template inline constexpr bool operator>(Nothing, Maybe const &) { return false; } template inline constexpr bool operator>=(Maybe const &, Nothing) { return true; } template inline constexpr bool operator>=(Nothing, Maybe const &v) { return !bool(v); } /* maybe vs T */ template inline constexpr bool operator==(Maybe const &a, T const &b) { return bool(a) ? (*a == b) : false; } template inline constexpr bool operator==(T const &b, Maybe const &a) { return bool(a) ? (*a == b) : false; } template inline constexpr bool operator!=(Maybe const &a, T const &b) { return bool(a) ? !(*a == b) : true; } template inline constexpr bool operator!=(T const &b, Maybe const &a) { return bool(a) ? !(*a == b) : true; } template inline constexpr bool operator<(Maybe const &a, T const &b) { return bool(a) ? Less()(*a, b) : true; } template inline constexpr bool operator<(T const &b, Maybe const &a) { return bool(a) ? Less()(b, *a) : false; } template inline constexpr bool operator<=(Maybe const &a, T const &b) { return !(a > b); } template inline constexpr bool operator<=(T const &b, Maybe const &a) { return !(b > a); } template inline constexpr bool operator>(Maybe const &a, T const &b) { return bool(a) ? (b < a) : true; } template inline constexpr bool operator>(T const &b, Maybe const &a) { return bool(a) ? (a < b) : true; } template inline constexpr bool operator>=(Maybe const &a, T const &b) { return !(a < b); } template inline constexpr bool operator>=(T const &b, Maybe const &a) { return !(b < a); } /* make maybe */ template inline constexpr Maybe> make_maybe(T &&v) { return Maybe>(std::forward(v)); } template inline constexpr Maybe make_maybe(A &&...args) { return Maybe(in_place, std::forward(args)...); } template inline constexpr Maybe make_maybe(std::initializer_list il, A &&...args) { return Maybe(in_place, il, std::forward(args)...); } } /* namespace ostd */ #endif