#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 namespace sprout { template class variant; namespace detail { template class variant_impl { template friend class variant; protected: typedef sprout::tuples::tuple tuple_type; typedef sprout::types::type_tuple::type...> uncvref_tuple_type; private: template static SPROUT_CONSTEXPR tuple_type init(T&& operand, sprout::index_tuple) { return sprout::tuples::make( typename sprout::tuples::tuple_element::type()..., sprout::forward(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(operand), sprout::index_range<0, Index::value>::make())) , which_(Index::value) { static_assert(Index::value < sizeof...(Types), "variant<>: invalid operand"); } public: void swap(variant_impl& other) SPROUT_NOEXCEPT_EXPR(SPROUT_NOEXCEPT_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::uncvref_tuple_type uncvref_tuple_type; public: typedef typename impl_type::tuple_type tuple_type; private: template struct visitor_result_impl_1; template struct visitor_result_impl_1 > { public: typedef typename std::decay< typename std::common_type< decltype((std::declval())(sprout::tuples::get(std::declval())))... >::type >::type type; }; template struct visitor_result_impl; template struct visitor_result_impl< Visitor, Tuple, typename std::enable_if >::value>::type > : public visitor_result_impl_1::value>::type> {}; template struct visitor_result_impl< Visitor, Tuple, typename std::enable_if >::value>::type > { public: typedef typename sprout::weak_result_type::result_type type; }; public: 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& l, tuple_type const& r, int which) { 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& l, tuple_type const& r, int which) { 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 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 os; } template static 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 which) { return I == which ? sprout::forward(v)(sprout::tuples::get(sprout::forward(t))) : throw std::domain_error("variant<>: bad visit") ; } 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(v)(sprout::tuples::get(sprout::forward(t))) : visit(sprout::forward(t), sprout::forward(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(operand), sprout::types::find_index::type>() ) {} // modifiers void swap(variant& other) SPROUT_NOEXCEPT_EXPR(SPROUT_NOEXCEPT_EXPR(std::declval().swap(other))) { impl_type::swap(other); } variant& operator=(variant const& rhs) { static_cast(*this) = rhs; return *this; } variant& operator=(variant&& rhs) SPROUT_NOEXCEPT_EXPR(std::is_nothrow_move_assignable::value) { static_cast(*this) = sprout::move(rhs); return *this; } template variant& operator=(T&& rhs) { static_cast(*this) = variant(sprout::forward(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 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 I == static_cast(which_) ? sprout::tuples::get(tuple_) : (throw std::domain_error("variant<>: bad get"), sprout::tuples::get(tuple_)) ; } template typename std::enable_if< I != sizeof...(Types), typename sprout::tuples::tuple_element::type& >::type get_at() { return I == which_ ? sprout::tuples::get(tuple_) : (throw std::domain_error("variant<>: bad get"), sprout::tuples::get(tuple_)) ; } template SPROUT_CONSTEXPR typename std::enable_if< sprout::types::find_index::value != sizeof...(Types), U const& >::type get() const { return get_at::value>(); } template typename std::enable_if< sprout::types::find_index::value != sizeof...(Types), 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 visit(tuple_, sprout::forward(visitor), which_); } template typename visitor_result::type, variant>::type apply_visitor(Visitor&& visitor) { typedef typename visitor_result::type, variant>::type result_type; return visit(tuple_, sprout::forward(visitor), which_); } }; // // swap // template inline void swap(sprout::variant& lhs, sprout::variant& rhs) SPROUT_NOEXCEPT_EXPR(SPROUT_NOEXCEPT_EXPR(lhs.swap(rhs))) { lhs.swap(rhs); } } // namespace sprout #endif // #ifndef SPROUT_VARIANT_VARIANT_HPP