Skip to content

Instantly share code, notes, and snippets.

@mkitzan
Last active October 13, 2022 23:53
Show Gist options
  • Save mkitzan/ec0c3047f1ac4ecdd2deadc75b81337d to your computer and use it in GitHub Desktop.
Save mkitzan/ec0c3047f1ac4ecdd2deadc75b81337d to your computer and use it in GitHub Desktop.
Twisting the C++ constexpr interpreter and template system into performing ML-like pattern matching
/*
This code implements ML like pattern matching in C++17 using constexpr and the template system.
Like in ML, a standalone function evaluates the expression. However, with C++ templates and
constexpr, the evaluation function for the expression can be embedded within the type. Where
every node type has a static member function eval that performs its step of the expression.
See the link below for SQL WHERE predicate nodes implementing this pattern:
https://github.com/mkitzan/metaprogramming-optimization/blob/master/include/sql/predicate.hpp
https://godbolt.org/z/PQnBXf
clang -std=c++17 -lstdc++ -o pm pattern_matching.cpp
g++ -std=c++17 -o pm pattern_matching.cpp
For reference this is the SML code implemented in C++17:
datatype expr =
Constant of int
| Negate of expr
| Add of expr + expr
| Multiply of expr * expr
fun eval e =
case e of
Constant(value) => value
| Negate(subexpr) => ~(eval(subexpr))
| Add(left,right) => eval(left) + eval(right)
| Multiply(left,right) => eval(left) * eval(right)
eval(Add(Negate(Constant(10)), Multiply(Constant(2), Constant(26))))
*/
#include <iostream>
enum Opcode : int
{
CON, NEG, ADD, MUL,
};
template <Opcode Op>
struct Expr
{};
template <int Value>
struct Constant : Expr<Opcode::CON>
{
static constexpr int value{ Value };
};
template <typename Subexpr>
struct Negate : Expr<Opcode::NEG>
{
using subexpr = Subexpr;
};
template <typename Left, typename Right>
struct Add : Expr<Opcode::ADD>
{
using left = Left;
using right = Right;
};
template <typename Left, typename Right>
struct Multiply : Expr<Opcode::MUL>
{
using left = Left;
using right = Right;
};
template <typename E>
int constexpr eval()
{
if constexpr (std::is_base_of_v<Expr<Opcode::CON>, E>)
{
return E::value;
}
else if constexpr (std::is_base_of_v<Expr<Opcode::NEG>, E>)
{
return -eval<typename E::subexpr>();
}
else if constexpr (std::is_base_of_v<Expr<Opcode::ADD>, E>)
{
return eval<typename E::left>() + eval<typename E::right>();
}
else if constexpr (std::is_base_of_v<Expr<Opcode::MUL>, E>)
{
return eval<typename E::left>() * eval<typename E::right>();
}
}
int main()
{
using Expression = Add<Negate<Constant<10>>, Multiply<Constant<2>, Constant<26>>>;
constexpr int result{ eval<Expression>() };
std::cout << result << '\n';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment