#pragma once #include #include #include template class Option { public: Option(const T& value) : m_has_value(true) { m_storage.store_reference(value); } Option(T&& value) : m_has_value(true) { m_storage.store_moved_reference(move(value)); } Option(const Option& other) : m_has_value(other.m_has_value) { if (m_has_value) { m_storage.store_reference(other.m_storage.fetch_reference()); } } Option(Option&& other) : m_has_value(other.m_has_value) { other.m_has_value = false; if (m_has_value) { m_storage.store_moved_reference(move(other.m_storage.fetch_reference())); } } Option() : m_has_value(false) { } bool has_value() const { return m_has_value; } T value() const { expect(has_value(), "Option::value called on an empty Option"); return m_storage.fetch_reference(); } T release_value() { expect(has_value(), "Option::release_value called on an empty Option"); m_has_value = false; return move(m_storage.fetch_reference()); } T value_or(const T& other) const { if (has_value()) return m_storage.fetch_reference(); return other; } bool try_set_value(T& ref) const { if (!has_value()) return false; ref = m_storage.fetch_reference(); return true; } ~Option() { if (has_value()) m_storage.destroy(); } // For compatibility with TRY() struct ErrorHandle { private: explicit ErrorHandle() { } }; Option(const ErrorHandle&) : m_has_value(false) { } ErrorHandle release_error() { expect(!has_value(), "Option::release_error called on a non-empty Option"); return ErrorHandle{}; } private: struct Storage { u8 buffer[sizeof(T)]; T* fetch_ptr() { return (T*)buffer; } T& fetch_reference() { return *fetch_ptr(); } const T* fetch_ptr() const { return (const T*)buffer; } const T& fetch_reference() const { return *fetch_ptr(); } void store_reference(const T& ref) { new (buffer) T(ref); } void store_moved_reference(T&& ref) { new (buffer) T(move(ref)); } void destroy() { fetch_reference().~T(); } }; Storage m_storage; bool m_has_value{false}; };