Skip to content

Instantly share code, notes, and snippets.

@klemens-morgenstern
Created August 26, 2016 21:55
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 klemens-morgenstern/bf9bd5bc3559cf5fa9736f2876149e5b to your computer and use it in GitHub Desktop.
Save klemens-morgenstern/bf9bd5bc3559cf5fa9736f2876149e5b to your computer and use it in GitHub Desktop.
constexpr variant
#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