299 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)
 | |
| #define MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED 1
 | |
| 
 | |
| #include <cstdint>
 | |
| #include <utility>
 | |
| #include "../debug/assert.hpp"
 | |
| #include "../util/concepts.hpp"
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| 
 | |
| //
 | |
| // public defines
 | |
| //
 | |
| 
 | |
| //
 | |
| // public constants
 | |
| //
 | |
| 
 | |
| //
 | |
| // public types
 | |
| //
 | |
| 
 | |
| namespace impl
 | |
| {
 | |
| template<typename T>
 | |
| struct OptionalStorage
 | |
| {
 | |
|     alignas(T) std::uint8_t data[sizeof(T)]; // NOLINT(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
 | |
|     std::uint8_t used = 0;
 | |
| 
 | |
|     [[nodiscard]]
 | |
|     constexpr bool empty() const noexcept { return !used; }
 | |
| 
 | |
|     template<typename... TArgs>
 | |
|     constexpr void emplace(TArgs&&... args) noexcept
 | |
|     {
 | |
|         MIJIN_ASSERT(!used, "Attempting to emplace in an already set OptionalStorage!");
 | |
|         used = 1;
 | |
|         ::new (data) T(std::forward<TArgs>(args)...);
 | |
|     }
 | |
| 
 | |
|     void clear()
 | |
|     {
 | |
|         MIJIN_ASSERT(used, "Attempting to clear an empty OptionalStorage!");
 | |
|         get().~T();
 | |
|         used = 0;
 | |
|     }
 | |
| 
 | |
|     [[nodiscard]] constexpr T& get() noexcept { return *reinterpret_cast<T*>(data); }
 | |
|     [[nodiscard]] constexpr const T& get() const noexcept { return *reinterpret_cast<const T*>(data); }
 | |
| };
 | |
| 
 | |
| template<pointer_type T>
 | |
| struct OptionalStorage<T>
 | |
| {
 | |
|     static constexpr T invalidPointer() noexcept { return reinterpret_cast<T>(1); }
 | |
|     
 | |
|     T ptr = invalidPointer();
 | |
| 
 | |
|     [[nodiscard]]
 | |
|     constexpr bool empty() const noexcept { return ptr == invalidPointer(); }
 | |
| 
 | |
|     void emplace(T value) noexcept
 | |
|     {
 | |
|         ptr = value;
 | |
|     }
 | |
| 
 | |
|     void clear()
 | |
|     {
 | |
|         ptr = invalidPointer();
 | |
|     }
 | |
| 
 | |
|     [[nodiscard]] T& get() noexcept { return ptr; }
 | |
|     [[nodiscard]] const T& get() const noexcept { return ptr; }
 | |
| };
 | |
| 
 | |
| template<reference_type T>
 | |
| struct OptionalStorage<T>
 | |
| {
 | |
|     using pointer_t = std::remove_reference_t<T>*;
 | |
| 
 | |
|     pointer_t ptr = nullptr;
 | |
| 
 | |
|     [[nodiscard]]
 | |
|     constexpr bool empty() const noexcept { return ptr == nullptr; }
 | |
| 
 | |
|     void emplace(T value) noexcept
 | |
|     {
 | |
|         ptr = &value;
 | |
|     }
 | |
| 
 | |
|     void clear()
 | |
|     {
 | |
|         ptr = nullptr;
 | |
|     }
 | |
| 
 | |
|     T get() noexcept { return *ptr; }
 | |
|     const T get() const noexcept { return *ptr; } // NOLINT(readability-const-return-type) T already is a reference
 | |
| };
 | |
| }
 | |
| struct NullOptional {};
 | |
| static constexpr NullOptional NULL_OPTIONAL;
 | |
| 
 | |
| template<typename TValue>
 | |
| class Optional
 | |
| {
 | |
| private:
 | |
|     impl::OptionalStorage<TValue> storage_;
 | |
| public:
 | |
|     constexpr Optional() = default;
 | |
|     constexpr Optional(NullOptional) noexcept {}
 | |
|     inline Optional(const Optional& other) noexcept;
 | |
|     inline Optional(Optional&& other) noexcept;
 | |
|     inline Optional(TValue value) noexcept;
 | |
|     inline ~Optional() noexcept;
 | |
| public:
 | |
|     Optional& operator =(const Optional& other) noexcept;
 | |
|     Optional& operator =(Optional&& other) noexcept;
 | |
|     Optional& operator =(TValue value) noexcept;
 | |
|     Optional& operator =(NullOptional) noexcept;
 | |
| public:
 | |
|     [[nodiscard]]
 | |
|     constexpr bool operator ==(NullOptional) const noexcept { return empty(); }
 | |
|     template<typename TOther>
 | |
|     [[nodiscard]]
 | |
|     constexpr bool operator ==(const Optional<TOther>& other) const noexcept;
 | |
|     template<typename T>
 | |
|     [[nodiscard]]
 | |
|     constexpr bool operator ==(const T& value) const noexcept;
 | |
|     template<typename T>
 | |
|     [[nodiscard]]
 | |
|     constexpr bool operator !=(const T& value) const noexcept { return !(*this == value); }
 | |
|     [[nodiscard]]
 | |
|     constexpr explicit operator bool() const noexcept { return !empty(); }
 | |
|     [[nodiscard]]
 | |
|     constexpr bool operator !() const noexcept { return empty(); }
 | |
| public:
 | |
|     template<typename... Types>
 | |
|     void emplace(Types&&... params) noexcept;
 | |
| public:
 | |
|     [[nodiscard]] inline std::remove_reference_t<TValue>& get() noexcept;
 | |
|     [[nodiscard]] inline const std::remove_reference_t<TValue>& get() const noexcept;
 | |
|     [[nodiscard]] constexpr bool empty() const noexcept { return storage_.empty(); }
 | |
|     inline void reset() noexcept;
 | |
| };
 | |
| 
 | |
| //
 | |
| // public functions
 | |
| //
 | |
| 
 | |
| template<typename TValue>
 | |
| Optional<TValue>::Optional(const Optional& other) noexcept
 | |
| {
 | |
|     if (other) {
 | |
|         emplace(other.get());
 | |
|     }
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| Optional<TValue>::Optional(Optional&& other) noexcept
 | |
| {
 | |
|     if (other)
 | |
|     {
 | |
|         emplace(std::move(other.get()));
 | |
|         other.reset();
 | |
|     }
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| Optional<TValue>::Optional(TValue value) noexcept
 | |
| {
 | |
|     if constexpr (std::is_reference_v<TValue>) {
 | |
|         emplace(value);
 | |
|     }
 | |
|     else {
 | |
|         emplace(std::move(value));
 | |
|     }
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| Optional<TValue>::~Optional() noexcept
 | |
| {
 | |
|     reset();
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| auto Optional<TValue>::operator =(const Optional& other) noexcept -> Optional&
 | |
| {
 | |
|     if (&other == this) {
 | |
|         return *this;
 | |
|     }
 | |
|     reset();
 | |
|     if (other) {
 | |
|         emplace(other.get());
 | |
|     }
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| auto Optional<TValue>::operator =(Optional&& other) noexcept -> Optional&
 | |
| {
 | |
|     if (&other == this) {
 | |
|         return *this;
 | |
|     }
 | |
|     reset();
 | |
|     if (other)
 | |
|     {
 | |
|         emplace(std::move(other.get()));
 | |
|         other.reset();
 | |
|     }
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| auto Optional<TValue>::operator =(TValue value) noexcept -> Optional&
 | |
| {
 | |
|     if constexpr (std::is_reference_v<TValue>) {
 | |
|         emplace(value);
 | |
|     }
 | |
|     else {
 | |
|         emplace(std::move(value));
 | |
|     }
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| auto Optional<TValue>::operator =(NullOptional) noexcept -> Optional&
 | |
| {
 | |
|     reset();
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| template<typename TOther>
 | |
| constexpr bool Optional<TValue>::operator ==(const Optional<TOther>& other) const noexcept
 | |
| {
 | |
|     if (empty())
 | |
|     {
 | |
|         return other.empty();
 | |
|     }
 | |
|     if (!other.empty())
 | |
|     {
 | |
|         return get() == other.get();
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| template<typename T>
 | |
| constexpr bool Optional<TValue>::operator ==(const T& value) const noexcept
 | |
| {
 | |
|     if (empty())
 | |
|     {
 | |
|         return false;
 | |
|     }
 | |
|     return get() == value;
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| template<typename... Types>
 | |
| void Optional<TValue>::emplace(Types&&... params) noexcept
 | |
| {
 | |
|     reset();
 | |
|     storage_.emplace(std::forward<Types>(params)...);
 | |
|     MIJIN_ASSERT(!empty(), "Something is wrong.");
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| inline std::remove_reference_t<TValue>& Optional<TValue>::get() noexcept
 | |
| {
 | |
|     MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
 | |
|     return storage_.get();
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| inline const std::remove_reference_t<TValue>& Optional<TValue>::get() const noexcept
 | |
| {
 | |
|     MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
 | |
|     return storage_.get();
 | |
| }
 | |
| 
 | |
| template<typename TValue>
 | |
| inline void Optional<TValue>::reset() noexcept
 | |
| {
 | |
|     if (empty()) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     storage_.clear();
 | |
| }
 | |
| 
 | |
| } // namespace mijin
 | |
| 
 | |
| #endif // !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)
 |