343 lines
11 KiB
C++
343 lines
11 KiB
C++
#include <cstring>
|
|
|
|
template <typename T, typename ...Types>
|
|
struct get_index_by_type {
|
|
static const size_t value = -1;
|
|
};
|
|
|
|
template <typename T, typename Head, typename ...Tail>
|
|
struct get_index_by_type<T, Head, Tail...> {
|
|
static const size_t value = std::is_same_v<T, Head> ? 0 : get_index_by_type<T, Tail...>::value + 1;
|
|
};
|
|
|
|
template<typename T, typename ...Tail>
|
|
static const size_t get_index_by_type_v = get_index_by_type<T, Tail...>::value;
|
|
|
|
template<typename T, typename... Types>
|
|
struct t_is_on_of_types {
|
|
static const bool value = false;
|
|
};
|
|
|
|
template<typename T, typename Head, typename... Tail>
|
|
struct t_is_on_of_types<T, Head, Tail...> {
|
|
static const bool value = std::is_same_v<T, Head> || t_is_on_of_types<T, Tail...>::value;
|
|
};
|
|
|
|
template<typename T, typename... Types>
|
|
static const bool t_is_on_of_types_v = t_is_on_of_types<T, Types...>::value;
|
|
|
|
template<size_t N, typename... Types>
|
|
struct get_type_by_index {};
|
|
|
|
template<typename T, typename... Types>
|
|
struct get_type_by_index<0, T, Types...> {
|
|
using value = T;
|
|
};
|
|
|
|
template<size_t N, typename T, typename... Types>
|
|
struct get_type_by_index<N, T, Types...> {
|
|
using value = typename get_type_by_index<N - 1, Types...>::value;
|
|
};
|
|
|
|
|
|
template<typename T, typename... Types>
|
|
struct count_types_in_package {};
|
|
|
|
template<typename T, typename Head, typename... Tail>
|
|
struct count_types_in_package<T, Head, Tail...> {
|
|
static const size_t value = std::is_same_v<T, Head> + count_types_in_package<T, Tail...>::value;
|
|
};
|
|
|
|
template<typename T>
|
|
struct count_types_in_package<T> {
|
|
static const size_t value = 0;
|
|
};
|
|
|
|
|
|
template<size_t N, typename... Types>
|
|
using get_type_by_index_v = typename get_type_by_index<N, Types...>::value;
|
|
|
|
template<typename... Types>
|
|
struct get_max_size {
|
|
static const size_t value = 0;
|
|
};
|
|
|
|
template<typename Head, typename... Tail>
|
|
struct get_max_size<Head, Tail...> {
|
|
static const size_t value = std::max(sizeof(Head), get_max_size<Tail...>::value);
|
|
};
|
|
|
|
template<typename... Types>
|
|
static const size_t get_max_size_v = get_max_size<Types...>::value;
|
|
|
|
template <typename ...Types>
|
|
class Variant;
|
|
|
|
template<typename T, typename... Types>
|
|
struct VariantAlternative;
|
|
|
|
template<typename T, typename... Types>
|
|
struct VariantAlternative {
|
|
public:
|
|
VariantAlternative() {}
|
|
VariantAlternative(const VariantAlternative&) {}
|
|
|
|
VariantAlternative(const T& value) {
|
|
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(value);
|
|
}
|
|
VariantAlternative(T&& value) {
|
|
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(std::move(value));
|
|
}
|
|
VariantAlternative& operator=(const T& value) {
|
|
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
|
me->destroy();
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(value);
|
|
|
|
return *this;
|
|
}
|
|
VariantAlternative& operator=(T&& value) {
|
|
auto* me = static_cast<Variant<Types...>*>(this);
|
|
me->destroy();
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(std::move(value));
|
|
return *this;
|
|
}
|
|
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_constructible_v<U, K>, bool> = true>
|
|
VariantAlternative(const K& value) {
|
|
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(value);
|
|
}
|
|
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_constructible_v<U, K>, bool> = true>
|
|
VariantAlternative(K&& value) {
|
|
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(std::forward<K>(value));
|
|
}
|
|
|
|
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_assignable_v<U, K>, bool> = true>
|
|
VariantAlternative& operator=(const K& value) {
|
|
auto* me = static_cast<Variant<Types...>*>(this);
|
|
me->destroy();
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(value);
|
|
return *this;
|
|
}
|
|
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_assignable_v<U, K>, bool> = true>
|
|
VariantAlternative& operator=(K&& value) {
|
|
auto* me = static_cast<Variant<Types...>*>(this);
|
|
me->destroy();
|
|
me->index_ = get_index_by_type_v<T, Types...>;
|
|
new(me->object_) T(std::forward<K>(value));
|
|
return *this;
|
|
}
|
|
|
|
~VariantAlternative() {
|
|
auto* me = static_cast<Variant<Types...>*>(this);
|
|
if (get_index_by_type_v<T, Types...> == me->index_) {
|
|
me->destroy();
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T, typename... TTypes>
|
|
bool holds_alternative(const Variant<TTypes...>& another) {
|
|
return another.template holds_alternative<T>();
|
|
}
|
|
|
|
template<typename T, typename... TTypes>
|
|
const T& get(const Variant<TTypes...>& another) {
|
|
static_assert(t_is_on_of_types_v<T, TTypes...>);
|
|
static_assert(count_types_in_package<T, TTypes...>::value == 1);
|
|
|
|
if (another.template holds_alternative<T>()) {
|
|
return *reinterpret_cast<T*>(std::launder(const_cast<Variant<TTypes...>&>(another).object_));
|
|
} else {
|
|
throw std::bad_cast();
|
|
}
|
|
}
|
|
|
|
template<typename T, typename... TTypes>
|
|
T& get(Variant<TTypes...>& another) {
|
|
return const_cast<T&>(get<T>(const_cast<const Variant<TTypes...>&>(another)));
|
|
}
|
|
|
|
template<typename T, typename... TTypes>
|
|
T&& get(Variant<TTypes...>&& another) {
|
|
return std::move(const_cast<T>(get<T>(const_cast<const Variant<TTypes...>&>(another))));
|
|
}
|
|
|
|
template<size_t N, typename... TTypes>
|
|
const get_type_by_index_v<N, TTypes...>& get(const Variant<TTypes...>& another) {
|
|
return get<get_type_by_index_v<N, TTypes...>>(another);
|
|
}
|
|
|
|
template<size_t N, typename... TTypes>
|
|
get_type_by_index_v<N, TTypes...>& get(Variant<TTypes...>& another) {
|
|
using R = get_type_by_index_v<N, TTypes...>;
|
|
return const_cast<R&>(get<R>(const_cast<const Variant<TTypes...>&>(another)));
|
|
}
|
|
|
|
template<size_t N, typename... TTypes>
|
|
get_type_by_index_v<N, TTypes...>&& get(Variant<TTypes...>&& another) {
|
|
using R = get_type_by_index_v<N, TTypes...>;
|
|
return std::move(const_cast<R>(get<R>(const_cast<const Variant<TTypes...>&>(another))));
|
|
}
|
|
|
|
template <typename ...Types>
|
|
class Variant : public VariantAlternative<Types, Types...>... {
|
|
template<typename TT, typename... TTypes>
|
|
friend class VariantAlternative;
|
|
|
|
|
|
private:
|
|
static const size_t size_variant = get_max_size_v<Types...>;
|
|
alignas(Types...) int8_t object_[size_variant];
|
|
size_t index_;
|
|
template<class T>
|
|
bool holds_alternative() const {
|
|
if (get_index_by_type_v<T, Types...> == -1)
|
|
throw std::bad_cast();
|
|
else
|
|
return get_index_by_type_v<T, Types...> == index_;
|
|
}
|
|
template<size_t N = 0>
|
|
void destroy() {
|
|
if (index_ == N) {
|
|
index_ = -1;
|
|
using R = get_type_by_index_v<N, Types...>;
|
|
reinterpret_cast<R*>(object_)->~R();
|
|
} else {
|
|
if constexpr (N + 1 < sizeof...(Types)) {
|
|
destroy<N + 1>();
|
|
}
|
|
}
|
|
}
|
|
template<size_t N = 0>
|
|
void copy_constructor(const Variant& another) {
|
|
if (another.index_ == N) {
|
|
using R = get_type_by_index_v<N, Types...>;
|
|
new(object_) R(*reinterpret_cast<R const*>(std::launder(another.object_)));
|
|
index_ = another.index_;
|
|
} else {
|
|
if constexpr (N + 1 < sizeof...(Types)) {
|
|
copy_constructor<N + 1>(another);
|
|
}
|
|
}
|
|
}
|
|
template<size_t N = 0>
|
|
void move_constructor(Variant&& another) {
|
|
if (this == &another) return;
|
|
if (another.index_ == N) {
|
|
using R = get_type_by_index_v<N, Types...>;
|
|
|
|
new(object_) R(std::move(*reinterpret_cast<R*>(std::launder(another.object_))));
|
|
index_ = another.index_;
|
|
} else {
|
|
if constexpr (N + 1 < sizeof...(Types)) {
|
|
move_constructor<N + 1>(std::move(another));
|
|
}
|
|
}
|
|
}
|
|
template<size_t N = 0, size_t M = 0>
|
|
void swap(Variant& another) {
|
|
if (index_ == N && another.index_ == M) {
|
|
using R = get_type_by_index_v<N, Types...>;
|
|
using S = get_type_by_index_v<M, Types...>;
|
|
S tmp(std::move(*reinterpret_cast<S*>(another.object_)));
|
|
new(another.object_) R(std::move(*reinterpret_cast<R*>(object_)));
|
|
new(object_) S(std::move(tmp));
|
|
std::swap(index_, another.index_);
|
|
} else if (index_ != N) {
|
|
if constexpr(N + 1 < sizeof...(Types))
|
|
swap<N + 1, M>(another);
|
|
} else {
|
|
if constexpr(M + 1 < sizeof...(Types))
|
|
swap<N, M + 1>(another);
|
|
}
|
|
}
|
|
|
|
public:
|
|
using VariantAlternative<Types, Types...>::operator=...;
|
|
using VariantAlternative<Types, Types...>::VariantAlternative...;
|
|
|
|
Variant() : index_(0) {
|
|
new(object_) get_type_by_index_v<0, Types...>();
|
|
}
|
|
Variant(Variant&& another) {
|
|
move_constructor(std::move(another));
|
|
}
|
|
Variant(const Variant& another) : Variant() {
|
|
copy_constructor(another);
|
|
}
|
|
Variant& operator=(const Variant& another) {
|
|
Variant tmp(another);
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
Variant& operator=(Variant&& another) noexcept {
|
|
if (&another == this) return *this;
|
|
Variant tmp(std::move(another));
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
template<typename T, typename... Args>
|
|
T& emplace(Args&&... args) {
|
|
destroy();
|
|
index_ = get_index_by_type_v<T, Types...>;
|
|
new(object_) T(std::forward<Args...>(args)...);
|
|
return *reinterpret_cast<T*>(object_);
|
|
}
|
|
template<size_t I, typename... Args>
|
|
get_type_by_index_v<I, Types...>& emplace(Args&&... args) {
|
|
using T = get_type_by_index_v<I, Types...>;
|
|
destroy();
|
|
index_ = get_index_by_type_v<T, Types...>;
|
|
new(object_) T(std::forward<Args...>(args)...);
|
|
return *reinterpret_cast<T*>(object_);
|
|
}
|
|
template<typename T, typename U, typename... Args>
|
|
T& emplace(std::initializer_list<U> il, Args&&... args) {
|
|
destroy();
|
|
index_ = get_index_by_type_v<T, Types...>;
|
|
new(object_) T(il, std::forward<Args...>(args)...);
|
|
return *reinterpret_cast<T*>(object_);
|
|
}
|
|
template<size_t I, typename U, typename... Args>
|
|
get_type_by_index_v<I, Types...>& emplace(std::initializer_list<U> il, Args&&... args) {
|
|
using T = get_type_by_index_v<I, Types...>;
|
|
destroy();
|
|
index_ = get_index_by_type_v<T, Types...>;
|
|
new(object_) T(il, std::forward<Args...>(args)...);
|
|
return *reinterpret_cast<T*>(object_);
|
|
}
|
|
size_t index() const {
|
|
return index_;
|
|
}
|
|
bool valueless_by_exception() const {
|
|
return index_ == -1;
|
|
}
|
|
template<typename T, typename... TTypes>
|
|
friend bool holds_alternative(const Variant<TTypes...>& another);
|
|
template<typename T, typename... TTypes>
|
|
friend const T& get(const Variant<TTypes...>& another);
|
|
template<typename T, typename... TTypes>
|
|
friend T& get(Variant<TTypes...>& another);
|
|
template<typename T, typename... TTypes>
|
|
friend T&& get(Variant<TTypes...>&& another);
|
|
template<size_t N, typename... TTypes>
|
|
friend const get_type_by_index_v<N, TTypes...>& get(const Variant<TTypes...>& another);
|
|
template<size_t N, typename... TTypes>
|
|
friend get_type_by_index_v<N, TTypes...>& get(Variant<TTypes...>& another);
|
|
template<size_t N, typename... TTypes>
|
|
friend get_type_by_index_v<N, TTypes...>&& get(Variant<TTypes...>&& another);
|
|
}; |