Skip to content

Instantly share code, notes, and snippets.

@Quuxplusone
Last active June 13, 2016 02:25
Show Gist options
  • Save Quuxplusone/3f7100f72551f6a7028844b298c43508 to your computer and use it in GitHub Desktop.
Save Quuxplusone/3f7100f72551f6a7028844b298c43508 to your computer and use it in GitHub Desktop.
#pragma once
#ifndef H_SWITCH_ON
#define H_SWITCH_ON
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
namespace xstd {
class bad_switch_case : public std::exception {
public:
bad_switch_case() = default;
virtual const char *what() const noexcept override { return "no matching case in switch"; }
};
template<typename Condition, typename Action>
class switch_generic_case {
std::tuple<Condition, Action> ca;
public:
template<typename C, typename A>
constexpr switch_generic_case(C&& c, A&& a) : ca(std::forward<C>(c), std::forward<A>(a)) {}
constexpr const Action& action() const { return std::get<1>(ca); }
template<typename U>
constexpr bool accepts(U&& u) const {
return std::get<0>(ca)(std::forward<U>(u));
}
};
template<typename C, typename A>
constexpr auto make_switch_generic_case(const C&& c, const A&& a)
{
using Condition = std::decay_t<C>;
using Action = std::decay_t<A>;
return switch_generic_case<Condition, Action>(c, a);
}
template<typename V, typename A>
constexpr auto make_switch_case(V&& v, A&& a)
{
return make_switch_generic_case(
[value = std::forward<V>(v)](auto&& x){ return std::forward<decltype(x)>(x) == value; },
std::forward<A>(a)
);
}
template<typename A>
constexpr auto make_switch_default(A&& a)
{
return make_switch_generic_case(
[](auto&&) { return true; },
std::forward<A>(a)
);
}
template<typename... Cases>
class detail_switch_;
template<>
class detail_switch_<> {
public:
detail_switch_() = default;
template<typename U> void on(U&& u) { throw bad_switch_case(); }
};
template<typename... Conditions, typename... Actions>
class detail_switch_<switch_generic_case<Conditions, Actions>...> {
std::tuple<switch_generic_case<Conditions, Actions>...> cases;
template<typename U, size_t I>
constexpr auto detail_on_(U&& u, std::index_sequence<I>, int) const {
const auto& caseI = std::get<I>(cases);
if (caseI.accepts(std::forward<U>(u))) {
return caseI.action()();
} else {
throw bad_switch_case();
}
}
template<typename U, size_t I, size_t J, size_t K, size_t... Is>
constexpr auto detail_on_(U&& u, std::index_sequence<I, J, K, Is...>, ...) const {
const auto& caseI = std::get<I>(cases);
const auto& caseJ = std::get<J>(cases);
const auto& caseK = std::get<K>(cases);
if (caseI.accepts(std::forward<U>(u))) {
return caseI.action()();
} else if (caseJ.accepts(std::forward<U>(u))) {
return caseJ.action()();
} else {
return detail_on_(std::forward<U>(u), std::index_sequence<K, Is...>{}, 0);
}
}
template<typename U, size_t I, size_t... Is>
constexpr auto detail_on_(U&& u, std::index_sequence<I, Is...>, ...) const {
const auto& caseI = std::get<I>(cases);
if (caseI.accepts(std::forward<U>(u))) {
return caseI.action()();
} else {
return detail_on_(std::forward<U>(u), std::index_sequence<Is...>{}, 0);
}
}
public:
template<typename... Cs>
constexpr detail_switch_(Cs&&... cs) : cases(std::forward<Cs>(cs)...) {}
template<typename U>
constexpr auto on(U&& u) const {
return this->detail_on_(
std::forward<U>(u),
std::make_index_sequence<sizeof...(Actions)>{},
0
);
}
};
template<typename... Cs>
constexpr auto make_switch(const Cs&&... cases)
{
return detail_switch_<std::decay_t<Cs>...>(cases...);
}
template<typename V, typename... Cs>
constexpr auto switch_on(V&& v, Cs&&... cases)
{
return make_switch(std::forward<Cs>(cases)...).on(std::forward<V>(v));
}
} // namespace xstd
#ifdef TESTING
template<class... V>
struct VariantPtr {
std::size_t idx;
void* data;
};
template<class... V, class F>
auto apply(VariantPtr<V...> p, F&& f) {
return detail_apply_(p, std::forward<F>(f), std::make_index_sequence<sizeof...(V)>{});
}
template<class... V, class F, size_t... Is>
auto detail_apply_(VariantPtr<V...> p, F&& f, std::index_sequence<Is...>) {
return xstd::switch_on(p.idx,
xstd::make_switch_case(Is, [&](){
using elt_type = std::tuple_element_t<Is, std::tuple<V...>>;
return std::forward<F>(f)(static_cast<elt_type*>(p.data));
})...
);
}
struct Fake { template<typename T> auto operator()(T *x) const { *x = -*x; } };
template auto apply(VariantPtr<int,float,char,double,long,long long,unsigned,unsigned long>, Fake&&);
#endif // TESTING
#endif // H_SWITCH_ON
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment