Skip to content

Instantly share code, notes, and snippets.

@ZhekehZ
Last active March 23, 2021 20:42
Show Gist options
  • Save ZhekehZ/460ce58b3f477f530fcfdef615707004 to your computer and use it in GitHub Desktop.
Save ZhekehZ/460ce58b3f477f530fcfdef615707004 to your computer and use it in GitHub Desktop.
C++ type-matching (godbolt: https://godbolt.org/z/jY1MrY9dj)
#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