Skip to content

Instantly share code, notes, and snippets.

@jbandela
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbandela/c82b649ef4f31aed229d to your computer and use it in GitHub Desktop.
Save jbandela/c82b649ef4f31aed229d to your computer and use it in GitHub Desktop.
#include <tuple>
#include <utility>
#include <cstdint>
#include <stdexcept>
using std::size_t;
template<typename F, typename Tuple, size_t... I>
auto
apply_(F&& f, Tuple&& args, std::integer_sequence<size_t,I...>)
{
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}
template<typename F, typename Tuple>
auto
apply(F&& f, Tuple&& args)
{
return apply_(std::forward<F>(f), std::forward<Tuple>(args), std::make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type>::value >());
}
template<class T, class U>
struct matcher;
template<class T, class U>
bool match_check(T&& t, U&& u) {
using m = matcher<std::decay_t<T>, std::decay_t<U>>;
return m::check(std::forward<T>(t), std::forward<U>(u));
}
template<class T, class U>
auto match_get(T&& t, U&& u) {
using m = matcher<std::decay_t<T>, std::decay_t<U>>;
return m::get(std::forward<T>(t), std::forward<U>(u));
}
template<class T, class A1, class F1,class A2, class F2, class... Args>
auto match(T&& t, A1&& a, F1&& f, A2&& a2,F2&& f2, Args&&... args) {
if (match_check(t, a)) {
return apply(f, match_get(std::forward<T>(t), std::forward<A1>(a)));
}
else {
return match(t,std::forward<A2>(a2),std::forward<F2>(f2),std::forward<Args>(args)...);
}
}
template<class T, class A1, class F1>
auto match(T&& t, A1&& a, F1&& f) {
if (match_check(std::forward<T>(t), std::forward<A1>(a))) {
return apply(f, match_get(std::forward<T>(t), std::forward<A1>(a)));
}
else {
throw std::logic_error("No match");
}
}
// Match same type
template<class T>
struct matcher<T, T> {
static bool check(const T& t, const T& v) {
return t == v;
}
static auto get(const T&, const T&) {
return std::tie();
}
};
struct otherwise_t {};
const otherwise_t otherwise {};
const otherwise_t _ {};
// Match otherwise
template<class Type>
struct matcher<Type, otherwise_t> {
template<class T>
static bool check(T&&, otherwise_t) {
return true;
}
template<class T>
static auto get(T&&, otherwise_t) {
return std::tie();
}
};
template<class F>
struct matcher_predicate {
F f_;
};
template<class Type, class F>
struct matcher<Type, matcher_predicate<F>> {
template<class T, class U>
static bool check(T&& t, U&& u) {
return u.f_(std::forward<T>(t));
}
template<class T, class U>
static auto get(T&& t, U&&) {
return std::tie(std::forward<T>(t));
}
};
template<class F>
matcher_predicate<F> make_matcher_predicate(F&& f) {
return matcher_predicate<F>{std::forward<F>(f)};
}
const auto _x = make_matcher_predicate([](auto&&) {return true; });
// relational operators
template<class F, class T>
auto operator==(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x == t; });
}
template<class F, class T>
auto operator!=(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x != t; });
}
template<class F, class T>
auto operator<=(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x <= t; });
}
template<class F, class T>
auto operator>=(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x >= t; });
}
template<class F, class T>
auto operator<(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x < t; });
}
template<class F, class T>
auto operator>(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x > t; });
}
template<class F>
auto operator!(const matcher_predicate<F>& m) {
return make_matcher_predicate([m](const auto& x) {return !m.f_(x); });
}
template<class F, class F2>
auto operator&&(const matcher_predicate<F>& m, const matcher_predicate<F2>& m2) {
return make_matcher_predicate([m,m2](const auto& x) {return m.f_(x) && m2.f_(x); });
}
template<class F, class F2>
auto operator||(const matcher_predicate<F>& m, const matcher_predicate<F2>& m2) {
return make_matcher_predicate([m, m2](const auto& x) {return m.f_(x) || m2.f_(x); });
}
// relational operators
template<class F, class T>
auto operator==(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t == x; });
}
template<class F, class T>
auto operator!=(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t != x; });
}
template<class F, class T>
auto operator<=(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t <= x; });
}
template<class F, class T>
auto operator>=(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t >= x; });
}
template<class F, class T>
auto operator<(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t < x; });
}
template<class F, class T>
auto operator>(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t > x; });
}
template<class Type, class... Args>
struct matcher<Type, std::tuple<Args...>> {
enum { tuple_len = sizeof...(Args) };
template<size_t pos, size_t last>
struct helper {
template<class T, class A>
static bool check(T&& t, A&& a) {
return match_check(std::get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a)))
&& helper<pos + 1, last>::check(std::forward<T>(t), std::forward<A>(a));
}
template<class T, class A>
static auto get(T&& t, A&& a) {
return std::tuple_cat(match_get(std::get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a))),
helper<pos + 1, last>::get(std::forward<T>(t), std::forward<A>(a)));
}
};
template<size_t pos>
struct helper<pos, pos> {
template<class T, class A>
static bool check(T&& t, A&& a) {
return match_check(std::get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a)));
}
template<class T, class A>
static auto get(T&& t, A&& a) {
return match_get(std::get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a)));
}
};
template<class T, class A>
static bool check(T&& t, A&& a) {
return helper<0, tuple_len - 1>::check(std::forward<T>(t), std::forward<A>(a));
}
template<class T, class A>
static auto get(T&& t, A&& a) {
return helper<0, tuple_len - 1>::get(std::forward<T>(t), std::forward<A>(a));
}
};
template<class... A>
decltype(auto) tup(A&& ... a) {
return std::forward_as_tuple(std::forward<A>(a)...);
}
#include <iostream>
int main() {
std::tuple<int, int> t{1, 2};
int x = 0;
int y = 0;
using std::tie;
using std::forward_as_tuple;
while (true) {
std::cin >> x;
std::cin >> y;
match(tie(x,y),
tup(1,1), []() {std::cout << "The answer is one\n"; },
tup(2,2), []() {std::cout << "The answer is two\n"; },
tup(_x > 10, _x < 10), [](auto&& a, auto && b) {std::cout << "The answer " << a << " " << b << " is less than 10\n"; },
tup(10 < _x < 20,10 < _x < 20), [](auto&& a, auto&& b) {std::cout << "The answer " << a << " " << b << " is between 10 and 20 exclusive\n"; },
tup(_ ,100 <= _x && _x <= 200), [](auto&& a) {std::cout << "The answer " << a << " is between 100 and 200 inclusive\n"; },
otherwise, []() {std::cout << "Did not match\n"; }
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment