Skip to content

Instantly share code, notes, and snippets.

@riogu
Last active July 24, 2025 15:30
Show Gist options
  • Save riogu/73514511225221737dd0f934003db324 to your computer and use it in GitHub Desktop.
Save riogu/73514511225221737dd0f934003db324 to your computer and use it in GitHub Desktop.
match construct for std::variant using macros

using some templates with some hacky macros to allow matching on the contents of a std::variant, and getting the underlying value immediately.

also ignores if we match() on unique_ptr/shared_ptr/stack variable, match() works the same.

#include <matching-variant.hpp>
struct Add; struct Sub;struct Mul;struct Div; struct Var;struct Func;
using ASTNode = std::variant<Add, Sub, Mul, Div, Var, Func>;
struct Add{ std::unique_ptr<ASTNode> lhs; std::unique_ptr<ASTNode> rhs;};
struct Sub{ int lhs, rhs;};
struct Mul{int lhs;};
struct Div{};
struct Var{};
struct Func{};
int main() {
ASTNode v = Add {
.lhs = std::make_unique<ASTNode>(Sub {1, 2}),
.rhs = std::make_unique<ASTNode>(Sub {2, 4}),
};
match(v) {
holds(Add, &add_var) {
match(add_var.lhs) {
holds(Mul, mul) {
std::cout << "mul was: " << mul.lhs << '\n';
}
holds(Sub, sub) std::cout << "inner was sub: "<< sub.lhs;
_ {}
}
}
holds(Sub, sub) std::cout << "was sub";
holds(Mul, mul) std::cout << "was mul.\n";
_ std::cerr << "type unknown.\n";
}
}
#include <iostream>
#include <memory>
#include <type_traits>
#include <variant>
template<typename T, typename Head, typename... Tail>
constexpr int type_index(int index = 0) {
if constexpr (std::is_same_v<T, Head>) return index;
else
return type_index<T, Tail...>(index + 1);
}
template<typename... Types>
struct type_sequence {
constexpr type_sequence(const std::variant<Types...>&) {}
constexpr type_sequence(std::unique_ptr<std::variant<Types...>>&) {}
constexpr type_sequence(std::shared_ptr<std::variant<Types...>>&) {}
template<typename T>
constexpr static int idx = type_index<T, Types...>();
};
template<typename ...Types> inline size_t index_of(std::shared_ptr<std::variant<Types...>>& node) { return node->index(); }
template<typename ...Types> inline size_t index_of(std::unique_ptr<std::variant<Types...>>& node) { return node->index(); }
template<typename ...Types> inline size_t index_of(std::variant<Types...>& node) { return node.index(); }
template<typename T, typename ...Types> auto& get_elem(std::unique_ptr<std::variant<Types...>>& node) { return std::get<T>(*node); }
template<typename T, typename ...Types> auto& get_elem(std::shared_ptr<std::variant<Types...>>& node) { return std::get<T>(*node); }
template<typename T, typename ...Types> auto& get_elem(std::variant<Types...>& node) { return std::get<T>(node); }
#define match(v) \
{ \
auto& fumo__v__ = v; \
bool was_default = false; \
switch (auto fumo__t_seq__ = type_sequence(v); index_of(v)) {
#define holds(T, name) \
break;} \
case fumo__t_seq__.idx<std::remove_pointer<std ::remove_cvref<T>::type>::type>: { \
T name = get_elem<std::remove_pointer<std ::remove_cvref<T>::type>::type>(fumo__v__);
#define _ \
break; \
} \
default: \
was_default = true; \
} \
if (was_default)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment