Skip to content

Instantly share code, notes, and snippets.

@revsic
Last active November 3, 2018 16:58
Show Gist options
  • Save revsic/afa0dcd3e596c1b04cc3bbeb2e581dd6 to your computer and use it in GitHub Desktop.
Save revsic/afa0dcd3e596c1b04cc3bbeb2e581dd6 to your computer and use it in GitHub Desktop.
cpp implementation of functional style match syntax
#include <iostream>
#include <tuple>
#include <type_traits>
template <typename T, typename U>
using comparison_t = decltype(std::declval<T>() == std::declval<U>());
template <typename T, typename U, typename = std::void_t<>>
struct is_comparable : std::false_type {};
template <typename T, typename U>
struct is_comparable<T, U, std::void_t<comparison_t<T, U>>>
: std::is_same<comparison_t<T, U>, bool> {};
template <typename T, typename U>
inline constexpr bool is_comparable_v = is_comparable<T, U>::value;
template <typename T>
struct case_m {
T cond;
constexpr case_m(T&& cond) : cond(std::forward<T>(cond)) {
// Do Nothing
}
template <typename F>
constexpr std::tuple<T, F> operator>>(F&& fn) {
return std::make_tuple(std::forward<T>(cond), std::forward<F>(fn));
}
};
template <typename S>
struct match_ {
S obj;
constexpr match_(S&& obj) : obj(std::forward<S>(obj)) {
// Do Nothing
}
template <typename F>
constexpr auto custom_invoke(F&& func) {
if constexpr (std::is_invocable_v<F, S>) {
return func(obj);
}
else if constexpr (std::is_invocable_v<F>) {
return func();
}
}
template <typename F>
constexpr bool condition(F&& cond) {
if constexpr (is_comparable_v<F, S>) {
return obj == cond;
}
else {
return custom_invoke(cond);
}
}
template <typename T>
constexpr auto impl(T&& match) {
auto const&[cond, fn] = match;
if (condition(cond)) {
return custom_invoke(fn);
}
else {
return decltype(custom_invoke(fn))();
}
}
template <typename T, typename... Ts>
constexpr auto impl(T&& match, Ts&&... others) {
auto const&[cond, fn] = match;
if (condition(cond)) {
return custom_invoke(fn);
}
else {
return impl(std::forward<Ts>(others)...);
}
}
template <typename... T>
constexpr auto operator()(T&&... matches) {
return impl(std::forward<T>(matches)...);
}
};
template <typename S>
constexpr auto match(S&& obj) {
return match_<S>(std::forward<S>(obj));
}
inline case_m default_m([](auto) { return true; });
int main() {
std::string target;
std::cin >> target;
match(target)(
case_m(std::string_view("456")) >>
[]{ std::cout << "same with 456" << std::endl; },
case_m([](auto& a) { return a.size() == 3; }) >>
[](auto& a) { std::cout << a << " length 3" << std::endl; },
case_m([](auto& a) { return a[0] == '1'; }) >>
[]{ std::cout << "start with 1" << std::endl; },
default_m >>
[]{ std::cout << "default value" << std::endl; }
);
constexpr auto cmp = match(3)(
case_m(5) >> []{ return "same with 5"; },
case_m([](int a) { return a > 5; }) >> []{ return "over 5"; },
case_m([](int a) { return a < 5; }) >> []{ return "under 5"; }
);
static_assert(cmp, "under 5");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment