/***************************************************************************** * \file variant_union.hpp * * \brief This internal header provides the definition of a utility for * variant, variant_union *****************************************************************************/ /* The MIT License (MIT) Copyright (c) 2020 Matthew Rodusek All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BPSTD_DETAIL_VARIANT_UNION_HPP #define BPSTD_DETAIL_VARIANT_UNION_HPP #include "config.hpp" // BPSTD_CPP14_CONSTEXPR #include "nth_type.hpp" // detail::nth_type #include "move.hpp" // forward #include "variant_traits.hpp" #include // std::size_t #include // std::decay #include BPSTD_COMPILER_DIAGNOSTIC_PREAMBLE namespace bpstd { namespace detail { template struct match_cv { using type = To; }; template struct match_cv { using type = const To; }; template struct match_cv { using type = volatile To; }; template struct match_cv { using type = const volatile To; }; template struct match_reference { using type = To&&; }; template struct match_reference { using type = To&&; }; template struct match_reference { using type = To&; }; template using match_cvref = match_reference::type,To >::type >; template using match_cvref_t = typename match_cvref::type; //////////////////////////////////////////////////////////////////////////// /// \brief A tag type used to represent an empty variant //////////////////////////////////////////////////////////////////////////// struct variant_empty{}; template struct variant_index_tag{}; ///////////////////////////////////////////////////////////////////////////// /// \brief Implementation of the variant_union class /// /// \tparam IsTrivial true if all the types in the variant are trivial /// \tparam Types the types in the variant //////////////////////////////////////////////////////////////////////////// template union variant_union; /// \brief A type-trait for retrieving the number of elements in a variant /// union template struct variant_union_size : variant_union_size::type>{}; template struct variant_union_size> : std::integral_constant{}; //========================================================================== // union : variant_union //========================================================================== // Partial specialization: All types are trivial; is trivially destructible template union variant_union { //------------------------------------------------------------------------ // Public Members Types //------------------------------------------------------------------------ using current_type = Type0; using next_type = variant_union; //------------------------------------------------------------------------ // Public Members //------------------------------------------------------------------------ variant_empty empty; current_type current; next_type next; //------------------------------------------------------------------------ // Constructors //------------------------------------------------------------------------ constexpr variant_union(); template constexpr variant_union(variant_index_tag<0>, Args&&...args); template constexpr variant_union(variant_index_tag, Args&&...args); }; //========================================================================== // union : variant_union //========================================================================== // Partial specialization: At least one type is not trivial // This specialization needs to exist to explicitly define ~variant_union, // but is otherwise the *only* difference template union variant_union { //------------------------------------------------------------------------ // Public Members Types //------------------------------------------------------------------------ using current_type = Type0; using next_type = variant_union; //------------------------------------------------------------------------ // Public Members //------------------------------------------------------------------------ variant_empty empty; current_type current; next_type next; //------------------------------------------------------------------------ // Constructors / Destructor //------------------------------------------------------------------------ constexpr variant_union(); template constexpr variant_union(variant_index_tag<0>, Args&&...args); template constexpr variant_union(variant_index_tag, Args&&...args); ~variant_union(){} }; //========================================================================== // union : variant_union //========================================================================== template union variant_union { //------------------------------------------------------------------------ // Public Members //------------------------------------------------------------------------ variant_empty empty; //------------------------------------------------------------------------ // Constructors //------------------------------------------------------------------------ constexpr variant_union(); template constexpr variant_union(variant_index_tag<0>); }; //========================================================================== // non-member functions : class : variant_union //========================================================================== //-------------------------------------------------------------------------- // Utilities //-------------------------------------------------------------------------- /// \brief Visits the element in the variant_union \p v at index \p n /// /// \param n the index /// \param fn the function to invoke on the underlying value /// \param v the variant_union template BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t visit_union(std::size_t n, Fn&& fn, VariantUnion&& v); /// \brief Visits the elements in the variant_union \p v1 and \p v2 at /// index \p n /// /// \note it is assumed that \p n is the active member of both \p v1 and /// \p v2 /// /// \param n the index /// \param fn the function to invoke on the underlying value /// \param v1 the first variant_union /// \param v2 the second variant_union template BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t visit_union(std::size_t n, Fn&& fn, VariantUnion&& v1, UVariantUnion&& v2); /// \{ /// \brief Gets the element at index \p N out of the variant_union /// /// \tparam N the nth object to retrieve /// \param u the union /// \return the object at N template constexpr bpstd::detail::nth_type_t& union_get(variant_union& u); template constexpr const bpstd::detail::nth_type_t& union_get(const variant_union& u); template constexpr bpstd::detail::nth_type_t&& union_get(variant_union&& u); template constexpr const bpstd::detail::nth_type_t&& union_get(const variant_union&& u); /// \} } // namespace detail } // namespace bpstd //============================================================================== // union : variant_union //============================================================================== //------------------------------------------------------------------------------ // Constructors //------------------------------------------------------------------------------ template inline constexpr bpstd::detail::variant_union ::variant_union() : empty{} { } template template inline constexpr bpstd::detail::variant_union ::variant_union(variant_index_tag<0>, Args&&...args) : current(bpstd::forward(args)...) { } template template inline constexpr bpstd::detail::variant_union ::variant_union(variant_index_tag, Args&&...args) : next{variant_index_tag{}, bpstd::forward(args)...} { } //============================================================================== // union : variant_union //============================================================================== //------------------------------------------------------------------------------ // Constructors //------------------------------------------------------------------------------ template inline constexpr bpstd::detail::variant_union ::variant_union() : empty{} { } template template inline constexpr bpstd::detail::variant_union ::variant_union(variant_index_tag<0>, Args&&...args) : current(bpstd::forward(args)...) { } template template inline constexpr bpstd::detail::variant_union ::variant_union(variant_index_tag, Args&&...args) : next{variant_index_tag{}, bpstd::forward(args)...} { } //============================================================================== // union : variant_union //============================================================================== //------------------------------------------------------------------------------ // Constructors //------------------------------------------------------------------------------ template inline BPSTD_INLINE_VISIBILITY constexpr bpstd::detail::variant_union::variant_union() : empty{} { // base-case; should never be executed } template template inline BPSTD_INLINE_VISIBILITY constexpr bpstd::detail::variant_union::variant_union(variant_index_tag<0>) : empty{} { // base-case; should never be executed } //============================================================================== // non-member functions : class : variant_union //============================================================================== //------------------------------------------------------------------------------ // Utilities //------------------------------------------------------------------------------ namespace bpstd { namespace detail { // Single-case template inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t do_visit_union(variant_index_tag<1>, std::size_t n, Fn&& fn, VariantUnion&& v) { BPSTD_UNUSED(n); return bpstd::forward(fn)( union_get<0>(bpstd::forward(v)) ); } template inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t do_visit_union(variant_index_tag, std::size_t n, Fn&& fn, VariantUnion&& v) { using size_type = variant_union_size; if (n == 0) { return bpstd::forward(fn)( union_get<0>(bpstd::forward(v)) ); } return do_visit_union( variant_index_tag<(size_type::value - 1)>{}, n-1, bpstd::forward(fn), static_cast>(v.next) ); } // Double-case template inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t do_visit_union(variant_index_tag<1>, std::size_t n, Fn&& fn, VariantUnion&& v0, UVariantUnion&& v1) { BPSTD_UNUSED(n); return bpstd::forward(fn)( union_get<0>(bpstd::forward(v0)), union_get<0>(bpstd::forward(v1)) ); } template inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t do_visit_union(variant_index_tag, std::size_t n, Fn&& fn, VariantUnion&& v0, UVariantUnion&& v1) { using size_type = variant_union_size; if (n == 0) { return bpstd::forward(fn)( union_get<0>(bpstd::forward(v0)), union_get<0>(bpstd::forward(v1)) ); } return do_visit_union( variant_index_tag<(size_type::value - 1)>{}, n-1, bpstd::forward(fn), static_cast>(v0.next), static_cast>(v1.next) ); } }} // namespace bpstd::detail template inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t bpstd::detail::visit_union(std::size_t n, Fn&& fn, VariantUnion&& v) { using size_type = variant_union_size; return detail::do_visit_union( variant_index_tag{}, n, bpstd::forward(fn), bpstd::forward(v) ); } template inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t bpstd::detail::visit_union(std::size_t n, Fn&& fn, VariantUnion&& v1, UVariantUnion&& v2) { using size_type = variant_union_size; return detail::do_visit_union( variant_index_tag{}, n, bpstd::forward(fn), bpstd::forward(v1), bpstd::forward(v2) ); } //------------------------------------------------------------------------------ namespace bpstd { namespace detail { // private implementation: recurse on index template inline BPSTD_INLINE_VISIBILITY constexpr nth_type_t& do_union_get(variant_index_tag, variant_union& u) { return do_union_get(variant_index_tag{}, u.next); } template inline BPSTD_INLINE_VISIBILITY constexpr nth_type_t<0,Types...>& do_union_get(variant_index_tag<0>, variant_union& u) { return u.current; } template inline BPSTD_INLINE_VISIBILITY constexpr const nth_type_t& do_union_get(variant_index_tag, const variant_union& u) { return do_union_get(variant_index_tag{}, u.next); } template inline BPSTD_INLINE_VISIBILITY constexpr const nth_type_t<0,Types...>& do_union_get(variant_index_tag<0>, const variant_union& u) { return u.current; } }} // namespace bpstd::detail template inline BPSTD_INLINE_VISIBILITY constexpr bpstd::detail::nth_type_t& bpstd::detail::union_get(variant_union& u) { static_assert(N < sizeof...(Types), "N index out of bounds"); return do_union_get(variant_index_tag{}, u); } template inline BPSTD_INLINE_VISIBILITY constexpr const bpstd::detail::nth_type_t& bpstd::detail::union_get(const variant_union& u) { static_assert(N < sizeof...(Types), "N index out of bounds"); return do_union_get(variant_index_tag{}, u); } template inline BPSTD_INLINE_VISIBILITY constexpr bpstd::detail::nth_type_t&& bpstd::detail::union_get(variant_union&& u) { static_assert(N < sizeof...(Types), "N index out of bounds"); return bpstd::move(do_union_get(variant_index_tag{}, u)); } template inline BPSTD_INLINE_VISIBILITY constexpr const bpstd::detail::nth_type_t&& bpstd::detail::union_get(const variant_union&& u) { static_assert(N < sizeof...(Types), "N index out of bounds"); return bpstd::move(do_union_get(variant_index_tag{}, u)); } BPSTD_COMPILER_DIAGNOSTIC_POSTAMBLE #endif /* BPSTD_DETAIL_VARIANT_UNION_HPP */