Last active
March 4, 2018 03:02
-
-
Save bumfo/1c0e7c6fa0372fcc36a138e13e9925be to your computer and use it in GitHub Desktop.
my C++ variant implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _MY_VARIANT_HPP | |
#define _MY_VARIANT_HPP | |
#include <type_traits> | |
#include <exception> | |
#include <new> | |
namespace my { | |
template <size_t ...is> | |
class static_max; | |
template <> | |
class static_max<> { | |
public: | |
static constexpr size_t value = 0; | |
}; | |
template <size_t i0, size_t ...is> | |
class static_max<i0, is...> { | |
public: | |
static constexpr size_t value = i0 > static_max<is...>::value ? i0 : static_max<is...>::value; | |
}; | |
template <int N, typename ...Ts> | |
class variant_type; | |
template <typename T0, typename ...Ts> | |
class variant_type<0, T0, Ts...> { | |
public: | |
using type = T0; | |
}; | |
template <int N, typename T0, typename ...Ts> | |
class variant_type<N, T0, Ts...> { | |
public: | |
using type = typename variant_type<N - 1, Ts...>::type; | |
}; | |
class default_init { | |
}; | |
template <typename S, int I, int N, typename ...Ts> | |
class variant_helper; | |
template <typename S, int I> | |
class variant_helper<S, I, 0> { | |
public: | |
S storage; | |
int _index; | |
variant_helper() { | |
} | |
variant_helper(default_init) { | |
} | |
template <typename F> | |
void visit(F && f) const { | |
} | |
void copyTo(void * storage) const & { | |
} | |
template <typename T> | |
void moveFrom(T && t) { | |
} | |
void deleteMe() { | |
} | |
template <typename T> | |
void callMoveAssign(T && that) { | |
} | |
template <typename T> | |
variant_helper & operator = (T && t) { | |
return *this; | |
} | |
template <typename T> | |
constexpr bool is() const { | |
return false; | |
} | |
}; | |
template <typename A, typename B> | |
struct static_equals; | |
template <typename A> | |
struct static_equals<A, A> { | |
static constexpr bool value = true; | |
}; | |
template <typename A, typename B> | |
struct static_equals { | |
static constexpr bool value = false; | |
}; | |
template <typename S, int I, int N, typename T0, typename ...Ts> | |
class variant_helper<S, I, N, T0, Ts...> : public variant_helper<S, I + 1, N - 1, Ts...> { | |
using super = variant_helper<S, I + 1, N - 1, Ts...>; | |
public: | |
template <typename T> | |
variant_helper(T && t) : super(std::forward<T>(t)) { | |
} | |
variant_helper(T0 const & t0) { | |
this->_index = I; | |
new (&this->storage) T0(std::forward<T0 const>(t0)); | |
} | |
variant_helper(T0 & t0) { | |
this->_index = I; | |
new (&this->storage) T0(std::forward<T0 const>(static_cast<T0 const &>(t0))); | |
} | |
variant_helper(T0 && t0) { | |
this->_index = I; | |
new (&this->storage) T0(std::forward<T0>(t0)); | |
} | |
variant_helper(variant_helper const & that) { | |
this->_index = that._index; | |
that.copyTo(static_cast<void *>(&this->storage)); | |
} | |
variant_helper(variant_helper && that) { | |
this->_index = that._index; | |
moveFrom(std::move(that)); | |
} | |
variant_helper() { | |
} | |
variant_helper(default_init) { | |
this->_index = I; | |
new (&this->storage) T0(); | |
} | |
template <typename T> | |
variant_helper & operator = (T && t) { | |
this->super::operator = (std::forward<T>(t)); | |
return *this; | |
} | |
variant_helper & operator = (T0 & t0) { | |
*this = static_cast<T0 const &>(t0); | |
return *this; | |
} | |
variant_helper & operator = (T0 const & t0) { | |
if (this->_index == I) { | |
cast() = std::forward<T0 const>(t0); | |
} else { | |
deleteMe(); | |
this->_index = I; | |
new (&this->storage) T0(std::forward<T0 const>(t0)); | |
} | |
return *this; | |
} | |
variant_helper & operator = (T0 && t0) { | |
if (this->_index == I) { | |
cast() = std::forward<T0>(t0); | |
} else { | |
deleteMe(); | |
this->_index = I; | |
new (&this->storage) T0(std::forward<T0>(t0)); | |
} | |
return *this; | |
} | |
variant_helper & operator = (variant_helper & that) { | |
return *this = static_cast<variant_helper const &>(that); | |
} | |
variant_helper & operator = (variant_helper const & that) { | |
this->_index = that._index; | |
that.copyTo(static_cast<void *>(&this->storage)); | |
return *this; | |
} | |
variant_helper & operator = (variant_helper && that) { | |
if (this->_index == that._index) { | |
callMoveAssign(std::move(that)); | |
} else { | |
this->_index = that._index; | |
deleteMe(); | |
moveFrom(std::move(that)); | |
} | |
return *this; | |
} | |
template <typename T> | |
void moveFrom(T && that) { | |
if (that._index == I) { | |
new (&this->storage) T0(std::move(*reinterpret_cast<T0 *>(&that.storage))); | |
} | |
this->super::moveFrom(std::move(that)); | |
} | |
void copyTo(void * storage) const & { | |
if (this->_index == I) { | |
new (storage) T0(cast()); | |
} | |
this->super::copyTo(storage); | |
} | |
template <typename F> | |
void visit(F && f) { | |
if (this->_index == I) { | |
f(cast()); | |
} | |
this->super::visit(f); | |
} | |
template <typename F> | |
void visit(F && f) const { | |
if (this->_index == I) { | |
f(cast()); | |
} | |
this->super::visit(f); | |
} | |
T0 & cast() { | |
return *reinterpret_cast<T0 *>(&this->storage); | |
} | |
T0 const & cast() const { | |
return *reinterpret_cast<T0 const *>(&this->storage); | |
} | |
template <typename T> | |
constexpr bool is() const { | |
if (this->_index == I) { | |
return static_equals<T, T0>::value; | |
} else { | |
return this->super::template is<T>(); | |
} | |
} | |
void deleteMe() { | |
if (this->_index == I) { | |
cast().~T0(); | |
} | |
this->super::deleteMe(); | |
} | |
template <typename T> | |
void callMoveAssign(T && that) { | |
if (this->_index == I) { | |
cast() = std::move(*reinterpret_cast<T0 *>(&that.storage)); | |
} | |
this->super::callMoveAssign(std::move(that)); | |
} | |
~variant_helper() { | |
if (this->_index == I) { | |
cast().~T0(); | |
} | |
} | |
}; | |
class bad_variant_access : public std::exception { | |
}; | |
template <typename ...Ts> | |
class variant { | |
static constexpr size_t len = static_max<sizeof(Ts)...>::value; | |
static constexpr size_t align = sizeof...(Ts) == 0 ? alignof(bool) : static_max<alignof(Ts)...>::value; | |
using storage_t = typename std::aligned_storage<len, align>::type; | |
variant_helper<storage_t, 0, sizeof...(Ts), Ts...> fields; | |
public: | |
variant() : fields(default_init()) { | |
} | |
template <typename T> | |
variant(T && t) : fields(std::forward<T>(t)) { | |
} | |
variant(variant const & that) : fields(that.fields) { | |
} | |
variant(variant & that) : variant(static_cast<variant const &>(that)) { | |
} | |
variant(variant && that) : fields(std::move(that.fields)) { | |
} | |
template <typename T> | |
variant & operator = (T && t) { | |
fields = std::forward<T>(t); | |
return *this; | |
} | |
variant & operator = (variant const & that) { | |
fields = that.fields; | |
return *this; | |
} | |
variant & operator = (variant & that) { | |
return *this = static_cast<variant const &>(that); | |
} | |
variant & operator = (variant && that) { | |
fields = std::move(that.fields); | |
return *this; | |
} | |
template <typename F> | |
void visit(F && f) { | |
fields.visit(f); | |
} | |
template <typename F> | |
void visit(F && f) const { | |
fields.visit(f); | |
} | |
constexpr int index() const { | |
return fields._index; | |
} | |
template <typename T> | |
bool is() const { | |
return fields.template is<T>(); | |
} | |
template <int I> | |
typename variant_type<I, Ts...>::type & get() { | |
if (I != index()) throw bad_variant_access(); | |
return *reinterpret_cast<typename variant_type<I, Ts...>::type *>(&fields.storage); | |
} | |
template <typename T> | |
T & get() { | |
if (is<T>()) { | |
return *reinterpret_cast<T *>(&fields.storage); | |
} else { | |
throw bad_variant_access(); | |
} | |
} | |
template <typename T> | |
T const & get() const { | |
if (is<T>()) { | |
return *reinterpret_cast<T const *>(&fields.storage); | |
} else { | |
throw bad_variant_access(); | |
} | |
} | |
}; | |
template <typename F, typename ...Ts> | |
auto visit(F && f, my::variant<Ts...> && v) { | |
v.visit(f); | |
} | |
template <typename F, typename ...Ts> | |
auto visit(F && f, my::variant<Ts...> & v) { | |
v.visit(f); | |
} | |
template <typename F, typename ...Ts> | |
auto visit(F && f, my::variant<Ts...> const & v) { | |
v.visit(f); | |
} | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "my_variant.hpp" | |
#include <iostream> | |
struct A { | |
A() { | |
std::cout << "A" << '\n'; | |
} | |
~A() { | |
std::cout << "~A" << '\n'; | |
} | |
}; | |
struct B { | |
B() { | |
std::cout << "B" << '\n'; | |
} | |
~B() { | |
std::cout << "~B" << '\n'; | |
} | |
}; | |
std::ostream & operator << (std::ostream & out, A & x) { | |
std::cout << "visit A"; | |
return out; | |
} | |
std::ostream & operator << (std::ostream & out, B & x) { | |
std::cout << "visit B"; | |
return out; | |
} | |
int main() { | |
my::variant<A, B> a = A(); | |
my::variant<A, B> b = B(); | |
a.visit([](auto && x) { | |
std::cout << x << '\n'; | |
}); | |
b.visit([](auto && x) { | |
std::cout << x << '\n'; | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment