diff --git a/luna/include/luna/Option.h b/luna/include/luna/Option.h new file mode 100644 index 00000000..49d58ee7 --- /dev/null +++ b/luna/include/luna/Option.h @@ -0,0 +1,139 @@ +#pragma once +#include +#include +#include + +template class Option +{ + public: + Option(const T& value) + { + m_storage.store_reference(value); + m_has_value = true; + } + + Option(T&& value) + { + m_storage.store_moved_reference(move(value)); + m_has_value = true; + } + + Option(const Option& other) + { + m_has_value = other.has_value(); + if (m_has_value) { m_storage.store_reference(other.m_storage.fetch_reference()); } + } + + Option(Option&& other) + { + m_has_value = other.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"); + T item = m_storage.fetch_reference(); + m_has_value = false; + m_storage.destroy(); + return move(item); + } + + 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(); + } + + struct ErrorHandle + { + private: + explicit ErrorHandle() + { + } + }; + + Option(ErrorHandle) + { + m_has_value = false; + } + + // For compatibility with TRY() + 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(ref); + } + + void destroy() + { + fetch_reference().~T(); + } + }; + Storage m_storage; + bool m_has_value{false}; +}; \ No newline at end of file