#pragma once namespace nall { template struct variant_size { static constexpr uint size = max(sizeof(T), variant_size::size); }; template struct variant_size { static constexpr uint size = sizeof(T); }; template struct variant_index { static constexpr uint index = is_same_v ? Index : variant_index::index; }; template struct variant_index { static constexpr uint index = is_same_v ? Index : 0; }; template struct variant_copy { constexpr variant_copy(uint index, uint assigned, void* target, void* source) { if(index == assigned) new(target) T(*((T*)source)); else variant_copy(index + 1, assigned, target, source); } }; template struct variant_copy { constexpr variant_copy(uint index, uint assigned, void* target, void* source) { if(index == assigned) new(target) T(*((T*)source)); } }; template struct variant_move { constexpr variant_move(uint index, uint assigned, void* target, void* source) { if(index == assigned) new(target) T(move(*((T*)source))); else variant_move(index + 1, assigned, target, source); } }; template struct variant_move { constexpr variant_move(uint index, uint assigned, void* target, void* source) { if(index == assigned) new(target) T(move(*((T*)source))); } }; template struct variant_destruct { constexpr variant_destruct(uint index, uint assigned, void* data) { if(index == assigned) ((T*)data)->~T(); else variant_destruct(index + 1, assigned, data); } }; template struct variant_destruct { constexpr variant_destruct(uint index, uint assigned, void* data) { if(index == assigned) ((T*)data)->~T(); } }; template struct variant_equals { constexpr auto operator()(uint index, uint assigned) const -> bool { if(index == assigned) return is_same_v; return variant_equals()(index + 1, assigned); } }; template struct variant_equals { constexpr auto operator()(uint index, uint assigned) const -> bool { if(index == assigned) return is_same_v; return false; } }; template struct variant final { //final as destructor is not virtual variant() : assigned(0) {} variant(const variant& source) { operator=(source); } variant(variant&& source) { operator=(move(source)); } template variant(const T& value) { operator=(value); } template variant(T&& value) { operator=(move(value)); } ~variant() { reset(); } explicit operator bool() const { return assigned; } template explicit constexpr operator T&() { return get(); } template explicit constexpr operator const T&() const { return get(); } template constexpr auto is() const -> bool { return variant_equals()(1, assigned); } template constexpr auto get() -> T& { static_assert(variant_index<1, T, P...>::index, "type not in variant"); struct variant_bad_cast{}; if(!is()) throw variant_bad_cast{}; return *((T*)data); } template constexpr auto get() const -> const T& { static_assert(variant_index<1, T, P...>::index, "type not in variant"); struct variant_bad_cast{}; if(!is()) throw variant_bad_cast{}; return *((const T*)data); } template constexpr auto get(const T& fallback) const -> const T& { if(!is()) return fallback; return *((const T*)data); } auto reset() -> void { if(assigned) variant_destruct(1, assigned, (void*)data); assigned = 0; } auto& operator=(const variant& source) { reset(); if(assigned = source.assigned) variant_copy(1, source.assigned, (void*)data, (void*)source.data); return *this; } auto& operator=(variant&& source) { reset(); if(assigned = source.assigned) variant_move(1, source.assigned, (void*)data, (void*)source.data); source.assigned = 0; return *this; } template auto& operator=(const T& value) { static_assert(variant_index<1, T, P...>::index, "type not in variant"); reset(); new((void*)&data) T(value); assigned = variant_index<1, T, P...>::index; return *this; } template auto& operator=(T&& value) { static_assert(variant_index<1, T, P...>::index, "type not in variant"); reset(); new((void*)&data) T(move(value)); assigned = variant_index<1, T, P...>::index; return *this; } private: alignas(P...) char data[variant_size::size]; uint assigned; }; }