Last active
November 3, 2018 16:58
-
-
Save revsic/afa0dcd3e596c1b04cc3bbeb2e581dd6 to your computer and use it in GitHub Desktop.
cpp implementation of functional style match syntax
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 <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