#pragma once #include #include "gp_config.hpp" #include "gp/exception.hpp" #include "gp/algorithm/move.hpp" #include "gp/memory.hpp" namespace gp{ struct nullopt_t{}; constexpr nullopt_t nullopt; // TODO: Add allocators to the template template::value || std::is_fundamental::value> class optional; template class optional{ bool ready = false; char buffer[sizeof(T)]; public: constexpr optional() : ready{false} {} constexpr optional(nullopt_t) : ready{false} {} constexpr optional(T& value) : ready{true} { new(buffer) T(value); } constexpr optional(T&& value) : ready{true} { new(buffer) T(gp::move(value)); } optional operator=(nullopt_t) { if(ready) { ((T*)buffer)->~T(); ready = false; } return *this; } optional operator=(T& value) { if(ready) { *(T*)buffer = value; } else { ready = true; new(buffer) T(value); } return *this; } optional operator=(T&& value) { if(ready) { *(T*)buffer = gp::move(value); } else { ready = true; new(buffer) T(gp::move(value)); } return *this; } constexpr bool has_value() { return ready; } constexpr T& value() { if constexpr (gp_config::has_exceptions) { if(!ready) { throw bad_optional{}; } } else { gp_config::assertion(ready, "bad optional access"); } return *reinterpret_cast(buffer); } }; // TODO: Add allocators to the template template class optional{ bool ready = false; void* ptr; public: constexpr optional() : ready{false} {} constexpr optional(nullopt_t) : ready{false} {} // TODO: Add typesafe generic assignment constexpr optional(T& value) : ready{true} { ptr = (void*)new T(value); // TODO: Use allocators } // TODO: Add typesafe generic assignment constexpr optional(T&& value) : ready{true} { ptr = (void*)new T(gp::move(value)); // TODO: Use allocators } optional operator=(nullopt_t) { if(ready) { delete (T*)ptr; ready = false; } return *this; } // TODO: Add typesafe generic assignment optional operator=(T& value) { if(ready) { *(T*)ptr = value; } else { ready = true; ptr = (void*)new T(value); // TODO: Use allocators } return *this; } // TODO: Add typesafe generic assignment optional operator=(T&& value) { if(ready) { *(T*)ptr = gp::move(value); } else { ready = true; ptr = (void*)new T(gp::move(value)); // TODO: Use allocators } return *this; } constexpr bool has_value() { return ready; } constexpr T& value() { if constexpr (gp_config::has_exceptions) { if(!ready) { throw bad_optional{}; } } else { gp_config::assertion(ready, "bad optional access"); } return *reinterpret_cast(ptr); } }; }