Last active
October 13, 2022 23:53
-
-
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 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
/* | |
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