Skip to content

Commit

Permalink
🔀 Implement variant multi-visitation
Browse files Browse the repository at this point in the history
`visit` for `variant` is required by the standard to support multiple
variants for a singular visitor, and still invoke the appropriate
overload. This was left out of the 1.0.0 release initially due to the
complexity involved in the solution, but has been implemented here
for completion.

Tests have been added to ensure that this code compiles and operates
correctly on the various target systems. In order to make this code
compile successfully on older systems, such as gcc-4.9, some small
alterations needed to be made for the handling of `tuple`'s `get`
implementation.

Closes #16
  • Loading branch information
bitwizeshift committed May 21, 2020
2 parents c84fa2f + d58feff commit bdf6d6a
Show file tree
Hide file tree
Showing 3 changed files with 527 additions and 72 deletions.
89 changes: 33 additions & 56 deletions include/bpstd/tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,27 +78,40 @@ namespace bpstd {
// Utilities
//----------------------------------------------------------------------------

template <std::size_t N, typename... Types>
constexpr tuple_element_t<N,tuple<Types...>>&
get(tuple<Types...>&) noexcept;
template <std::size_t N, typename... Types>
constexpr tuple_element_t<N,tuple<Types...>>&&
get(tuple<Types...>&&) noexcept;
template <std::size_t N, typename... Types>
constexpr const tuple_element_t<N,tuple<Types...>>&
get(const tuple<Types...>&) noexcept;
template <std::size_t N, typename... Types>
constexpr const tuple_element_t<N,tuple<Types...>>&&
get(const tuple<Types...>&&) noexcept;
namespace detail {
template <typename T>
struct is_tuple : false_type{};

template <typename...Types>
struct is_tuple<std::tuple<Types...>> : true_type{};
} // namespace detail

template <std::size_t N, typename Tuple,
typename = enable_if_t<detail::is_tuple<remove_cvref_t<Tuple>>::value && is_lvalue_reference<Tuple>::value>>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
tuple_element_t<N,remove_reference_t<Tuple>>&
get(Tuple&& t) noexcept
{
return std::get<N>(t);
}

template <std::size_t N, typename Tuple,
typename = enable_if_t<detail::is_tuple<remove_cvref_t<Tuple>>::value && !is_lvalue_reference<Tuple>::value>>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
tuple_element_t<N,remove_reference_t<Tuple>>&&
get(Tuple&& t) noexcept
{
return bpstd::move(std::get<N>(t));
}

template <typename T, typename... Types>
constexpr T& get(tuple<Types...>& t) noexcept;
BPSTD_CPP14_CONSTEXPR T& get(tuple<Types...>& t) noexcept;
template <typename T, typename... Types>
constexpr T&& get(tuple<Types...>&& t) noexcept;
BPSTD_CPP14_CONSTEXPR T&& get(tuple<Types...>&& t) noexcept;
template <typename T, typename... Types>
constexpr const T& get(const tuple<Types...>& t) noexcept;
BPSTD_CPP14_CONSTEXPR const T& get(const tuple<Types...>& t) noexcept;
template <typename T, typename... Types>
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
BPSTD_CPP14_CONSTEXPR const T&& get(const tuple<Types...>&& t) noexcept;

//----------------------------------------------------------------------------

Expand Down Expand Up @@ -151,42 +164,6 @@ namespace bpstd {
// Utilities
//------------------------------------------------------------------------------

template <std::size_t N, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
bpstd::tuple_element_t<N,bpstd::tuple<Types...>>&
bpstd::get(tuple<Types...>& t)
noexcept
{
return std::get<N>(t);
}

template <std::size_t N, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
bpstd::tuple_element_t<N,bpstd::tuple<Types...>>&&
bpstd::get(tuple<Types...>&& t)
noexcept
{
return move(std::get<N>(t));
}

template <std::size_t N, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
const bpstd::tuple_element_t<N,bpstd::tuple<Types...>>&
bpstd::get(const tuple<Types...>& t)
noexcept
{
return std::get<N>(t);
}

template <std::size_t N, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
const bpstd::tuple_element_t<N,bpstd::tuple<Types...>>&&
bpstd::get(const tuple<Types...>&& t)
noexcept
{
return move(std::get<N>(t));
}

namespace bpstd { namespace detail {

template <typename T, std::size_t Index, typename...Types>
Expand All @@ -206,31 +183,31 @@ namespace bpstd { namespace detail {
}} // namespace bpstd::detail

template <typename T, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
T& bpstd::get(tuple<Types...>& t)
noexcept
{
return std::get<detail::index_of<T,Types...>::value>(t);
}

template <typename T, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
T&& bpstd::get(tuple<Types...>&& t)
noexcept
{
return move(std::get<detail::index_of<T,Types...>::value>(t));
}

template <typename T, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
const T& bpstd::get(const tuple<Types...>& t)
noexcept
{
return std::get<detail::index_of<T,Types...>::value>(t);
}

template <typename T, typename... Types>
inline BPSTD_INLINE_VISIBILITY constexpr
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
const T&& bpstd::get(const tuple<Types...>&& t)
noexcept
{
Expand Down
202 changes: 192 additions & 10 deletions include/bpstd/variant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "detail/variant_base.hpp"
#include "detail/variant_visitors.hpp"
#include "detail/variant_traits.hpp"
#include "tuple.hpp"
#include "utility.hpp" // in_place_index_t, in_place_type_t
#include "type_traits.hpp" // conjunction
#include "functional.hpp" // less, greater, equal_to, etc
Expand Down Expand Up @@ -917,18 +918,15 @@ namespace bpstd {
BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t<Visitor,Variant>
visit(Visitor&& visitor, Variant&& v);

// /// \brief Visits the variants \p v0 and \p v1 with the given \p visitor
// /// \brief Visits the variants \p variant0 and \p variants
// ///
// /// \param visitor the visitor to visit the active entry of \p v0
// /// \param v0 the first variant to visit
// /// \param v1 the second variant to visit
// /// \return the result of visiting the variants \p v0 and \p v1
// template <typename Visitor, typename Variant, typename UVariant>
// void visit(Visitor&& visitor, Variant&& v0, UVariant&& v1);

// template <typename Visitor, typename...Variants,
// typename = enable_if_t<(sizeof...(Variants) > 3)>>
// void visit(Visitor&& visitor, Variants&&...vrest);
// /// \param variant0 the first variant to visit
// /// \param variants the rest of the variant to visit
// /// \return the result of visiting the variants
template <typename Visitor, typename Variant0, typename...Variants>
BPSTD_CPP14_CONSTEXPR bpstd::detail::variant_visitor_invoke_result_t<Visitor,Variant0, Variants...>
visit(Visitor&& visitor, Variant0&& variant0, Variants&&...variants);

//----------------------------------------------------------------------------

Expand Down Expand Up @@ -1596,6 +1594,190 @@ bpstd::detail::variant_visitor_invoke_result_t<Visitor,Variant>
);
}

namespace bpstd { namespace detail {

template <typename Variant0, typename...Variants>
inline BPSTD_INLINE_VISIBILITY constexpr
bool are_any_valueless_by_exception(const Variant0& v0, const Variants&...vs)
{
return v0.valueless_by_exception() || are_any_valueless_by_exception(vs...);
}

template <typename Variant0>
inline BPSTD_INLINE_VISIBILITY constexpr
bool are_any_valueless_by_exception(const Variant0& v0)
{
return v0.valueless_by_exception();
}

template <typename...Args, typename T, std::size_t...Idxs>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
std::tuple<Args&&...,T&&> tuple_push_back_aux(std::tuple<Args...>& args, T&& v, index_sequence<Idxs...>)
{
(void) args;
return std::forward_as_tuple(std::get<Idxs>(bpstd::move(args))..., bpstd::forward<T>(v));
}

// Appends an element to a forwarding tuple
// Returns with '&&' to reference-collapse returned types
template <typename...Args, typename T>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
std::tuple<Args&&...,T&&> tuple_push_back(std::tuple<Args...>& args, T&& t)
{
static_assert(
conjunction<is_reference<Args>...,true_type>::value,
"'args' must be a forwarding tuple'"
);
// return std::tuple_cat(args, std::forward_as_tuple(std::forward<T>(t)));
return tuple_push_back_aux(args, bpstd::forward<T>(t), index_sequence_for<Args...>{});
}

template <typename Arg0, typename...Args, std::size_t...Idxs>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
std::tuple<Args...> tuple_pop_front_aux(std::tuple<Arg0,Args...>& args, index_sequence<0, Idxs...>)
{
(void) args;
return std::forward_as_tuple(std::get<Idxs>(bpstd::move(args))...);
}

// Removes the front element of a forwarding tuple
template <typename Arg0, typename...Args>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
std::tuple<Args...> tuple_pop_front(std::tuple<Arg0,Args...>& args)
{
static_assert(
conjunction<is_reference<Arg0>,is_reference<Args>...>::value,
"'args' must be a forwarding tuple'"
);

return tuple_pop_front_aux(args, index_sequence_for<Arg0,Args...>{});
}

template <typename Return, typename Visitor, typename Variants, typename Arguments>
class multi_variant_visitor;

template <typename T, typename Visitor, typename Variants, typename Arguments>
BPSTD_CPP14_CONSTEXPR
multi_variant_visitor<T, Visitor, remove_cvref_t<Variants>, remove_cvref_t<Arguments>>
make_multi_visitor(Visitor&& vistior, Variants&& variants, Arguments&& args);

template <typename Return, typename Visitor, typename Variant0, typename...Variants, typename...Args>
class multi_variant_visitor<Return, Visitor, std::tuple<Variant0,Variants...>, std::tuple<Args...>>
{
static_assert(
conjunction<is_reference<Variant0>, is_reference<Variants>...>::value,
"All Variants must be captured by reference"
);
static_assert(
conjunction<is_reference<Args>...,true_type>::value,
"All arguments must be captured by reference"
);

public:
template <typename UVisitor, typename UVariants, typename Arguments>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
multi_variant_visitor(UVisitor&& visitor, UVariants&& variants, Arguments&& args)
: m_visitor(bpstd::forward<UVisitor>(visitor)),
m_variants(bpstd::forward<UVariants>(variants)),
m_args(bpstd::forward<Arguments>(args))
{

}

template <typename T>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
Return operator()(T&& v)
{
// static_assert(is_reference<T>::value, "T should always be a reference type");

return visit(
detail::make_multi_visitor<Return>(
bpstd::forward<Visitor>(m_visitor),
tuple_pop_front(m_variants),
tuple_push_back(m_args, bpstd::forward<T>(v))
),
std::get<0>(bpstd::move(m_variants)) // 'move' used for reference collapse
);
}

private:

Visitor m_visitor;
std::tuple<Variant0, Variants...> m_variants;
std::tuple<Args...> m_args;
};

template <typename Return, typename Visitor, typename...Args>
class multi_variant_visitor<Return,Visitor,std::tuple<>,std::tuple<Args...>>
{
static_assert(
conjunction<is_reference<Args>...,true_type>::value,
"All arguments must be captured by reference"
);

public:
template <typename UVisitor, typename Variants, typename Arguments>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
multi_variant_visitor(UVisitor&& visitor, Variants&&, Arguments&& args)
: m_visitor(bpstd::forward<UVisitor>(visitor)),
m_args(bpstd::forward<Arguments>(args))
{

}

template <typename T>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
Return operator()(T&& v)
{
return apply(
bpstd::forward<Visitor>(m_visitor),
tuple_push_back(m_args, bpstd::forward<T>(v))
);
}

private:

Visitor m_visitor;
std::tuple<Args...> m_args;
};

template <typename T, typename Visitor, typename Variants, typename Arguments>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
multi_variant_visitor<T,Visitor, remove_cvref_t<Variants>, remove_cvref_t<Arguments>>
make_multi_visitor(Visitor&& visitor, Variants&& variants, Arguments&& args)
{
return {
bpstd::forward<Visitor>(visitor),
bpstd::forward<Variants>(variants),
bpstd::forward<Arguments>(args)
};
}

}} // namespace bpstd::detail

template <typename Visitor, typename Variant0, typename...Variants>
inline BPSTD_INLINE_VISIBILITY BPSTD_CPP14_CONSTEXPR
bpstd::detail::variant_visitor_invoke_result_t<Visitor,Variant0, Variants...>
bpstd::visit(Visitor&& visitor, Variant0&& variant0, Variants&&...variants)
{
using type = bpstd::detail::variant_visitor_invoke_result_t<Visitor,Variant0, Variants...>;

if (detail::are_any_valueless_by_exception(variant0, variants...)) {
throw bad_variant_access{};
}

return visit(
detail::make_multi_visitor<type>(
bpstd::forward<Visitor>(visitor),
std::forward_as_tuple(bpstd::forward<Variants>(variants)...),
std::make_tuple()
),
bpstd::forward<Variant0>(variant0)
);

}


template <typename T, typename...Types>
inline BPSTD_INLINE_VISIBILITY constexpr
bool bpstd::holds_alternative(const variant<Types...>& v)
Expand Down
Loading

0 comments on commit bdf6d6a

Please sign in to comment.