#pragma once #include #include "gp_config.hpp" #include "gp/exception.hpp" #include "gp/algorithm/move.hpp" #include "gp/algorithm/modifiers.hpp" namespace gp{ struct nullopt_t{}; constexpr nullopt_t nullopt; template< typename T, typename allocator = gp_config::memory_module::default_allocator, bool copy_allocator = false, bool B = std::is_final::value || std::is_fundamental::value > class optional; template class optional{ bool ready = false; char buffer[sizeof(T)]; typename gp::either< copy_allocator, allocator, gp::reference_wrapper >::type alloc; public: constexpr optional(allocator p = allocator{}) : ready{false} , alloc(p) {} constexpr optional(nullopt_t, allocator p = allocator{}) : ready{false} , alloc(p) {} constexpr optional(T& value, allocator p = allocator{}) : ready{true} , alloc(p) { new(buffer) T(value); } constexpr optional(T&& value, allocator p = allocator{}) : ready{true} , alloc(p) { 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); } }; template class optional{ bool ready = false; T* ptr; typename gp::either< copy_allocator, allocator, gp::reference_wrapper >::type alloc; public: constexpr optional(allocator p = allocator{}) : ready{false} , alloc(p) {} constexpr optional(nullopt_t, allocator p = allocator{}) : ready{false} , alloc(p) {} template constexpr optional(U& value, allocator p = allocator{}) : ready{true} , alloc(p) { ptr = new U(value); // TODO: Use allocators } template constexpr optional(U&& value, allocator p = allocator{}) : ready{true} , alloc(p) { ptr = new U(gp::move(value)); // TODO: Use allocators } optional& operator=(nullopt_t) { if(ready) { delete ptr; ready = false; } return *this; } template optional& operator=(U& value) { if(ready) { if constexpr (std::is_same_v) { *ptr = value; } else { delete ptr; // TODO: Use allocators ptr = new U(value); // TODO: Use allocators } } else { ready = true; ptr = new U(value); // TODO: Use allocators } return *this; } optional& operator=(optional&& value){ if(ready) { delete ptr; // TODO: Use allocators } if(value.ready) { ptr = value.ptr; value.ready = false; ready = true; return *this; } else { ready = false; return *this; } } template optional& operator=(U&& value) { if(ready) { if constexpr (std::is_same_v) { *ptr = gp::move(value); } else { delete ptr; // TODO: Use allocators ptr = new U(gp::move(value)); // TODO: Use allocators } } else { ready = true; ptr = new U(gp::move(value)); // TODO: Use allocators } return *this; } operator T&() { gp_config::assertion(ready, "bad optional access"); return *ptr; } 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 *ptr; } ~optional() { if(ready) { delete ptr; // TODO: Use allocators } } }; }