|
|
- #pragma once
- #include <type_traits>
- #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<T>::value || std::is_fundamental<T>::value
- >
- class optional;
-
- template<typename T, typename allocator, bool copy_allocator>
- class optional<T, allocator, copy_allocator, true>{
- bool ready = false;
- char buffer[sizeof(T)];
- typename gp::either<
- copy_allocator,
- allocator,
- gp::reference_wrapper<allocator>
- >::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<T*>(buffer);
- }
- };
-
- template<typename T, typename allocator, bool copy_allocator>
- class optional<T, allocator, copy_allocator, false>{
- bool ready = false;
- T* ptr;
- typename gp::either<
- copy_allocator,
- allocator,
- gp::reference_wrapper<allocator>
- >::type alloc;
- public:
- constexpr optional(allocator p = allocator{})
- : ready{false}
- , alloc(p)
- {}
-
- constexpr optional(nullopt_t, allocator p = allocator{})
- : ready{false}
- , alloc(p)
- {}
-
- template<typename U>
- constexpr optional(U& value, allocator p = allocator{})
- : ready{true}
- , alloc(p)
- {
- ptr = new U(value); // TODO: Use allocators
- }
-
- template<typename U>
- 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<typename U>
- optional& operator=(U& value) {
- if(ready) {
- if constexpr (std::is_same_v<T, U>) {
- *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;
- }
-
- template<typename U>
- optional& operator=(U&& value) {
- if(ready) {
- if constexpr (std::is_same_v<T, U>) {
- *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;
- }
-
- template<>
- optional& operator=<optional>(optional& value) {
- if(ready) {
- *ptr = value;
- } else {
- ready = true;
- ptr = new optional(value.value()); // TODO: Use allocators
- }
- return *this;
- }
-
- template<>
- optional& operator=<optional>(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;
- }
- }
-
- 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
- }
- }
- };
- }
|