Created
August 26, 2016 21:55
-
-
Save klemens-morgenstern/bf9bd5bc3559cf5fa9736f2876149e5b to your computer and use it in GitHub Desktop.
constexpr variant
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 <system_error> | |
#include <iostream> | |
#include <cstring> | |
#include <boost/type_index/ctti_type_index.hpp> | |
#include <boost/utility/in_place_factory.hpp> | |
#include <boost/utility/typed_in_place_factory.hpp> | |
#include <boost/hana/front.hpp> | |
#include <boost/hana/back.hpp> | |
#include <boost/hana/sort.hpp> | |
#include <boost/hana/tuple.hpp> | |
#include <boost/hana/set.hpp> | |
#include <boost/hana/all_of.hpp> | |
#include <boost/hana/find.hpp> | |
#include <boost/hana/difference.hpp> | |
#include <boost/hana/less.hpp> | |
#include <boost/hana/contains.hpp> | |
#include <boost/hana/maximum.hpp> | |
#include <boost/hana/string.hpp> | |
#include <boost/hana/fwd/type.hpp> | |
#include <typeinfo> | |
#include <type_traits> | |
#include <boost/type_index.hpp> | |
using namespace std; | |
using boost::detail::ctti; | |
namespace boost { | |
template<std::size_t> | |
struct in_place_index {}; | |
template<typename T> | |
struct in_place_type {}; | |
template<typename ...Args> | |
class variant; | |
namespace detail { namespace variant { | |
struct ctti_less_t | |
{ | |
template<typename T, typename U> | |
constexpr std::integral_constant<bool,(ctti<T>() < ctti<U>())> operator()(ctti<T>, ctti<U>) {return {};} | |
}; | |
constexpr static ctti_less_t ctti_less; | |
template<template <class> class Trait> | |
struct ctti_trait_t | |
{ | |
template<typename T> | |
constexpr bool operator()(ctti<T>) {return Trait<T>::value;} | |
}; | |
/* | |
template<typename ... Args> | |
using first | |
*/ | |
template<bool trivial, typename ...Args> | |
union variant_store; | |
template<typename T> | |
union variant_store<true, T> | |
{ | |
T value; | |
constexpr variant_store(const T & t) : value(t) {} | |
constexpr variant_store(T && t) : value(std::move(t)) {} | |
template<typename ... Args> | |
constexpr variant_store(boost::in_place_type<T>, Args&& ... args) : value(std::forward<Args>(args)...) {} | |
template<typename ... Args> | |
constexpr variant_store(boost::in_place_index<0>, Args&& ... args) : value(std::forward<Args>(args)...) {} | |
}; | |
template<typename T, typename ...Args> | |
union variant_store<true, T, Args...> | |
{ | |
T value; | |
variant_store<true, Args...> next; | |
template<typename ... CArgs> | |
constexpr variant_store(boost::in_place_type<T>, CArgs&& ... args) : value(std::forward<CArgs>(args)...) {} | |
template<typename ... CArgs> | |
constexpr variant_store(boost::in_place_index<0>, CArgs&& ... args) : value(std::forward<CArgs>(args)...) {} | |
template<typename U, typename ... CArgs> | |
constexpr variant_store(boost::in_place_type<U>, CArgs&& ... args) : next(boost::in_place_type<U>(), std::forward<CArgs>(args)...) {} | |
template<int I, typename ... CArgs> | |
constexpr variant_store(boost::in_place_index<I>, CArgs&& ... args) : next(boost::in_place_index<I-1>(), std::forward<CArgs>(args)...) {} | |
}; | |
template<typename T> | |
union variant_store<false, T> | |
{ | |
T value; | |
constexpr variant_store(const T & t) : value(t) {} | |
constexpr variant_store(T && t) : value(std::move(t)) {} | |
template<typename ... Args> | |
constexpr variant_store(boost::in_place_type<T>, Args&& ... args) : value(std::forward<Args>(args)...) {} | |
template<typename ... Args> | |
constexpr variant_store(boost::in_place_index<0>, Args&& ... args) : value(std::forward<Args>(args)...) {} | |
void destruct(int which) {value.~T();} | |
~variant_store() {} | |
}; | |
template<typename T, typename ...Args> | |
union variant_store<false, T, Args...> | |
{ | |
T value; | |
variant_store<false, Args...> next; | |
template<typename ... CArgs> | |
constexpr variant_store(boost::in_place_type<T>, CArgs&& ... args) : value(std::forward<CArgs>(args)...) {} | |
template<typename ... CArgs> | |
constexpr variant_store(boost::in_place_index<0>, CArgs&& ... args) : value(std::forward<CArgs>(args)...) {} | |
template<typename U, typename ... CArgs> | |
constexpr variant_store(boost::in_place_type<U>, CArgs&& ... args) : next(boost::in_place_type<U>(), std::forward<CArgs>(args)...) {} | |
template<int I, typename ... CArgs> | |
constexpr variant_store(boost::in_place_index<I>, CArgs&& ... args) : next(boost::in_place_index<I-1>(), std::forward<CArgs>(args)...) {} | |
void destruct(int which) | |
{ | |
if (which) | |
next.destruct(which-1); | |
else | |
value.~T(); | |
} | |
~variant_store() {} | |
}; | |
template<typename T, bool trivial, typename ...Args> | |
constexpr auto& get_impl(ctti<T>, variant_store<trivial, T, Args...> & v) | |
{ | |
return v.value; | |
} | |
template<typename T, bool trivial, typename First ,typename ...Args> | |
constexpr auto& get_impl(ctti<T>, variant_store<trivial, First, Args...> & v) | |
{ | |
return get_impl(ctti<T>(), v.next); | |
} | |
template<typename T, bool trivial, typename ...Args> | |
constexpr const auto& get_impl(ctti<T>, const variant_store<trivial, T, Args...> & v) | |
{ | |
return v.value; | |
} | |
template<typename T, bool trivial, typename First ,typename ...Args> | |
constexpr const auto& get_impl(ctti<T>, const variant_store<trivial, First, Args...> & v) | |
{ | |
return get_impl(ctti<T>(), v.next); | |
} | |
template<bool trivial, typename ...Args> | |
constexpr auto& get_impl(std::integral_constant<int, 0>, variant_store<trivial, Args...> & v) | |
{ | |
return v.value; | |
} | |
template<int I, bool trivial, typename ...Args, typename = std::enable_if_t<(I>0)>> | |
constexpr auto& get_impl(std::integral_constant<int, I>, variant_store<trivial, Args...> & v) | |
{ | |
return get_impl(std::integral_constant<int, I-1>(), v.next); | |
} | |
template<bool trivial, typename ...Args> | |
constexpr const auto& get_impl(std::integral_constant<int, 0>, const variant_store<trivial, Args...> & v) | |
{ | |
return v.value; | |
} | |
template<int I, bool trivial, typename ...Args, typename = std::enable_if_t<(I > 0)>> | |
constexpr const auto& get_impl(std::integral_constant<int, I>, const variant_store<trivial, Args...> & v) | |
{ | |
return get_impl(std::integral_constant<int, I-1>(), v.next); | |
} | |
template<typename T, int I, typename ...Args> | |
struct position_t; | |
template<typename T, int I, typename First, typename ...Args> | |
struct position_t<T, I, First, Args...> | |
{ | |
using type = typename position_t<T, I+1, Args...>::type; | |
}; | |
template<typename T, int I, typename ...Args> | |
struct position_t<T, I, T, Args...> | |
{ | |
using type = std::integral_constant<std::size_t, I>; | |
}; | |
template<typename T, int I, typename Elem> | |
struct position_t<T, I, Elem> { using type = std::integral_constant<int, -1>;}; | |
template<typename T, int I> | |
struct position_t<T, I, T> { using type = std::integral_constant<int, I>;}; | |
template<typename ...Args> | |
struct variant_meta | |
{ | |
template<typename T> | |
constexpr static T remove_ctti(ctti<T>); | |
template<typename T> | |
constexpr static detail::ctti<std::remove_cv_t<std::remove_reference_t<T>>> get_impl() {return {};} | |
constexpr static auto ctti_tup = hana::make_tuple(get_impl<Args>()...); | |
constexpr static auto sorted_tup = hana::sort(ctti_tup, ctti_less); | |
template<int I> | |
constexpr static auto at = hana::at(ctti_tup, hana::size_c<I>); | |
template<int I> | |
using at_t = decltype(remove_ctti(at<I>)); | |
constexpr static auto front = hana::front(ctti_tup); | |
constexpr static auto back = hana::back(ctti_tup); | |
constexpr static auto trivially_destructible = hana::all_of(ctti_tup, ctti_trait_t<std::is_trivially_destructible>()); | |
using front_t = decltype(remove_ctti(front)); | |
using back_t = decltype(remove_ctti(back)); | |
template<typename T> | |
constexpr static auto contains = hana::contains(ctti_tup, ctti<T>()); | |
template<typename T> | |
using position_t = typename position_t<T, 0, Args...>::type; | |
template<typename T> | |
constexpr static int position = position_t<T>::value; | |
template<typename T> | |
constexpr static int position_f () {return position<T>;} | |
}; | |
template<bool trivial, typename ...Args> | |
struct variant_impl | |
{ | |
using which_t = int; | |
using storage_t = boost::detail::variant::variant_store<trivial, Args...>; | |
which_t which; | |
storage_t storage; | |
template<typename ...CArgs> | |
constexpr variant_impl(int which, CArgs && ... args) : which(which), storage(std::forward<CArgs>(args)...) {} | |
~variant_impl() {storage.destruct(which); } | |
}; | |
template<typename ...Args> | |
struct variant_impl<true, Args...> | |
{ | |
using which_t = int; | |
using storage_t = boost::detail::variant::variant_store<true, Args...>; | |
which_t which; | |
storage_t storage; | |
template<typename ...CArgs> | |
constexpr variant_impl(int which, CArgs && ... args) : which(which), storage(std::forward<CArgs>(args)...) {} | |
}; | |
}} | |
struct bad_variant_access | |
: std::exception | |
{ | |
public: // std::exception interface | |
virtual const char * what() const noexcept | |
{ | |
return "boost::bad_variant_access: accesing variant with wrong held type."; | |
} | |
}; | |
template<typename Visitor, typename ...Variants> | |
constexpr decltype(auto) visit(Visitor && vis, Variants && ... variants); | |
template<typename ...Args> | |
class variant | |
{ | |
using _vm = boost::detail::variant::variant_meta<Args...>; | |
using _first_t = typename _vm::front_t; | |
using _impl_t = boost::detail::variant::variant_impl<_vm::trivially_destructible, Args...>; | |
_impl_t _impl; | |
public: | |
using which_t = typename _impl_t::which_t; | |
using storage_t = typename _impl_t::storage_t; | |
constexpr which_t which() const {return _impl.which;} | |
template<typename = std::enable_if_t<std::is_default_constructible<_first_t>::value>> | |
constexpr variant() noexcept(std::is_nothrow_default_constructible<_first_t>::value) | |
: _impl(0, /*storage */boost::in_place_index<0>()) | |
{ | |
} | |
template<typename T, int pos = _vm::template position<std::remove_cv_t<std::remove_reference_t<T>>>, typename = enable_if_t<(pos >= 0)>> | |
constexpr variant(T && t) noexcept(std::is_nothrow_constructible<T, T&&>::value) | |
: _impl(pos, /*storage*/ boost::in_place_type<T>(), std::forward<T>(t)) | |
{ | |
} | |
template<typename T, typename ...CArgs, | |
int pos = _vm::template position<std::remove_cv_t<std::remove_reference_t<T>>>, typename = enable_if_t<(pos >= 0)>> | |
constexpr variant(boost::in_place_type<T> place, CArgs && ...args) | |
noexcept(std::is_nothrow_constructible<T, CArgs...>::value) | |
: _impl(pos, /* storage */ place, std::forward<CArgs>(args)...) | |
{ | |
} | |
template<int I, typename ...CArgs, typename = enable_if_t<(I < sizeof...(Args))>> | |
constexpr variant(boost::in_place_index<I> place, CArgs && ...args) | |
noexcept(std::is_nothrow_constructible<typename _vm::template at_t<I>, CArgs...>::value) | |
: _impl(I, /* storage */ place, std::forward<CArgs>(args)...) | |
{ } | |
template<typename T> | |
constexpr auto & get() | |
{ | |
if (_impl.which != _vm::template position<T>) | |
throw bad_variant_access(); | |
return detail::variant::get_impl(detail::ctti<T>(), _impl.storage); | |
} | |
template<typename T> | |
constexpr const auto & get() const | |
{ | |
if (_impl.which != _vm::template position<T>) | |
throw bad_variant_access(); | |
return detail::variant::get_impl(detail::ctti<T>(), _impl.storage); | |
} | |
template<int I> | |
constexpr auto & get() | |
{ | |
if (_impl.which != I) | |
throw bad_variant_access(); | |
return detail::variant::get_impl(std::integral_constant<int, I>(), _impl.storage); | |
} | |
template<int I> | |
constexpr const auto & get() const | |
{ | |
if (_impl.which != I) | |
throw bad_variant_access(); | |
return detail::variant::get_impl(std::integral_constant<int, I>(), _impl.storage); | |
} | |
}; | |
template<typename T, typename ...Args> | |
constexpr auto & get(variant<Args...> & v) | |
{ | |
return v.template get<T>(); | |
} | |
template<typename T, typename ...Args> | |
constexpr const auto & get(const variant<Args...> & v) | |
{ | |
return v.template get<T>(); | |
} | |
template<int I, typename ...Args> | |
constexpr auto & get(variant<Args...> & v) | |
{ | |
return v.template get<I>(); | |
} | |
template<int I, typename ...Args> | |
constexpr const auto & get(const variant<Args...> & v) | |
{ | |
return v.template get<I>(); | |
} | |
template<typename T, typename Visitor, typename ...Variants> | |
constexpr auto visit(Visitor && vis, Variants && ... variants) -> T | |
{ | |
} | |
template<typename Visitor, typename ...Variants> | |
constexpr decltype(auto) visit(Visitor && vis, Variants && ... variants) | |
{ | |
} | |
} | |
using namespace boost; | |
template<typename T> | |
auto ti () {return boost::typeindex::ctti_type_index::type_id<T>().pretty_name();} | |
struct S | |
{ | |
S() {cout << " S()" <<endl;} | |
S(const S&) {cout << " S(const S&)" <<endl;} | |
~S() {cout << "~S()" <<endl;} | |
}; | |
int main(int argc, char* argv[]) | |
{ | |
variant<int, char, void*> v1 { 'D' }; | |
variant<int, S, void*> v2 { boost::in_place_type<S>() }; | |
variant<int, S, void*> v3 { S() }; | |
cout << "which 2 " << v2.which() << endl; | |
cout << "which 3 " << v3.which() << endl; | |
constexpr variant<int, char, void*> v ( 'C' ); | |
constexpr char c1 = boost::get<1>(v); | |
constexpr char c2 = boost::get<char>(v); | |
using boost::detail::variant::variant_meta; | |
using vm = variant_meta<int, char, void*>; | |
cout << "C: '" << c1 << "'" << endl; | |
cout << "C: '" << c2 << "'" << endl; | |
cout << "Check the thingy" << endl; | |
constexpr variant<char, int, void*> v_('A'); | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment