#ifndef AL_OPTIONAL_H #define AL_OPTIONAL_H #include #include #include #include "almalloc.h" namespace al { struct nullopt_t { }; struct in_place_t { }; constexpr nullopt_t nullopt{}; constexpr in_place_t in_place{}; template::value> struct optional_storage; template struct optional_storage { bool mHasValue{false}; union { char mDummy; T mValue; }; optional_storage() { } template explicit optional_storage(in_place_t, Args&& ...args) : mHasValue{true}, mValue{std::forward(args)...} { } ~optional_storage() = default; }; template struct optional_storage { bool mHasValue{false}; union { char mDummy; T mValue; }; optional_storage() { } template explicit optional_storage(in_place_t, Args&& ...args) : mHasValue{true}, mValue{std::forward(args)...} { } ~optional_storage() { if(mHasValue) al::destroy_at(std::addressof(mValue)); } }; template class optional { using storage_t = optional_storage; storage_t mStore; template void doConstruct(Args&& ...args) { ::new(std::addressof(mStore.mValue)) T{std::forward(args)...}; mStore.mHasValue = true; } public: using value_type = T; optional() = default; optional(nullopt_t) noexcept { } optional(const optional &rhs) { if(rhs) doConstruct(*rhs); } optional(optional&& rhs) { if(rhs) doConstruct(std::move(*rhs)); } template explicit optional(in_place_t, Args&& ...args) : mStore{al::in_place, std::forward(args)...} { } ~optional() = default; optional& operator=(nullopt_t) noexcept { reset(); return *this; } std::enable_if_t::value && std::is_copy_assignable::value, optional&> operator=(const optional &rhs) { if(!rhs) reset(); else if(*this) mStore.mValue = *rhs; else doConstruct(*rhs); return *this; } std::enable_if_t::value && std::is_move_assignable::value, optional&> operator=(optional&& rhs) { if(!rhs) reset(); else if(*this) mStore.mValue = std::move(*rhs); else doConstruct(std::move(*rhs)); return *this; } template std::enable_if_t::value && std::is_assignable::value && !std::is_same, optional>::value && (!std::is_same, T>::value || !std::is_scalar::value), optional&> operator=(U&& rhs) { if(*this) mStore.mValue = std::forward(rhs); else doConstruct(std::forward(rhs)); return *this; } const T* operator->() const { return std::addressof(mStore.mValue); } T* operator->() { return std::addressof(mStore.mValue); } const T& operator*() const& { return this->mValue; } T& operator*() & { return mStore.mValue; } const T&& operator*() const&& { return std::move(mStore.mValue); } T&& operator*() && { return std::move(mStore.mValue); } operator bool() const noexcept { return mStore.mHasValue; } bool has_value() const noexcept { return mStore.mHasValue; } T& value() & { return mStore.mValue; } const T& value() const& { return mStore.mValue; } T&& value() && { return std::move(mStore.mValue); } const T&& value() const&& { return std::move(mStore.mValue); } template T value_or(U&& defval) const& { return bool{*this} ? **this : static_cast(std::forward(defval)); } template T value_or(U&& defval) && { return bool{*this} ? std::move(**this) : static_cast(std::forward(defval)); } void reset() noexcept { if(mStore.mHasValue) al::destroy_at(std::addressof(mStore.mValue)); mStore.mHasValue = false; } }; template inline optional> make_optional(T&& arg) { return optional>{in_place, std::forward(arg)}; } template inline optional make_optional(Args&& ...args) { return optional{in_place, std::forward(args)...}; } template inline optional make_optional(std::initializer_list il, Args&& ...args) { return optional{in_place, il, std::forward(args)...}; } } // namespace al #endif /* AL_OPTIONAL_H */