Last active
June 16, 2016 19:04
-
-
Save ecatmur/7f5f8b44e70f414742e4eae1efdd0ca7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <boost/preprocessor/control/if.hpp> | |
#include <boost/preprocessor/punctuation/comma_if.hpp> | |
#include <boost/preprocessor/repetition/repeat.hpp> | |
#include <tuple> | |
#include <type_traits> | |
namespace impl { | |
#define SWITCH_MAX 64 | |
template<class C> struct Size : std::tuple_size<C> {}; | |
template<class T, T... C> | |
struct Size<std::integer_sequence<T, C...>> | |
: std::integral_constant<std::size_t, sizeof...(C)> {}; | |
template<std::size_t N, class C> struct Element | |
: std::tuple_element<N, C> {}; | |
template<std::size_t N, class T, T... C> | |
struct Element<N, std::integer_sequence<T, C...>> | |
: std::tuple_element<N, std::tuple<std::integral_constant<T, C>...>> {}; | |
template<std::size_t N, class C, class A, class F> | |
struct Switch { | |
static_assert(N < SWITCH_MAX, "Please increase SWITCH_MAX"); | |
}; | |
#define SWITCH_CASE(Z,N,_) \ | |
case typename Element<N, C>::type{} : \ | |
return std::forward<F>(f)(typename Element<N, A>::type{}); \ | |
// | |
#define SWITCH_RESULT(Z,N,_) \ | |
BOOST_PP_COMMA_IF(N) std::result_of_t<F(typename Element<N, A>::type)> | |
#define SWITCH_SPECIALIZATION(Z,N,_) \ | |
template<class C, class A, class F> \ | |
struct Switch<N, C, A, F> { \ | |
using R = std::common_type_t< \ | |
BOOST_PP_IF(N,, void) \ | |
BOOST_PP_REPEAT_ ## Z(N, SWITCH_RESULT, nil)>; \ | |
template<class T> \ | |
R operator()(T&& t, F&& f) { \ | |
switch(t) { BOOST_PP_REPEAT_ ## Z(N, SWITCH_CASE, nil) } \ | |
__builtin_unreachable(); \ | |
} \ | |
template<class T, class D> \ | |
std::enable_if_t<std::is_void<R>::value || \ | |
!std::is_void<std::result_of_t<D(T)>>::value, R> \ | |
operator()(T&& t, F&& f, D&& d) { \ | |
switch(t) { BOOST_PP_REPEAT_ ## Z(N, SWITCH_CASE, nil) } \ | |
return std::forward<D>(d)(t); \ | |
} \ | |
template<class T, class D> \ | |
std::enable_if_t<!std::is_void<R>::value && \ | |
std::is_void<std::result_of_t<D(T)>>::value, R> \ | |
operator()(T&& t, F&& f, D&& d) { \ | |
switch(t) { BOOST_PP_REPEAT_ ## Z(N, SWITCH_CASE, nil) } \ | |
std::forward<D>(d)(t); __builtin_unreachable(); \ | |
} \ | |
}; \ | |
// | |
BOOST_PP_REPEAT(SWITCH_MAX, SWITCH_SPECIALIZATION, nil) | |
} | |
template<class C, class A = C, class T, class F> | |
auto switch_(T&& t, F&& f) { | |
return impl::Switch<impl::Size<C>::value, C, A, F>{}( | |
std::forward<T>(t), std::forward<F>(f)); | |
} | |
template<class C, class A = C, class T, class F, class D> | |
auto switch_(T&& t, F&& f, D&& d) { | |
return impl::Switch<impl::Size<C>::value, C, A, F>{}( | |
std::forward<T>(t), std::forward<F>(f), std::forward<D>(d)); | |
} | |
// Examples | |
template<class... V> | |
struct VariantPtr { | |
std::size_t idx; | |
void* data; | |
}; | |
template<class... V, class F> | |
auto apply(VariantPtr<V...> p, F&& f) { | |
return switch_<std::index_sequence_for<V...>, std::tuple<V*...>>(p.idx, | |
[&](auto* P) { return std::forward<F>(f)(static_cast<decltype(P)>(p.data)); }, | |
[](auto) {}); | |
} | |
int main() { | |
VariantPtr<int, float, char> p{1u, new float{3.14}}; | |
return apply(p, [](auto* x) { return -*x; }); | |
} | |
enum class E { A, B, C = 5 }; | |
using Es = std::integer_sequence<E, E::A, E::B, E::C>; | |
int f(E e) { | |
return switch_<Es>(e, [](auto N) { return int(N()); }); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment