// Range v3 library // // Copyright Eric Niebler 2014-present // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 // #ifndef RANGES_V3_DETAIL_VARIANT_HPP #define RANGES_V3_DETAIL_VARIANT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { template struct emplaced_index_t; template struct emplaced_index_t : meta::size_t {}; /// \cond #if !RANGES_CXX_VARIABLE_TEMPLATES template inline emplaced_index_t emplaced_index() { return {}; } template using _emplaced_index_t_ = emplaced_index_t (&)(); #define RANGES_EMPLACED_INDEX_T(I) _emplaced_index_t_ #else /// \endcond #if RANGES_CXX_INLINE_VARIABLES < RANGES_CXX_INLINE_VARIABLES_17 namespace { template constexpr auto & emplaced_index = static_const>::value; } #else // RANGES_CXX_INLINE_VARIABLES >= RANGES_CXX_INLINE_VARIABLES_17 template inline constexpr emplaced_index_t emplaced_index{}; #endif // RANGES_CXX_INLINE_VARIABLES /// \cond #define RANGES_EMPLACED_INDEX_T(I) emplaced_index_t #endif /// \endcond struct bad_variant_access : std::logic_error { explicit bad_variant_access(std::string const & what_arg) : std::logic_error(what_arg) {} explicit bad_variant_access(char const * what_arg) : std::logic_error(what_arg) {} }; template struct indexed_element { private: reference_wrapper ref_; public: constexpr indexed_element(reference_wrapper r) noexcept : ref_(r) {} constexpr decltype(auto) get() const noexcept { return ref_.get(); } }; template struct indexed_element { void get() const noexcept {} }; /// \cond namespace detail { struct indexed_element_fn; template auto uninitialized_copy(I first, S last, O out) -> CPP_ret(O)( // requires(!sized_sentinel_for)) { for(; first != last; ++first, ++out) ::new((void *)std::addressof(*out)) iter_value_t(*first); return out; } template auto uninitialized_copy(I first, S last, O out) -> CPP_ret(O)( // requires sized_sentinel_for) { return std::uninitialized_copy_n(first, (last - first), out); } template O uninitialized_copy(I first, I last, O out) { return std::uninitialized_copy(first, last, out); } template struct indexed_datum { private: template friend struct indexed_datum; T datum_; public: CPP_member constexpr CPP_ctor(indexed_datum)()( // noexcept(std::is_nothrow_default_constructible::value) // requires default_constructible) : datum_{} {} CPP_template(typename... Ts)( // requires constructible_from && (sizeof...(Ts) != 0)) // constexpr indexed_datum(Ts &&... ts) noexcept( std::is_nothrow_constructible::value) : datum_(static_cast(ts)...) {} template constexpr CPP_ctor(indexed_datum)(indexed_datum that)( // noexcept(std::is_nothrow_constructible::value) // requires(!same_as) && convertible_to) : datum_(std::move(that.datum_)) {} constexpr indexed_element ref() noexcept { return {datum_}; } constexpr indexed_element ref() const noexcept { return {datum_}; } constexpr T & get() noexcept { return datum_; } constexpr T const & get() const noexcept { return datum_; } }; template struct indexed_datum; template struct indexed_datum { private: template friend struct indexed_datum; reference_wrapper ref_; public: constexpr indexed_datum(reference_wrapper ref) noexcept : ref_(ref) {} constexpr indexed_element ref() const noexcept { return {ref_.get()}; } }; template struct indexed_datum { private: template friend struct indexed_datum; reference_wrapper ref_; public: constexpr indexed_datum(reference_wrapper ref) noexcept : ref_(ref) {} constexpr indexed_element ref() const noexcept { return {ref_.get()}; } }; template struct indexed_datum { void get() const noexcept {} constexpr indexed_element ref() const noexcept { return {}; } }; template using variant_datum_t = detail::indexed_datum, Index>, meta::size_t>; } // namespace detail template meta::if_c<(bool)constructible_from, Args...>> emplace(variant &, Args &&...); namespace detail { using variant_nil = indexed_datum; template, meta::transform>>:: type::value> struct variant_data_ { using type = indexed_datum; }; template struct variant_data_, true> { struct type { using head_t = T; using tail_t = meta::_t>>; union { head_t head; tail_t tail; }; type() noexcept {} template constexpr type(meta::size_t<0>, Args &&... args) noexcept( std::is_nothrow_constructible::value) : head{((Args &&) args)...} {} template constexpr type(meta::size_t, Args &&... args) noexcept( std::is_nothrow_constructible, Args...>::value) : tail{meta::size_t{}, ((Args &&) args)...} {} }; }; template struct variant_data_, false> { struct type { using head_t = T; using tail_t = meta::_t>>; union { head_t head; tail_t tail; }; type() noexcept {} ~type() {} template constexpr type(meta::size_t<0>, Args &&... args) noexcept( std::is_nothrow_constructible::value) : head{((Args &&) args)...} {} template constexpr type(meta::size_t, Args &&... args) noexcept( std::is_nothrow_constructible, Args...>::value) : tail{meta::size_t{}, ((Args &&) args)...} {} }; }; template using variant_data = meta::_t, meta::as_list>, meta::quote>>>; inline std::size_t variant_move_copy_(std::size_t, variant_nil, variant_nil) { return 0; } template std::size_t variant_move_copy_(std::size_t n, Data0 & self, Data1 && that) { using Head = typename Data0::head_t; return 0 == n ? ((void)::new((void *)&self.head) Head(((Data1 &&) that).head), 0) : variant_move_copy_(n - 1, self.tail, ((Data1 &&) that).tail) + 1; } constexpr bool variant_equal_(std::size_t, variant_nil, variant_nil) { return true; } template constexpr bool variant_equal_(std::size_t n, Data0 const & self, Data1 const & that) { return n == 0 ? self.head.get() == that.head.get() : variant_equal_(n - 1, self.tail, that.tail); } template constexpr int variant_visit_(std::size_t, variant_nil, Fun, Proj = {}) { return (RANGES_EXPECT(false), 0); } template constexpr int variant_visit_(std::size_t n, Data & self, Fun fun, Proj proj = {}) { return 0 == n ? ((void)invoke(fun, invoke(proj, self.head)), 0) : detail::variant_visit_( n - 1, self.tail, detail::move(fun), detail::move(proj)); } struct get_datum_fn { template decltype(auto) operator()(T && t) const noexcept { return t.get(); } }; struct indexed_element_fn { template decltype(auto) operator()(T && t) const noexcept { return t.ref(); } }; struct empty_variant_tag {}; struct variant_core_access { template static constexpr variant_data & data(variant & var) noexcept { return var.data_(); } template static constexpr variant_data const & data( variant const & var) noexcept { return var.data_(); } template static constexpr variant_data && data(variant && var) noexcept { return detail::move(var.data_()); } template static variant make_empty(meta::id> = {}) noexcept { return variant{empty_variant_tag{}}; } }; struct delete_fn { template void operator()(T const & t) const noexcept { t.~T(); } }; template struct construct_fn { std::tuple args_; template void construct_(U & u, meta::index_sequence) noexcept( std::is_nothrow_constructible::value) { ::new((void *)std::addressof(u)) U(static_cast(std::get(args_))...); } construct_fn(Ts &&... ts) noexcept( std::is_nothrow_constructible, Ts...>::value) : args_{static_cast(ts)...} {} template [[noreturn]] meta::if_c operator()( indexed_datum> &) noexcept { RANGES_EXPECT(false); } template meta::if_> operator()( indexed_datum> & u) noexcept(std::is_nothrow_constructible::value) { this->construct_(u.get(), meta::make_index_sequence{}); } template meta::if_>> operator()( indexed_datum> & u) noexcept(std::is_nothrow_constructible, Ts...>::value) { this->construct_(u, meta::make_index_sequence{}); } }; template struct get_fn { T ** t_; template [[noreturn]] meta::if_c operator()(indexed_element) const { throw bad_variant_access("bad variant access"); } template void operator()(indexed_element t) const noexcept { *t_ = std::addressof(t.get()); } template void operator()(indexed_element t) const noexcept { U && u = t.get(); *t_ = std::addressof(u); } void operator()(indexed_element) const noexcept {} }; template struct emplace_fn { Variant * var_; // clang-format off template auto CPP_auto_fun(operator())(Ts &&...ts) (const) ( return ranges::emplace(*var_, static_cast(ts)...) ) // clang-format on }; template struct variant_visitor { Fun fun_; Variant * var_; // clang-format off template auto CPP_auto_fun(operator())(indexed_element u) ( return compose(emplace_fn{var_}, fun_)(u) ) // clang-format on }; template variant_visitor make_variant_visitor( Variant & var, Fun fun) noexcept(std::is_nothrow_move_constructible::value) { return {detail::move(fun), &var}; } template struct unique_visitor; template struct unique_visitor, variant> { variant * var_; template void operator()(indexed_element t) const { using E = meta::at_c, N>; static_assert(RANGES_IS_SAME(T const, E const), "Is indexed_element broken?"); using F = meta::find, E>; static constexpr std::size_t M = sizeof...(To) - F::size(); compose(emplace_fn, M>{var_}, get_datum_fn{})(t); } }; template constexpr T & variant_deref_(T * t) noexcept { return *t; } inline void variant_deref_(void const volatile *) noexcept {} template struct variant_get { //////////////////////////////////////////////////////////////////////////////////////////// // get template friend meta::_t< std::add_lvalue_reference, N>>> get(Variant & var) { using elem_t = meta::_t< std::remove_reference, N>>>; elem_t * elem = nullptr; auto & data_var = detail::variant_core_access::data(var); detail::variant_visit_( var.index(), data_var, detail::get_fn{&elem}); return detail::variant_deref_(elem); } template friend meta::_t< std::add_lvalue_reference, N> const>> get(Variant const & var) { using elem_t = meta::_t< std::remove_reference, N> const>>; elem_t * elem = nullptr; auto & data_var = detail::variant_core_access::data(var); detail::variant_visit_( var.index(), data_var, detail::get_fn{&elem}); return detail::variant_deref_(elem); } template friend meta::_t< std::add_rvalue_reference, N>>> get(Variant && var) { using elem_t = meta::_t< std::remove_reference, N>>>; elem_t * elem = nullptr; auto & data_var = detail::variant_core_access::data(var); detail::variant_visit_( var.index(), data_var, detail::get_fn{&elem}); using res_t = meta::_t< std::add_rvalue_reference, N>>>; return static_cast(detail::variant_deref_(elem)); } }; template, meta::as_list>>::value> struct variant_base : variant_get { ~variant_base() { static_cast(this)->clear_(); } }; template struct variant_base, true> : variant_get> {}; template struct variant_visit_results {}; template struct variant_visit_results< Fun, meta::list, meta::index_sequence, meta::void_>...>> { using type = variant>...>; }; template using variant_visit_results_t = meta::_t, meta::make_index_sequence>>; } // namespace detail /// \endcond /// \addtogroup group-utility /// @{ template struct variant : private detail::variant_data , private detail::variant_base> { private: friend detail::variant_core_access; template friend struct variant; friend detail::variant_base; template using datum_t = detail::variant_datum_t; template using add_const_t = meta::if_, void, T const>; using unbox_fn = detail::get_datum_fn; detail::variant_data & data_() & noexcept { return *this; } detail::variant_data const & data_() const & noexcept { return *this; } detail::variant_data && data_() && noexcept { return static_cast &&>(*this); } std::size_t index_; void clear_() noexcept { if(valid()) { detail::variant_visit_(index_, data_(), detail::delete_fn{}, identity{}); index_ = (std::size_t)-1; } } template void assign_(That && that) { if(that.valid()) index_ = detail::variant_move_copy_( that.index_, data_(), ((That &&) that).data_()); } constexpr variant(detail::empty_variant_tag) noexcept : detail::variant_data{} , index_((std::size_t)-1) {} template static constexpr auto all_convertible_to(int) noexcept -> CPP_ret(bool)( // requires(sizeof...(Args) == sizeof...(Ts))) { return and_v...>; } template static constexpr bool all_convertible_to(long) noexcept { return false; } public: CPP_member constexpr CPP_ctor(variant)()( // noexcept(std::is_nothrow_default_constructible>::value) // requires default_constructible>) : variant{emplaced_index<0>} {} CPP_template(std::size_t N, typename... Args)( // requires constructible_from, Args...>) // constexpr variant(RANGES_EMPLACED_INDEX_T(N), Args &&... args) noexcept( std::is_nothrow_constructible, Args...>::value) : detail::variant_data{meta::size_t{}, static_cast(args)...} , index_(N) {} CPP_template(std::size_t N, typename T, typename... Args)( // requires constructible_from, std::initializer_list &, Args...>) // constexpr variant( RANGES_EMPLACED_INDEX_T(N), std::initializer_list il, Args &&... args) noexcept(std:: is_nothrow_constructible< datum_t, std::initializer_list &, Args...>::value) : detail::variant_data{meta::size_t{}, il, static_cast(args)...} , index_(N) {} template constexpr CPP_ctor(variant)(RANGES_EMPLACED_INDEX_T(N), meta::nil_)( // noexcept(std::is_nothrow_constructible, meta::nil_>::value) // requires constructible_from, meta::nil_>) : detail::variant_data{meta::size_t{}, meta::nil_{}} , index_(N) {} variant(variant && that) : detail::variant_data{} , index_(detail::variant_move_copy_(that.index(), data_(), std::move(that.data_()))) {} variant(variant const & that) : detail::variant_data{} , index_(detail::variant_move_copy_(that.index(), data_(), that.data_())) {} template CPP_ctor(variant)(variant that)( // requires(!same_as, variant>) && (all_convertible_to(0))) : detail::variant_data{} , index_(detail::variant_move_copy_(that.index(), data_(), std::move(that.data_()))) {} variant & operator=(variant && that) { // TODO do a simple move assign when index()==that.index() this->clear_(); this->assign_(detail::move(that)); return *this; } variant & operator=(variant const & that) { // TODO do a simple copy assign when index()==that.index() this->clear_(); this->assign_(that); return *this; } template auto operator=(variant that) -> CPP_ret(variant &)( // requires(!same_as, variant>) && (all_convertible_to(0))) { // TODO do a simple copy assign when index()==that.index() this->clear_(); this->assign_(that); return *this; } static constexpr std::size_t size() noexcept { return sizeof...(Ts); } template auto emplace(Args &&... args) -> CPP_ret(void)( // requires constructible_from, Args...>) { this->clear_(); detail::construct_fn fn{static_cast(args)...}; detail::variant_visit_(N, data_(), std::ref(fn), identity{}); index_ = N; } constexpr bool valid() const noexcept { return index() != (std::size_t)-1; } constexpr std::size_t index() const noexcept { return index_; } template detail::variant_visit_results_t, Ts...> visit(Fun fun) { detail::variant_visit_results_t, Ts...> res{ detail::empty_variant_tag{}}; detail::variant_visit_(index_, data_(), detail::make_variant_visitor( res, compose(detail::move(fun), unbox_fn{}))); return res; } template detail::variant_visit_results_t, add_const_t...> visit(Fun fun) const { detail::variant_visit_results_t, add_const_t...> res{detail::empty_variant_tag{}}; detail::variant_visit_(index_, data_(), detail::make_variant_visitor( res, compose(detail::move(fun), unbox_fn{}))); return res; } template detail::variant_visit_results_t visit_i(Fun fun) { detail::variant_visit_results_t res{detail::empty_variant_tag{}}; detail::variant_visit_( index_, data_(), detail::make_variant_visitor(res, detail::move(fun))); return res; } template detail::variant_visit_results_t...> visit_i(Fun fun) const { detail::variant_visit_results_t...> res{ detail::empty_variant_tag{}}; detail::variant_visit_( index_, data_(), detail::make_variant_visitor(res, detail::move(fun))); return res; } }; template auto operator==(variant const & lhs, variant const & rhs) -> CPP_ret(bool)( // requires and_v...>) { return (!lhs.valid() && !rhs.valid()) || (lhs.index() == rhs.index() && detail::variant_equal_(lhs.index(), detail::variant_core_access::data(lhs), detail::variant_core_access::data(rhs))); } template auto operator!=(variant const & lhs, variant const & rhs) -> CPP_ret(bool)( // requires and_v...>) { return !(lhs == rhs); } //////////////////////////////////////////////////////////////////////////////////////////// // emplace template meta::if_c<(bool)constructible_from, Args...>> emplace(variant & var, Args &&... args) { var.template emplace(static_cast(args)...); } //////////////////////////////////////////////////////////////////////////////////////////// // variant_unique template struct variant_unique {}; template struct variant_unique> { using type = meta::apply, meta::unique>>; }; template using variant_unique_t = meta::_t>; //////////////////////////////////////////////////////////////////////////////////////////// // unique_variant template variant_unique_t> unique_variant(variant const & var) { using From = variant; using To = variant_unique_t; auto res = detail::variant_core_access::make_empty(meta::id{}); var.visit_i(detail::unique_visitor{&res}); RANGES_EXPECT(res.valid()); return res; } /// @} } // namespace ranges RANGES_DIAGNOSTIC_PUSH RANGES_DIAGNOSTIC_IGNORE_MISMATCHED_TAGS namespace std { template struct tuple_size<::ranges::variant> : tuple_size> {}; template struct tuple_element> : tuple_element> {}; } // namespace std RANGES_DIAGNOSTIC_POP #endif