ralpha-assets/Plugins/CesiumForUnreal/Source/ThirdParty/include/swl/variant_visit.hpp

134 lines
3.9 KiB
C++

#ifdef SWL_CPP_LIBRARY_VARIANT_HPP
// ========================= visit dispatcher
template <class Fn, class... Vars>
using rtype_visit = decltype( ( std::declval<Fn>()( std::declval<Vars>().template unsafe_get<0>()... ) ) );
template <class Fn, class Var>
using rtype_index_visit = decltype( ( std::declval<Fn>()( std::declval<Var>().template unsafe_get<0>(),
std::integral_constant<std::size_t, 0>{} ) )
);
inline namespace v1 {
#if defined(__GNUC__) || defined( __clang__ ) || defined( __INTEL_COMPILER )
#define DeclareUnreachable() __builtin_unreachable()
#elif defined (_MSC_VER)
#define DeclareUnreachable() __assume(false)
#else
#error "Compiler not supported, please file an issue."
#endif
#define DEC(N) X((N)) X((N) + 1) X((N) + 2) X((N) + 3) X((N) + 4) X((N) + 5) X((N) + 6) X((N) + 7) X((N) + 8) X((N) + 9)
#define SEQ30(N) DEC( (N) + 0 ) DEC( (N) + 10 ) DEC( (N) + 20 )
#define SEQ100(N) SEQ30((N) + 0) SEQ30((N) + 30) SEQ30((N) + 60) DEC((N) + 90)
#define SEQ200(N) SEQ100((N) + 0) SEQ100((N) + 100)
#define SEQ400(N) SEQ200((N) + 0) SEQ200((N) + 200)
#define CAT(M, N) M##N
#define CAT2(M, N) CAT(M, N)
#define INJECTSEQ(N) CAT2(SEQ, N)(0)
// single-visitation
template <unsigned Offset, class Rtype, class Fn, class V>
constexpr Rtype single_visit_tail(Fn&& fn, V&& v){
constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
#define X(N) case (N + Offset) : \
if constexpr (N < RemainingIndex) { \
return static_cast<Fn&&>(fn)( static_cast<V&&>(v).template unsafe_get<N+Offset>() ); \
break; \
} else DeclareUnreachable();
#define SEQSIZE 200
switch( v.index() ){
INJECTSEQ(SEQSIZE)
default :
if constexpr (SEQSIZE < RemainingIndex)
return vimpl::single_visit_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
else
DeclareUnreachable();
}
#undef X
#undef SEQSIZE
}
template <unsigned Offset, class Rtype, class Fn, class V>
constexpr Rtype single_visit_w_index_tail(Fn&& fn, V&& v){
constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
#define X(N) case (N + Offset) : \
if constexpr (N < RemainingIndex) { \
return static_cast<Fn&&>(fn)( static_cast<V&&>(v).template unsafe_get<N+Offset>(), std::integral_constant<unsigned, N+Offset>{} ); \
break; \
} else DeclareUnreachable();
#define SEQSIZE 200
switch( v.index() ){
INJECTSEQ(SEQSIZE)
default :
if constexpr (SEQSIZE < RemainingIndex)
return vimpl::single_visit_w_index_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
else
DeclareUnreachable();
}
#undef X
#undef SEQSIZE
}
template <class Fn, class V>
constexpr decltype(auto) visit(Fn&& fn, V&& v){
return vimpl::single_visit_tail<0, rtype_visit<Fn&&, V&&>>(SWL_FWD(fn), SWL_FWD(v));
}
// unlike other visit functions, this takes the variant first!
// this is confusing, but make the client code easier to read
template <class Fn, class V>
constexpr decltype(auto) visit_with_index(V&& v, Fn&& fn){
return vimpl::single_visit_w_index_tail<0, rtype_index_visit<Fn&&, V&&>>(SWL_FWD(fn), SWL_FWD(v));
}
template <class Fn, class Head, class... Tail>
constexpr decltype(auto) multi_visit(Fn&& fn, Head&& head, Tail&&... tail){
// visit them one by one, starting with the last
auto vis = [&fn, &head] (auto&&... args) -> decltype(auto) {
return vimpl::visit( [&fn, &args...] (auto&& elem) -> decltype(auto) {
return SWL_FWD(fn)( SWL_FWD(elem), SWL_FWD(args)... );
}, SWL_FWD(head) );
};
if constexpr (sizeof...(tail) == 0)
return SWL_FWD(vis)();
else if constexpr (sizeof...(tail) == 1)
return vimpl::visit( SWL_FWD(vis), SWL_FWD(tail)... );
else
return vimpl::multi_visit(SWL_FWD(vis), SWL_FWD(tail)...);
}
#undef DEC
#undef SEQ30
#undef SEQ100
#undef SEQ200
#undef SEQ400
#undef DeclareUnreachable
#undef CAT
#undef CAT2
#undef INJECTSEQ
}
#endif