-
-
Save Rapptz/1b11c78284c52c429ef4 to your computer and use it in GitHub Desktop.
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 "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!"); | |
} |
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 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 |
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 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