types/tagged: add 'visit' call

This commit is contained in:
Danny Robson 2020-04-01 14:52:32 +11:00
parent c9b5605213
commit 4a556af89d

View File

@ -134,4 +134,71 @@ namespace cruft {
cruft::max (sizeof (ValueT)...)
];
};
namespace detail {
// If the tagged object matches the IndexV'th type in ComponentsT then
// invoke visitor with the object as the argument. Else, advance to
// the next index and try with that type.
//
// We assume that outside callers only call this function with an
// IndexV of 0 (so that we have assurances that every type is visited).
template <
std::size_t IndexV,
typename VisitorT,
template <typename...> typename TaggedT,
typename ...ComponentsT
>
requires std::is_same_v<
std::remove_cvref_t<TaggedT<ComponentsT...>>,
tagged<ComponentsT...>
>
decltype (auto)
visit (
VisitorT &&visitor,
TaggedT<ComponentsT...> arg
) {
static_assert (IndexV < sizeof...(ComponentsT));
using nth_t = std::tuple_element_t<IndexV, std::tuple<ComponentsT...>>;
// If we're the last valid index then we must be invoked because
// there's no other option. Do this, and (statically) avoid
// further recursion.
if constexpr (IndexV + 1 == sizeof...(ComponentsT)) {
return std::invoke (visitor, arg.template get<nth_t> ());
} else {
// If the tag matches, then dispatch, else recurse with the
// next available type index.
if (arg.tag () == nth_t::tag) {
return std::invoke (visitor, arg.template get<nth_t> ());
} else {
return visit<IndexV+1> (
std::forward<VisitorT> (visitor),
arg
);
}
}
}
}
template <
typename VisitorT,
template <typename...> typename TaggedT,
typename ...ComponentsT
>
requires std::is_same_v<
std::remove_cvref_t<TaggedT<ComponentsT...>>,
tagged<ComponentsT...>
>
decltype (auto)
visit (VisitorT &&visitor, TaggedT<ComponentsT...> arg)
{
static_assert (sizeof...(ComponentsT));
return detail::visit<0> (
std::forward<VisitorT> (visitor),
arg
);
}
}