Skip to content

Instantly share code, notes, and snippets.

@bumfo
Last active March 4, 2018 03:02
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 bumfo/1c0e7c6fa0372fcc36a138e13e9925be to your computer and use it in GitHub Desktop.
Save bumfo/1c0e7c6fa0372fcc36a138e13e9925be to your computer and use it in GitHub Desktop.
my C++ variant implementation
#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
#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