/* * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2021, Daniel Bertalan * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace AK { // NOTE: If you're here because of an internal compiler error in GCC 10.3.0+, // it's because of the following bug: // // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96745 // // Make sure you didn't accidentally make your destructor private before // you start bug hunting. :^) template class [[nodiscard]] Optional { public: using ValueType = T; ALWAYS_INLINE Optional() = default; #ifdef AK_HAS_CONDITIONALLY_TRIVIAL Optional(Optional const& other) requires(!IsCopyConstructible) = delete; Optional(Optional const& other) = default; Optional(Optional&& other) requires(!IsMoveConstructible) = delete; Optional& operator=(Optional const&) requires(!IsCopyConstructible || !IsDestructible) = delete; Optional& operator=(Optional const&) = default; Optional& operator=(Optional&& other) requires(!IsMoveConstructible || !IsDestructible) = delete; ~Optional() requires(!IsDestructible) = delete; ~Optional() = default; #endif ALWAYS_INLINE Optional(Optional const& other) #ifdef AK_HAS_CONDITIONALLY_TRIVIAL requires(!IsTriviallyCopyConstructible) #endif : m_has_value(other.m_has_value) { if (other.has_value()) { new (&m_storage) T(other.value()); } } ALWAYS_INLINE Optional(Optional&& other) : m_has_value(other.m_has_value) { if (other.has_value()) { new (&m_storage) T(other.release_value()); } } template ALWAYS_INLINE explicit(!IsConvertible) Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) : m_has_value(true) { new (&m_storage) T(forward(value)); } ALWAYS_INLINE Optional& operator=(Optional const& other) #ifdef AK_HAS_CONDITIONALLY_TRIVIAL requires(!IsTriviallyCopyConstructible || !IsTriviallyDestructible) #endif { if (this != &other) { clear(); m_has_value = other.m_has_value; if (other.has_value()) { new (&m_storage) T(other.value()); } } return *this; } ALWAYS_INLINE Optional& operator=(Optional&& other) { if (this != &other) { clear(); m_has_value = other.m_has_value; if (other.has_value()) { new (&m_storage) T(other.release_value()); } } return *this; } template ALWAYS_INLINE bool operator==(Optional const& other) const { return has_value() == other.has_value() && (!has_value() || value() == other.value()); } template ALWAYS_INLINE bool operator==(O const& other) const { return has_value() && value() == other; } ALWAYS_INLINE ~Optional() #ifdef AK_HAS_CONDITIONALLY_TRIVIAL requires(!IsTriviallyDestructible) #endif { clear(); } ALWAYS_INLINE void clear() { if (m_has_value) { value().~T(); m_has_value = false; } } template ALWAYS_INLINE void emplace(Parameters&&... parameters) { clear(); m_has_value = true; new (&m_storage) T(forward(parameters)...); } [[nodiscard]] ALWAYS_INLINE bool has_value() const { return m_has_value; } [[nodiscard]] ALWAYS_INLINE T& value() & { VERIFY(m_has_value); return *__builtin_launder(reinterpret_cast(&m_storage)); } [[nodiscard]] ALWAYS_INLINE T const& value() const& { VERIFY(m_has_value); return *__builtin_launder(reinterpret_cast(&m_storage)); } [[nodiscard]] ALWAYS_INLINE T value() && { return release_value(); } [[nodiscard]] T release_value() { VERIFY(m_has_value); T released_value = move(value()); value().~T(); m_has_value = false; return released_value; } [[nodiscard]] ALWAYS_INLINE T value_or(T const& fallback) const& { if (m_has_value) return value(); return fallback; } [[nodiscard]] ALWAYS_INLINE T value_or(T&& fallback) && { if (m_has_value) return move(value()); return move(fallback); } ALWAYS_INLINE T const& operator*() const { return value(); } ALWAYS_INLINE T& operator*() { return value(); } ALWAYS_INLINE T const* operator->() const { return &value(); } ALWAYS_INLINE T* operator->() { return &value(); } private: alignas(T) u8 m_storage[sizeof(T)]; bool m_has_value { false }; }; } using AK::Optional;