Skip to content

Instantly share code, notes, and snippets.

@Bananattack
Last active August 31, 2017 06:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bananattack/ac1b3ad2a4bc383370d3c39e4a3d480e to your computer and use it in GitHub Desktop.
Save Bananattack/ac1b3ad2a4bc383370d3c39e4a3d480e to your computer and use it in GitHub Desktop.
C++14 variant type. Table-based visitor dispatch with O(1) time complexity. Improvement over previous tail-recursive version https://gist.github.com/Bananattack/1e2d3bbbf80f9ab63779 -- Probably not as useful in C++17 which has std::variant, but still nice for slightly older compilers.
#ifndef WIZ_UTILITY_VARIANT_H
#define WIZ_UTILITY_VARIANT_H
#include <cstddef>
#include <cassert>
#include <type_traits>
#include <utility>
namespace wiz {
namespace detail {
template <std::size_t... Ns>
struct MaxValue;
template <>
struct MaxValue<> {
static constexpr std::size_t value = 0;
};
template <std::size_t N, std::size_t... Ns>
struct MaxValue<N, Ns...> {
static constexpr std::size_t value = MaxValue<Ns...>::value > N
? MaxValue<Ns...>::value
: N;
};
template <typename... Ts>
struct CommonType {};
template <typename T>
struct CommonType<T> {
using type = typename std::decay<T>::type;
};
template <typename T, typename U>
struct CommonType<T, U> {
using type = typename std::decay<decltype(true ? std::declval<T>() : std::declval<U>())>::type;
};
template <typename T, typename U, typename... Ts>
struct CommonType<T, U, Ts...> {
using type = typename CommonType<typename CommonType<T, U>::type, Ts...>::type;
};
template <typename... Fs>
struct Overload;
template <typename F>
struct Overload<F> {
public:
Overload(F&& f) : f(std::forward<F>(f)) {}
template <typename... Ts>
auto operator()(Ts&&... args) const
-> decltype(std::declval<F>()(std::forward<Ts>(args)...)) {
return f(std::forward<Ts>(args)...);
}
private:
F f;
};
template <typename F, typename... Fs>
struct Overload<F, Fs...> : Overload<F>, Overload<Fs...> {
using Overload<F>::operator();
using Overload<Fs...>::operator();
Overload(F&& f, Fs&&... fs) :
Overload<F>(std::forward<F>(f)),
Overload<Fs...>(std::forward<Fs>(fs)...) {}
};
template <typename U, typename... Ts>
struct TypeIndexOf;
template <typename U>
struct TypeIndexOf<U> {
static constexpr int value = -1;
};
template <typename U, typename... Ts>
struct TypeIndexOf<U, U, Ts...> {
static constexpr int value = 0;
};
template <typename U, typename T, typename... Ts>
struct TypeIndexOf<U, T, Ts...> {
static constexpr int value = TypeIndexOf<U, Ts...>::value >= 0
? TypeIndexOf<U, Ts...>::value + 1
: -1;
};
template <typename T>
void destroyVariantKind(void* data) {
reinterpret_cast<typename std::add_pointer<T>::type>(data)->~T();
}
template <typename... Ts>
void destroyVariant(int tag, void* data) {
using Table = void(*)(void*);
static const Table table[] = {
destroyVariantKind<Ts>...
};
return table[tag](data);
}
template <typename T>
void copyVariantKind(void* dest, void* src) {
new (dest) T(*reinterpret_cast<typename std::add_pointer<const T>::type>(src));
}
template <typename... Ts>
void copyVariant(int tag, void* dest, const void* src) {
using Table = void(*)(void*, const void*);
static const Table table[] = {
copyVariantKind<Ts>...
};
return table[tag](dest, src);
}
template <typename T>
void moveVariantKind(void* dest, void* src) {
new (dest) T(std::move(*reinterpret_cast<typename std::add_pointer<T>::type>(src)));
}
template <typename... Ts>
void moveVariant(int tag, void* dest, void* src) {
using Table = void(*)(void*, void*);
static const Table table[] = {
moveVariantKind<Ts>...
};
return table[tag](dest, src);
}
template <typename F, typename T>
auto visitVariantKind(F&& f, const void* data)
-> typename std::result_of<F&&(const T&)>::type {
return std::forward<F>(f)(*reinterpret_cast<typename std::add_pointer<const T>::type>(data));
}
template <typename F, typename... Ts>
auto visitVariant(int tag, F&& f, const void* data) {
using Result = typename CommonType<
typename std::result_of<F&&(const Ts&)>::type...
>::type;
using Table = Result(*)(F&&, const void*);
static const Table table[] = {
visitVariantKind<F, Ts>...
};
return table[tag](std::forward<F>(f), data);
}
template <typename U, typename... Ts>
void staticAssertVariantTypeValid() {
static_assert(detail::TypeIndexOf<U, Ts...>::value >= 0, "Variant does not support the provided type.");
}
}
template <typename... Ts>
class Variant {
public:
Variant() = delete;
template <typename U>
Variant(const U& value)
: tag(detail::TypeIndexOf<U, Ts...>::value) {
detail::staticAssertVariantTypeValid<U, Ts...>();
new(&data) U(value);
}
template <typename U>
Variant(U&& value)
: tag(detail::TypeIndexOf<U, Ts...>::value) {
detail::staticAssertVariantTypeValid<U, Ts...>();
new(&data) U(std::forward<U>(value));
}
Variant(const Variant& other)
: tag(other.tag) {
detail::copyVariant<Ts...>(tag, &data, &other.data);
}
Variant(Variant&& other)
: tag(other.tag) {
detail::moveVariant<Ts...>(tag, &data, &other.data);
}
~Variant() {
detail::destroyVariant<Ts...>(tag, &data);
}
Variant& operator =(const Variant& other) {
if (this != &other) {
detail::destroyVariant<Ts...>(tag, &data);
tag = other.tag;
detail::copyVariant<Ts...>(tag, &data, &other.data);
}
return *this;
}
Variant& operator =(Variant&& other) {
if (this != &other) {
detail::destroyVariant<Ts...>(tag, &data);
tag = other.tag;
detail::moveVariant<Ts...>(tag, &data, &other.data);
}
return *this;
}
template <typename U>
bool is() const {
detail::staticAssertVariantTypeValid<U, Ts...>();
return detail::TypeIndexOf<U, Ts...>::value == tag;
}
template <typename U>
U& get() {
detail::staticAssertVariantTypeValid<U, Ts...>();
assert(is<U>());
return *reinterpret_cast<typename std::add_pointer<U>::type>(&data);
}
template <typename U>
const U& get() const {
detail::staticAssertVariantTypeValid<U, Ts...>();
assert(is<U>());
return *reinterpret_cast<typename std::add_pointer<const U>::type>(&data);
}
template <typename U>
typename std::add_pointer<U>::type tryGet() {
return is<U>() ? &get<U>() : nullptr;
}
template <typename U>
typename std::add_pointer<const U>::type tryGet() const {
return is<U>() ? &get<U>() : nullptr;
}
template <typename F>
auto visit(F&& f) const {
return detail::visitVariant<F, Ts...>(tag, std::forward<F>(f), &data);
}
template <typename F, typename... Fs>
auto visit(F&& f, Fs&&... fs) const {
using OverloadType = detail::Overload<F, Fs...>;
return detail::visitVariant<OverloadType, Ts...>(
tag,
OverloadType(std::forward<F>(f), std::forward<Fs>(fs)...),
&data);
}
private:
using DataType = typename std::aligned_storage<
detail::MaxValue<sizeof(Ts)...>::value,
detail::MaxValue<alignof(Ts)...>::value>::type;
int tag;
DataType data;
};
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment