cpp/variant/variant.h

343 lines
11 KiB
C
Raw Normal View History

2023-07-16 07:03:47 +00:00
#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);
};