#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
|
|
}
|
|
}
|
|
};
|
|
}
|