Skip to content

Instantly share code, notes, and snippets.

@ecatmur
Last active June 16, 2016 19:04
Show Gist options
  • Save ecatmur/7f5f8b44e70f414742e4eae1efdd0ca7 to your computer and use it in GitHub Desktop.
Save ecatmur/7f5f8b44e70f414742e4eae1efdd0ca7 to your computer and use it in GitHub Desktop.
#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