General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

225 lines
4.2 KiB

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