#pragma once #include #include #include #include "gp_config.hpp" #include "gp/exception.hpp" #include "gp/memory.hpp" #include "gp/function.hpp" namespace gp{ template< typename Enable, typename ...T> class variant; template class variant::value,int>::type, T...>{ std::size_t index = std::numeric_limits::max(); char buffer[max_size]; gp::function dtor = [](void*){}; public: template::value,int>::type> constexpr variant(U& value) : index{r_index_of::value} { new(buffer) U(value); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } template::value,int>::type> constexpr variant(U&& value) : index{r_index_of::value} { new(buffer) U(std::move(value)); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } template::value,int>::type> void operator=(U& value) { if(index != std::numeric_limits::max()) { dtor((void*)buffer); } index = r_index_of::value; new(buffer) U(value); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } template::value,int>::type> void operator=(U&& value) { if(index != std::numeric_limits::max()) { dtor((void*)buffer); } index = r_index_of::value; new(buffer) U(std::move(value)); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } ~variant() { if(index != std::numeric_limits::max()) { dtor((void*)buffer); } } template constexpr U& value() { if constexpr (gp_config::has_exceptions) { if(r_index_of::value != index) { throw bad_variant_access{}; } } return *reinterpret_cast(buffer); } template constexpr U& is_a() { if(r_index_of::value == index) { return true; } else { return false; } } }; template class variant>::type, T...>{ std::size_t index = std::numeric_limits::max(); void* ptr; gp::function dtor = [](void*){}; public: template::value,int>::type> constexpr variant(U& value) : index{r_index_of::value} { ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(value); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } template::value,int>::type> constexpr variant(U&& value) : index{r_index_of::value} { ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(std::move(value)); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } template::value,int>::type> void operator=(U& value) { if(index != std::numeric_limits::max()) { dtor(ptr); default_memory_allocator<>{}.deallocate(ptr); } index = r_index_of::value; ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(value); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } template::value,int>::type> void operator=(U&& value) { if(index != std::numeric_limits::max()) { dtor(ptr); default_memory_allocator<>{}.deallocate(ptr); } index = r_index_of::value; ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(std::move(value)); dtor = gp::function([](void* thing){((U*)thing)->~U();}); } ~variant() { if(index != std::numeric_limits::max()) { dtor(ptr); default_memory_allocator<>{}.deallocate(ptr); } } template constexpr U& value() { if constexpr (gp_config::has_exceptions) { if(r_index_of::value != index) { throw bad_variant_access{}; } } return *reinterpret_cast(ptr); } template constexpr U& is_a() { if(r_index_of::value == index) { return true; } else { return false; } } }; }