/*============================================================================= Copyright (c) 2011-2015 Bolero MURAKAMI https://github.com/bolero-MURAKAMI/Sprout Distributed under 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) =============================================================================*/ #ifndef SPROUT_VARIANT_VARIANT_HPP #define SPROUT_VARIANT_VARIANT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace sprout { namespace detail { template class variant_impl { template friend class variant; protected: typedef sprout::tuples::tuple tuple_type; typedef sprout::types::type_tuple::type...> decayed_tuple_type; private: template static SPROUT_CONSTEXPR tuple_type init(T&& operand, sprout::index_tuple) { return sprout::tuples::make( sprout::tuples::flexibly_construct, typename sprout::tuples::tuple_element::type()..., SPROUT_FORWARD(T, operand) ); } protected: tuple_type tuple_; int which_; protected: SPROUT_CONSTEXPR variant_impl() : tuple_() , which_() {} template SPROUT_CONSTEXPR variant_impl(T&& operand, Index) : tuple_(init(SPROUT_FORWARD(T, operand), sprout::make_index_tuple::make())) , which_(Index::value) { static_assert(Index::value < sizeof...(Types), "variant<>: invalid operand"); } public: SPROUT_CXX14_CONSTEXPR void swap(variant_impl& other) SPROUT_NOEXCEPT_IF_EXPR(sprout::swap(tuple_, other.tuple_)) { sprout::swap(tuple_, other.tuple_); sprout::swap(which_, other.which_); } variant_impl& operator=(variant_impl const&) = default; }; } // namespace detail // // variant // template class variant : private sprout::detail::variant_impl { private: typedef sprout::detail::variant_impl impl_type; typedef typename impl_type::decayed_tuple_type decayed_tuple_type; public: typedef typename impl_type::tuple_type tuple_type; private: template struct visitor_result_impl_2; template struct visitor_result_impl_2 > : public Visitor::template visitor_result< decltype((std::declval())(sprout::tuples::get(std::declval())))... > {}; template struct visitor_result_impl_1; template struct visitor_result_impl_1 > : public sprout::common_decay< decltype((std::declval())(sprout::tuples::get(std::declval())))... > {}; template struct visitor_result_impl; template struct visitor_result_impl< Visitor, Tuple, typename std::enable_if< sprout::has_result_type >::value >::type > { public: typedef typename sprout::weak_result_type::result_type type; }; template struct visitor_result_impl< Visitor, Tuple, typename std::enable_if< sprout::has_visitor_result::value && !sprout::has_result_type >::value >::type > : public visitor_result_impl_2::type> {}; template struct visitor_result_impl< Visitor, Tuple, typename std::enable_if< !sprout::has_visitor_result::value && !sprout::has_result_type >::value >::type > : public visitor_result_impl_1::type> {}; public: // visitation support template struct visitor_result : public visitor_result_impl {}; template struct visitor_result : public visitor_result_impl {}; template struct visitor_result : public visitor_result_impl {}; template struct visitor_result : public visitor_result_impl {}; private: template static SPROUT_CONSTEXPR typename std::enable_if< I == sizeof...(Types), bool >::type eq(tuple_type const&, tuple_type const&, int) { return false; } template static SPROUT_CONSTEXPR typename std::enable_if< I != sizeof...(Types), bool >::type eq(tuple_type const& l, tuple_type const& r, int which) { return I == which ? sprout::tuples::get(l) == sprout::tuples::get(r) : eq(l, r, which) ; } template static SPROUT_CONSTEXPR typename std::enable_if< static_cast(I) == sizeof...(Types), bool >::type lt(tuple_type const&, tuple_type const&, int) { return false; } template static SPROUT_CONSTEXPR typename std::enable_if< static_cast(I) != sizeof...(Types), bool >::type lt(tuple_type const& l, tuple_type const& r, int which) { return I == which ? sprout::tuples::get(l) < sprout::tuples::get(r) : lt(l, r, which) ; } template static SPROUT_NON_CONSTEXPR typename std::enable_if< static_cast(I) == sizeof...(Types), std::basic_ostream& >::type output(std::basic_ostream& os, tuple_type const&, int) { return os; } template static SPROUT_NON_CONSTEXPR typename std::enable_if< static_cast(I) != sizeof...(Types), std::basic_ostream& >::type output(std::basic_ostream& os, tuple_type const& t, int which) { return I == which ? os << sprout::tuples::get(t) : output(os, t, which) ; } template static SPROUT_CONSTEXPR typename std::enable_if< I == sizeof...(Types) - 1, Result >::type visit(Tuple&& t, Visitor&& v, int) { return SPROUT_FORWARD(Visitor, v)(sprout::tuples::get(SPROUT_FORWARD(Tuple, t))); } template static SPROUT_CONSTEXPR typename std::enable_if< I != sizeof...(Types) - 1, Result >::type visit(Tuple&& t, Visitor&& v, int which) { return I == which ? SPROUT_FORWARD(Visitor, v)(sprout::tuples::get(SPROUT_FORWARD(Tuple, t))) : visit(SPROUT_FORWARD(Tuple, t), SPROUT_FORWARD(Visitor, v), which) ; } private: using impl_type::tuple_; using impl_type::which_; public: // construct/copy/destruct SPROUT_CONSTEXPR variant() : impl_type() {} variant(variant const&) = default; variant(variant&&) = default; template SPROUT_CONSTEXPR variant(T&& operand) : impl_type( SPROUT_FORWARD(T, operand), sprout::types::find_index::type>() ) {} // modifiers SPROUT_CXX14_CONSTEXPR void swap(variant& other) SPROUT_NOEXCEPT_IF_EXPR(std::declval().swap(other)) { impl_type::swap(other); } SPROUT_CXX14_CONSTEXPR variant& operator=(variant const& rhs) { static_cast(*this) = rhs; return *this; } SPROUT_CXX14_CONSTEXPR variant& operator=(variant&& rhs) SPROUT_NOEXCEPT_IF(std::is_nothrow_move_assignable::value) { static_cast(*this) = sprout::move(rhs); return *this; } template SPROUT_CXX14_CONSTEXPR variant& operator=(T&& rhs) { static_cast(*this) = variant(SPROUT_FORWARD(T, rhs)); return *this; } // queries SPROUT_CONSTEXPR int which() const { return which_; } SPROUT_CONSTEXPR bool empty() const { return false; } // relational friend SPROUT_CONSTEXPR bool operator==(variant const& lhs, variant const& rhs) { return lhs.which_ == rhs.which_ && eq<0>(lhs.tuple_, rhs.tuple_, lhs.which_); } friend SPROUT_CONSTEXPR bool operator!=(variant const& lhs, variant const& rhs) { return !(lhs == rhs); } friend SPROUT_CONSTEXPR bool operator<(variant const& lhs, variant const& rhs) { return lhs.which_ < rhs.which_ || (lhs.which_ == rhs.which_ && lt<0>(lhs.tuple_, rhs.tuple_, lhs.which_)) ; } friend SPROUT_CONSTEXPR bool operator>(variant const& lhs, variant const& rhs) { return rhs < lhs; } friend SPROUT_CONSTEXPR bool operator<=(variant const& lhs, variant const& rhs) { return !(rhs < lhs); } friend SPROUT_CONSTEXPR bool operator>=(variant const& lhs, variant const& rhs) { return !(lhs < rhs); } template friend SPROUT_NON_CONSTEXPR std::basic_ostream& operator<<(std::basic_ostream& lhs, variant const& rhs) { return output<0>(lhs, rhs.tuple_, rhs.which_); } // get support template SPROUT_CONSTEXPR typename std::enable_if< I != sizeof...(Types), typename sprout::tuples::tuple_element::type const& >::type get_at() const { return (SPROUT_ASSERT(I == which_), true) ? sprout::tuples::get(tuple_) : sprout::tuples::get(tuple_) ; } template SPROUT_CXX14_CONSTEXPR typename std::enable_if< I != sizeof...(Types), typename sprout::tuples::tuple_element::type& >::type get_at() { return (SPROUT_ASSERT(I == which_), true) ? sprout::tuples::get(tuple_) : sprout::tuples::get(tuple_) ; } template SPROUT_CONSTEXPR typename std::enable_if< sprout::types::contains::value, U const& >::type get() const { return get_at::value>(); } template SPROUT_CXX14_CONSTEXPR typename std::enable_if< sprout::types::contains::value, U& >::type get() { return get_at::value>(); } // visitation support template SPROUT_CONSTEXPR typename visitor_result::type, variant const>::type apply_visitor(Visitor&& visitor) const { typedef typename visitor_result::type, variant const>::type result_type; return SPROUT_ASSERT(0 <= which_ && sprout::math::less(which_, sizeof...(Types))), visit(tuple_, SPROUT_FORWARD(Visitor, visitor), which_) ; } template SPROUT_CXX14_CONSTEXPR typename visitor_result::type, variant>::type apply_visitor(Visitor&& visitor) { typedef typename visitor_result::type, variant>::type result_type; return SPROUT_ASSERT(0 <= which_ && sprout::math::less(which_, sizeof...(Types))), visit(tuple_, SPROUT_FORWARD(Visitor, visitor), which_) ; } }; // // swap // template inline SPROUT_CXX14_CONSTEXPR void swap(sprout::variant& lhs, sprout::variant& rhs) SPROUT_NOEXCEPT_IF_EXPR(lhs.swap(rhs)) { lhs.swap(rhs); } } // namespace sprout #endif // #ifndef SPROUT_VARIANT_VARIANT_HPP