#ifndef CORE_VARIANT_HPP #define CORE_VARIANT_HPP #include #include #include #include #include #include #include #include #include #include namespace core { inline namespace v2 { namespace impl { /* This is used to get around GCC's inability to expand lambdas in variadic * template functions. You make me so sad sometimes, GCC. */ template auto gen () -> Result { return [](Visitor&& visitor, Data* data, Args&&... args) { return ::core::v2::invoke( ::core::forward(visitor), *static_cast(data), ::core::forward(args)... ); }; } template struct result; template struct result, Args...> final : ::std::conditional< meta::all< meta::all_of< meta::list...>, ::std::is_same, result_of_t >()... >(), result_of_t, 0>, Args...)>, common_type_t...> > { }; template using result_t = typename result::type; /* Used to provide lambda based 'pattern matching' for variant and optional * types. * * Based off of Dave Abrahams C++11 'generic lambda' example (no longer * available on the internet) */ template struct overload; template struct overload : Lambda { using call_type = Lambda; using call_type::operator (); }; template struct overload : private Lambda, private overload::call_type { using base_type = typename overload::call_type; using lambda_type = Lambda; using call_type = overload; overload (Lambda&& lambda, Lambdas&&... lambdas) : lambda_type(pass(lambda)), base_type(pass(lambdas)...) { } using lambda_type::operator (); using base_type::operator (); }; template auto make_overload(Lambdas&&... lambdas) -> overload { return overload { pass(lambdas)... }; } }}} /* namespace core::v2::impl */ namespace core { inline namespace v2 { #ifndef CORE_NO_EXCEPTIONS struct bad_variant_get final : ::std::logic_error { using ::std::logic_error::logic_error; }; [[noreturn]] inline void throw_bad_variant_get () { throw bad_variant_get { "incorrect type" }; } #else /* CORE_NO_EXCEPTIONS */ [[noreturn]] inline void throw_bad_variant_get () { ::std::abort(); } #endif /* CORE_NO_EXCEPTIONS */ template <::std::size_t> struct emplace_index_t { }; template struct emplace_type_t { }; /* visitation semantics require that, given a callable type C, and variadic * arguments Args... that the return type of the visit will be SFINAE-ified * as common_type_t...> (this assumes a variadic * approach can be taken with common_type, index it cannot at this time. A * custom SFINAE-capable version has been written within the type traits * component. * * Obviously if a common type cannot be found, then the visitation function * cannot be generated. * * These same semantics are required for variant::match index simply * calls visit with a generate overload type. */ template class variant final { using typelist = meta::list; //static_assert(meta::all<(meta::count() == 1)...>(), ""); static_assert(meta::none_of(), ""); static_assert(meta::none_of(), ""); using storage_type = aligned_union_t<0, Ts...>; template <::std::size_t N> using size = meta::integral<::std::size_t, N>; template <::std::size_t N> using element = meta::get; struct copier final { using data_type = add_pointer_t; data_type data; template void operator ()(T const& value) const { ::new (this->data) T(value); } }; struct mover final { using data_type = add_pointer_t; data_type data; template void operator () (T&& value) { ::new (this->data) decay_t(::core::move(value)); } }; struct destroyer final { template void operator ()(T const& value) const { value.~T(); } }; struct swapper final { using data_type = add_pointer_t; data_type data; template void operator () (T const&) = delete; template void operator ()(T& value) noexcept(is_nothrow_swappable::value) { using ::std::swap; swap(*static_cast(this->data), value); } }; struct equality final { using data_type = add_pointer_t>; data_type data; template bool operator ()(T const& value) { return equal_to<> { }(*static_cast(this->data), value); } }; struct less_than final { using data_type = add_pointer_t>; data_type data; template bool operator ()(T const& value) noexcept { return less<> { }(*static_cast(this->data), value); } }; struct typeinfo final { template type_info const* operator ()(T&&) const noexcept { return ::std::addressof(type_of>()); } }; template < ::std::size_t N, class=enable_if_t, class T > explicit variant (size&&, ::std::false_type&&, T&& value) : variant { size { }, ::std::is_constructible, N + 1>, T> { }, ::core::forward(value) } { } template < ::std::size_t N, class=enable_if_t, class T > explicit variant (size&&, ::std::true_type&&, T&& value) : data { }, tag { N } { ::new (this->target()) element (::core::forward(value)); } template using select_index = meta::either< meta::count>(), meta::index_of_t>, size<0> >; public: /* The conditional_t used here allows us to first check if a given type * is declared in the variant and if it is, we will try to find its * constructor and immediately jump there, otherwise, we go the slower * route of trying to construct something from the value given. * * While this route is 'slower' this is a compile time performance issue and * will not impact runtime performance. * * Unfortunately we *do* instantiate templates several times, but there's * not much we can do about it. */ template < class T, meta::inhibit<::std::is_same, variant>::value> = __LINE__ > variant (T&& value) : variant { select_index { }, ::std::is_constructible::value>, T> { }, ::core::forward(value) } { } template < class T, class... Args, meta::require() == 1> = __LINE__, meta::require<::std::is_constructible::value> = __LINE__ > variant (emplace_type_t, Args&&... args) : data { }, tag { meta::index_of() } { ::new (this->target()) T(::core::forward(args)...); } template < ::std::size_t I, class... Args, meta::require = __LINE__, meta::require< ::std::is_constructible, Args...>::value > = __LINE__ > variant (emplace_index_t, Args&&... args) : data { }, tag { I } { ::new (this->target()) element(::core::forward(args)...); } variant (variant const& that) : data { }, tag { that.tag } { that.visit(copier { this->target() }); } variant (variant&& that) noexcept : data { }, tag { that.tag } { that.visit(mover { this->target() }); } variant () : variant { element<0> { } } { } ~variant () { this->visit(destroyer { }); } template < class T, meta::inhibit<::std::is_same, variant>::value> = __LINE__ > variant& operator = (T&& value) { variant { ::core::forward(value) }.swap(*this); return *this; } variant& operator = (variant const& that) { variant { that }.swap(*this); return *this; } variant& operator = (variant&& that) noexcept { this->visit(destroyer { }); this->tag = that.tag; that.visit(mover { this->target() }); return *this; } /* Placing these inside of the variant results in no implicit conversions * occuring with any potential constructor types. */ bool operator == (variant const& that) const noexcept { if (this->tag != that.tag) { return false; } return that.visit(equality { this->target() }); } bool operator < (variant const& that) const noexcept { if (this->tag != that.tag) { return this->tag < that.tag; } return that.visit(less_than { this->target() }); } void swap (variant& that) noexcept( meta::all_of() ) { if (this->index() == that.index()) { that.visit(swapper { this->target() }); return; } variant temp { ::core::move(*this) }; *this = ::core::move(that); that = ::core::move(temp); } /* V stands for Visitor */ template auto visit (V&& visitor, Args&&... args) -> common_type_t< result_of_t, Args...)>... > { using return_type = impl::result_t< V, meta::list...>, Args... >; using function = add_pointer_t; static auto const callers = make_array( impl::gen()... ); return callers[this->tag]( ::core::forward(visitor), this->target(), ::core::forward(args)... ); } /* V stands for Visitor */ template auto visit (V&& visitor, Args&&... args) const -> common_type_t< result_of_t>, Args...)>... > { using return_type = impl::result_t< V, meta::list>...>, Args... >; using function = add_pointer_t; static auto const callers = make_array( impl::gen, void const, function, Args...>()... ); return callers[this->tag]( ::core::forward(visitor), this->target(), ::core::forward(args)... ); } template auto match (Vs&&... vs) -> decltype( this->visit(impl::make_overload(::core::forward(vs)...)) ) { return this->visit(impl::make_overload(::core::forward(vs)...)); } template auto match (Vs&&... vs) const -> decltype( this->visit(impl::make_overload(::core::forward(vs)...)) ) { return this->visit(impl::make_overload(::core::forward(vs)...)); } /* These functions are undocumented and should not be used outside of core */ template <::std::size_t N> add_pointer_t>> cast () const noexcept { return static_cast>>>(this->target()); } template <::std::size_t N> add_pointer_t> cast () noexcept { return static_cast>>(this->target()); } type_info const& type () const noexcept { return *this->visit(typeinfo { }); } // Boost.Variant compatibility ::std::size_t which () const noexcept { return this->index(); } ::std::size_t index () const noexcept { return this->tag; } bool empty () const noexcept { return false; } private: void const* target () const noexcept { return as_void(this->data); } void* target () noexcept { return as_void(this->data); } storage_type data; ::std::size_t tag; }; template void swap (variant& lhs, variant& rhs) noexcept( noexcept(lhs.swap(rhs)) ) { lhs.swap(rhs); } template <::std::size_t I, class... Ts> auto get (variant const* v) noexcept -> meta::when< I < sizeof...(Ts), add_pointer_t, I>>> > { using t = add_pointer_t, I>>>; return v and v->index() == I ? static_cast(v->template cast()) : nullptr; } template <::std::size_t I, class... Ts> auto get (variant* v) noexcept -> meta::when< I < sizeof...(Ts), add_pointer_t, I>> > { using t = add_pointer_t, I>>; return v and v->index() == I ? static_cast(v->template cast()) : nullptr; } template <::std::size_t I, class... Ts> auto get (variant const& v) noexcept(false) -> meta::when< I < sizeof...(Ts), add_lvalue_reference_t, I>>> > { if (auto ptr = get(::std::addressof(v))) { return *ptr; } throw_bad_variant_get(); } template <::std::size_t I, class... Ts> auto get (variant&& v) noexcept(false) -> meta::when< I < sizeof...(Ts), add_rvalue_reference_t, I>> > { if (auto p = get(::std::addressof(v))) { return ::core::move(*p); } throw_bad_variant_get(); } template <::std::size_t I, class... Ts> auto get (variant& v) noexcept(false) -> meta::when< I < sizeof...(Ts), add_lvalue_reference_t, I>> > { if (auto ptr = get(::std::addressof(v))) { return *ptr; } throw_bad_variant_get(); } template auto get (variant const* v) noexcept -> meta::unless< meta::find, T>::empty(), decltype(get, T>()>(v)) > { return get, T>()>(v); } template auto get (variant* v) noexcept -> meta::unless< meta::find, T>::empty(), decltype(get, T>()>(v)) > { return get, T>()>(v); } template auto get (variant const& v) noexcept(false) -> meta::unless< meta::find, T>::empty(), decltype(get, T>()>(v)) > { return get, T>()>(v); } template auto get (variant&& v) noexcept(false) -> meta::unless< meta::find, T>::empty(), decltype(core::move(get, T>()>(v))) > { return core::move(get, T>()>(v)); } template auto get (variant& v) noexcept(false) -> meta::unless< meta::find, T>::empty(), decltype(get, T>()>(v)) > { return get, T>()>(v); } }} /* namespace core::v2 */ namespace std { template struct hash<::core::v2::variant> { size_t operator () (::core::v2::variant const& value) const { return value.match(hash { }...); } }; } /* namespace std */ #endif /* CORE_VARIANT_HPP */