Last active
March 23, 2021 20:42
-
-
Save ZhekehZ/460ce58b3f477f530fcfdef615707004 to your computer and use it in GitHub Desktop.
C++ type-matching (godbolt: https://godbolt.org/z/jY1MrY9dj)
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 <type_traits> | |
#include <functional> | |
template <typename F, typename T> | |
struct case_impl : F { | |
using IS_CASE_IMPL_FOR = T; | |
using F::operator(); | |
constexpr case_impl(F f) : F(f) {} | |
}; | |
struct anycase {}; | |
template <typename T, typename F> | |
constexpr auto case_(F f) -> case_impl<F, T> { return case_impl<F, T>(f); } | |
template <typename T, typename ... Ts> | |
constexpr auto match(Ts...); | |
template <typename T> | |
constexpr auto match() -> void { | |
struct INVALID_PATTERN_MATCHING {}; | |
static_assert(std::is_same_v<T, INVALID_PATTERN_MATCHING>); | |
} | |
template <typename T, typename T1, typename ... Ts> | |
constexpr auto match(T1 t1, Ts ... ts) { | |
using CASE = std::decay_t<typename T1::IS_CASE_IMPL_FOR>; | |
if constexpr (std::is_same_v<CASE, anycase> || std::is_same_v<T, CASE>) { | |
return t1(); | |
} else { | |
return match<T, Ts...>(ts...); | |
} | |
} | |
#define WHEN(T) case_<T> THEN | |
#define THEN(...)([]{__VA_ARGS__;}) | |
#define WHEN_CAPTURE(T) case_<T> THEN_CAPTURE_BEGIN | |
#define THEN_CAPTURE_BEGIN(...) ([__VA_ARGS__]{ THEN_CAPTURE_END | |
#define THEN_CAPTURE_END(...)__VA_ARGS__;}) | |
#define OTHERWISE WHEN(anycase) | |
#define OTHERWISE_CAPTURE WHEN_CAPTURE(anycase) | |
/* | |
Syntax example: | |
match < T > ( | |
WHEN (Type1) ( | |
// Statements | |
), | |
WHEN (Type2) (return 123), | |
WHEN_CAPTURE (TYPE3) (&global_variable) ( | |
return global_variable++; | |
), | |
OTHERWISE ( | |
throw std::runtime_error("otherwise case"); | |
), | |
) | |
*/ | |
template <typename T> | |
static constexpr int type_to_num = | |
match<T> ( | |
WHEN (int ) (return 1), | |
WHEN (char) (return 2), | |
WHEN (bool) (return 3), | |
OTHERWISE (return 0) | |
); | |
int main() { | |
static_assert(type_to_num<int> == 1); | |
static_assert(type_to_num<char> == 2); | |
static_assert(type_to_num<bool> == 3); | |
static_assert(type_to_num<void> == 0); | |
bool first_fail = true; | |
auto is_int = [&first_fail]<typename T>(T) { | |
return match<T> ( | |
WHEN (int) (return "INT =)"), | |
OTHERWISE_CAPTURE (&first_fail) ( | |
if (first_fail) { | |
first_fail = false; | |
return "NOT INT =("; | |
} | |
return "NOT INT AGAIN =(("; | |
) | |
); | |
}; | |
std::cout << is_int(1) << std::endl; // INT =) | |
std::cout << is_int('1') << std::endl; // NOT INT =( | |
std::cout << is_int(true) << std::endl; // NOT INT AGAIN =(( | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment