Skip to content

Instantly share code, notes, and snippets.

@Rapptz

Rapptz/main.cpp Secret

Last active August 29, 2015 14:00
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 Rapptz/1b11c78284c52c429ef4 to your computer and use it in GitHub Desktop.
Save Rapptz/1b11c78284c52c429ef4 to your computer and use it in GitHub Desktop.
#include "variant.hpp"
#include <iostream>
#include <cassert>
#include <string>
int main() {
test::variant<int, float, const char*> x(42);
std::cout << x.get_or<const char*>("Hello World!");
}
#ifndef META_HPP
#define META_HPP
#include <type_traits>
#include <utility>
#include <cstddef>
#include <tuple>
namespace test {
template<typename T>
using Type = typename T::type;
template<typename T>
struct identity {
using type = T;
};
template<typename T>
using Identity = typename identity<T>::type;
template<typename... Args>
using CommonType = typename std::common_type<Args...>::type;
template<typename T, T t>
using Const = std::integral_constant<T, t>;
template<bool B>
using Bool = Const<bool, B>;
template<size_t N>
using SizeType = Const<size_t, N>;
template<typename Condition, typename Then, typename Else>
using If = Type<std::conditional<Condition::value, Then, Else>>;
template<typename Condition, typename Then, typename Else>
using TypeIf = Type<If<Condition, Then, Else>>;
template<typename T>
using Decay = Type<std::decay<T>>;
template<typename T>
using Unqualified = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
template<size_t N, typename T, typename... Args>
struct index_of_impl;
template<size_t N, typename T, typename V, typename... Args>
struct index_of_impl<N, T, V, Args...> : If<std::is_same<T, V>, SizeType<N>,
SizeType<index_of_impl<N + 1, T, Args...>::value>> {};
template<size_t N, typename T, typename V>
struct index_of_impl<N, T, V> : If<std::is_same<T, V>, SizeType<N>, SizeType<static_cast<size_t>(-1)>> {};
template<typename T, typename... Args>
struct index_of : index_of_impl<0, T, Args...> {};
template<size_t N, typename... Args>
using TypeAt = Type<std::tuple_element<N, std::tuple<Args...>>>;
template<typename... Args>
struct Any : Bool<false> {};
template<typename T, typename... Args>
struct Any<T, Args...> : If<T, Bool<true>, Any<Args...>> {};
template<typename... Args>
struct All : Bool<true> {};
template<typename T, typename... Args>
struct All<T, Args...> : If<T, All<Args...>, Bool<false>> {};
template<typename T>
constexpr T max(T&& t) {
return std::forward<T>(t);
}
template<typename T, typename U>
constexpr auto max(T&& t, U&& u) -> CommonType<T,U> {
return t > u ? std::forward<T>(t) : std::forward<U>(u);
}
template<typename T, typename U, typename... Args>
constexpr auto max(T&& t, U&& u, Args&&... args) -> CommonType<T,U,Args...> {
return max(max(std::forward<T>(t), std::forward<U>(u)), std::forward<Args>(args)...);
}
} // test
#endif // META_HPP
#ifndef VARIANT_HPP
#define VARIANT_HPP
#include "meta.hpp"
#include <cstddef>
#include <stdexcept>
#include <iosfwd>
namespace test {
namespace detail {
template<template<typename> class F, typename... Args>
struct for_each;
template<template<typename> class F, typename T>
struct for_each<F, T> : F<T> {};
template<template<typename> class F, typename T, typename... Args>
struct for_each<F, T, Args...> : Bool<F<T>::value && for_each<F, Args...>::value> {};
template<template<typename> class F>
struct for_each<F> : std::true_type {};
template<typename T>
struct is_valid_variant_type : Any<std::is_nothrow_move_constructible<T>, std::is_copy_constructible<T>> {};
template<typename... Args>
struct visitor;
// use expression SFINAE techniques to pick an appropriate function
template<typename T, typename... Args>
struct visitor<T, Args...> {
template<typename Function, typename Variant>
static auto apply(Function f, Variant&& v, size_t index = 0) -> decltype(f(std::declval<T&>())) {
if(index == v.which()) {
return f(v. template get<T>());
}
return visitor<Args...>::apply(f, std::forward<Variant>(v), index + 1);
}
};
template<typename T>
struct visitor<T> {
template<typename Function, typename Variant>
static auto apply(Function f, Variant&& v, size_t index = 0) -> decltype(f(std::declval<T&>())) {
if(index == v.which()) {
return f(v. template get<T>());
}
throw std::runtime_error("Invalid visitor call");
}
};
template<typename OStream>
struct ostream_visitor {
private:
OStream& out;
public:
ostream_visitor(OStream& out): out(out) {}
template<typename T>
OStream& operator()(const T& element) {
return out << element;
}
};
template<typename... Functions>
struct visitor_type;
template<typename Function, typename... Rest>
struct visitor_type<Function, Rest...> : public Function, visitor_type<Rest...> {
using Function::operator();
using visitor_type<Rest...>::operator();
visitor_type(Function f, Rest... r): Function(f), visitor_type<Rest...>(r...) {}
};
template<typename Function>
struct visitor_type<Function> : public Function {
using Function::operator();
visitor_type(Function f): Function(f) {}
};
} // detail
template<typename... Args>
class variant {
private:
static_assert(detail::for_each<detail::is_valid_variant_type, Args...>::value,
"All types must have nothrow move or copy constructor");
typename std::aligned_storage<max(sizeof(Args)...), max(alignof(Args)...)>::type storage;
size_t current_index = -1;
struct destructor {
template<typename T>
void operator()(T& t) const noexcept {
t.~T();
}
};
void destroy() noexcept {
if(!empty()) {
detail::visitor<Args...>::apply(destructor{}, *this);
current_index = -1;
}
}
struct move_constructor {
private:
variant& self;
public:
move_constructor(variant& self): self(self) {}
template<typename T>
void operator()(T&& t) const noexcept {
self.set(std::move(t));
}
};
struct copy_constructor {
private:
variant& self;
public:
copy_constructor(variant& self): self(self) {}
template<typename T>
void operator()(const T& t) const noexcept {
self.set(t);
}
};
struct move_assign {
private:
variant& self;
size_t index;
public:
move_assign(variant& self, size_t index): self(self), index(index) {}
template<typename T>
void operator()(T&& t) const noexcept {
if(self.current_index == index) {
self.cast_to<Decay<T>>() = std::move(t);
}
else {
self.destroy();
self.set(std::move(t));
}
}
};
struct copy_assign {
private:
variant& self;
size_t index;
public:
copy_assign(variant& self, size_t index): self(self), index(index) {}
template<typename T>
void operator()(const T& t) const noexcept {
if(self.current_index == index) {
self.cast_to<T>() = t;
}
else {
self.destroy();
self.set(t);
}
}
};
template<typename T>
T& cast_to() {
return *static_cast<T*>(static_cast<void*>(&storage));
}
template<typename T>
const T& cast_to() const {
return *static_cast<const T*>(static_cast<const void*>(&storage));
}
public:
constexpr variant() = default;
~variant() {
destroy();
}
template<typename T, typename = Type<std::enable_if<!std::is_same<Decay<T>, variant>::value>>>
variant(T&& t) {
set(std::forward<T>(t));
}
variant(const variant& other): current_index(other.current_index) {
if(!other.empty()) {
copy_constructor ctor(*this);
detail::visitor<Args...>::apply(ctor, other);
}
else {
destroy();
}
}
variant(variant&& other): current_index(other.current_index) {
if(!other.empty()) {
move_constructor ctor(*this);
detail::visitor<Args...>::apply(ctor, std::move(other));
other.destroy();
}
else {
destroy();
}
}
variant& operator=(const variant& other) {
if(!other.empty()) {
copy_assign assign(*this, other.which());
detail::visitor<Args...>::apply(assign, other);
}
else {
destroy();
}
return *this;
}
variant& operator=(variant&& other) {
if(!other.empty()) {
move_assign assign(*this, other.which());
detail::visitor<Args...>::apply(assign, std::move(other));
other.destroy();
}
else {
destroy();
}
return *this;
}
template<typename T, typename = Type<std::enable_if<!std::is_same<Decay<T>, variant>::value>>>
variant& operator=(T&& t) {
set(std::forward<T>(t));
return *this;
}
template<typename T>
void set(T&& value) {
constexpr auto index = index_of<Decay<T>, Args...>::value;
static_assert(index != static_cast<size_t>(-1), "Type not found in variant");
destroy();
using type = Decay<T>;
::new(&storage) type(std::forward<T>(value));
current_index = index;
}
template<typename T>
T& get() {
constexpr auto index = index_of<T, Args...>::value;
static_assert(index != static_cast<size_t>(-1), "Type not found in variant");
if(index != current_index) {
throw std::runtime_error("Invalid type cast");
}
return cast_to<T>();
}
template<typename T>
const T& get() const {
constexpr auto index = index_of<T, Args...>::value;
static_assert(index != static_cast<size_t>(-1), "Type not found in variant");
if(index != current_index) {
throw std::runtime_error("Invalid type cast");
}
return cast_to<T>();
}
template<typename T>
const T& get_or(const Identity<T>& t) const {
constexpr auto index = index_of<T, Args...>::value;
static_assert(index != static_cast<size_t>(-1), "Type not found in variant");
if(index == current_index) {
return cast_to<T>();
}
return t;
}
bool empty() const {
return current_index == static_cast<size_t>(-1);
}
size_t which() const {
return current_index;
}
template<typename T>
bool is() const {
constexpr auto index = index_of<T, Args...>::value;
return index == current_index;
}
template<typename Function>
auto apply(Function f) -> decltype(detail::visitor<Args...>::apply(f, *this)) {
return detail::visitor<Args...>::apply(f, *this);
}
template<typename Function>
auto apply(Function f) const -> decltype(detail::visitor<Args...>::apply(f, *this)) {
return detail::visitor<Args...>::apply(f, *this);
}
};
template<typename CharT, typename Elem, typename... Args>
inline auto operator<<(std::basic_ostream<CharT, Elem>& out, const variant<Args...>& v) -> decltype(out) {
detail::ostream_visitor<decltype(out)> vs(out);
return v.apply(vs);
}
template<typename... Functions>
inline detail::visitor_type<Functions...> make_visitor(Functions&&... f) {
return { std::forward<Functions>(f)... };
}
template<typename Function, typename... Args>
inline auto apply_visitor(Function f, const variant<Args...>& v) -> decltype(v.apply(f)) {
return v.apply(f);
}
template<typename Function, typename... Args>
inline auto apply_visitor(Function f, variant<Args...>& v) -> decltype(v.apply(f)) {
return v.apply(f);
}
} // test
#endif // VARIANT_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment