0% found this document useful (0 votes)
55 views

MPark - Patterns - Pattern Matching in C++ - Michael Park - CppCon 2017

The document describes a C++ library called MPark.Patterns that provides pattern matching functionality in C++. It begins with some history and context on pattern matching in other languages. It then discusses algebraic data types, what pattern matching is, various forms of pattern matching in C++, an overview of the MPark.Patterns library, and other interesting patterns. The purpose of the library is to familiarize the C++ community with pattern matching and gain experience to help guide future language design.

Uploaded by

sinhviencabiet
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
55 views

MPark - Patterns - Pattern Matching in C++ - Michael Park - CppCon 2017

The document describes a C++ library called MPark.Patterns that provides pattern matching functionality in C++. It begins with some history and context on pattern matching in other languages. It then discusses algebraic data types, what pattern matching is, various forms of pattern matching in C++, an overview of the MPark.Patterns library, and other interesting patterns. The purpose of the library is to familiarize the C++ community with pattern matching and gain experience to help guide future language design.

Uploaded by

sinhviencabiet
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 77

MPark.

Patterns
Pattern Matching in C++

https://github.com/mpark/patterns

Michael Park
@mpark
@mcypark

1
History

OCaml Rust
SNOBOL ML C++ Haskell Scala Swift

'62 '73 '83 '90 '96 '04 '10 '14

• Open Pattern Matching for C++ / Mach7 (2013)



Yuriy Solodkyy, Gabriel Dos Reis, Bjarne Stroustrup

• Simple, Extensible C++ Pattern Matching Library (2015)



John Bandela

2
Why doesn't C++
have feature X?

3
No one proposed it

4
Standards Proposal

• P0095R1: Pattern Matching and Language Variants (2016)



David Sankel

• Next Revision: Pattern Matching (TBD)



David Sankel, Michael Park

5
Purpose

• Familiarize pattern matching in the C++ community

• Why now? What's changed?

• Can a modern library solution be "good enough"?

• If not, gain experience to guide the language design

6
Overview

• Algebraic Data Types

• What is Pattern Matching?

• Various Forms of Pattern Matching in C++

• MPark.Patterns

• Other Interesting Patterns

7
Algebraic Data Types

8
Algebraic Data Types

Description Example # of Possible States

Product one of X AND one of Y tuple<X, Y> |X| × |Y|

Sum one of X OR one of Y variant<X, Y> |X| + |Y|

9
Pattern matching is the best tool for decomposing
Algebraic Data Types

10
What is Pattern Matching?

11
“In pattern matching, we attempt to match
values against patterns and, if so desired,
bind variables to successful matches.”

https://en.wikibooks.org/wiki/Haskell/Pattern_matching
12
“In pattern matching, we attempt to match
values against patterns and, if so desired,
bind variables to successful matches.”

struct Point { x: i32, y: i32 }

let p = Point { x: 7, y: 0 };

match p {
Point { x: 0, y } => println!("Y axis: {}", y),
Point { x , y: 0 } => println!("X axis: {}", x),
Point { x , y } => println!("{}, {}", x, y)
}

// prints: "X axis: 7"

https://en.wikibooks.org/wiki/Haskell/Pattern_matching
12
Pattern matching is a declarative approach in lieu of
manually testing for a value with a sequence of
conditionals and extracting the desired components.

struct Point { int


x: i32,
x; int
y: y;
i32};
}

let p = Point { 7,
auto x: 0
7,};
y: 0 };

if (p.x
match p {
== 0) printf("Y axis: %d\n", p.y);
else
Point
if (p.y
{ x: ==
0, y
0) printf("X
} => println!("Y
axis: %d\n",
axis:p.x);
{}", y),
else
Point { x , y: 0
printf("%d,
} => println!("X
%d\n", p.x,
axis:p.y);
{}", x),
Point { x , y } => println!("{}, {}", x, y)
}

// prints: "X axis: 7"

12
Evaluating Expressions
enum Expr {
Int(i32), values

Neg(Box<Expr>), patterns
Add(Box<Expr>, Box<Expr>), variables
Mul(Box<Expr>, Box<Expr>),
}

fn eval(expr: Expr) -> i32 {


return match expr {
Expr::Int(value) => value,
Expr::Neg(expr) => -eval(*expr),
Expr::Add(lhs, rhs) => eval(*lhs) + eval(*rhs),
Expr::Mul(lhs, rhs) => eval(*lhs) * eval(*rhs),
};
}

13
Evaluating Expressions
enum Expr {
Int(i32), values

Neg(Box<Expr>), patterns
Add(Box<Expr>, Box<Expr>), variables
Mul(Box<Expr>, Box<Expr>),
}

fn eval(expr: Expr) -> i32 {


return match expr {
Expr::Int(value) => value,
Expr::Neg(expr) => -eval(*expr),
sum
Expr::Add(lhs, rhs) => eval(*lhs) + eval(*rhs),
Expr::Mul(lhs, rhs) => eval(*lhs) * eval(*rhs),
};
}

13
Evaluating Expressions
enum Expr {
Int(i32), values

Neg(Box<Expr>), patterns
Add(Box<Expr>, Box<Expr>), variables
Mul(Box<Expr>, Box<Expr>),
}

fn eval(expr: Expr) -> i32 {


return match expr {
Expr::Int(value) => value,
Expr::Neg(expr) => -eval(*expr),
sum
Expr::Add(lhs, rhs) => eval(*lhs) + eval(*rhs),
Expr::Mul(lhs, rhs) => eval(*lhs) * eval(*rhs),
};
} product

13
Evaluating Expressions
enum Expr {
Int(i32), values

Neg(Box<Expr>), patterns
Add(Box<Expr>, Box<Expr>), variables
Mul(Box<Expr>, Box<Expr>),
}

fn eval(expr: Expr) -> i32 {


return match expr {
Expr::Int(value) => value,
Expr::Neg(expr) => -eval(*expr),
Expr::Add(lhs, rhs) => eval(*lhs) + eval(*rhs),
Expr::Mul(lhs, rhs) => eval(*lhs) * eval(*rhs),
};
} Composed Patterns!

13
Evaluating Expressions
struct Expr { virtual ~Expr() = default; };
struct Int : Expr { int value; };
struct Neg : Expr { shared_ptr<Expr> expr; };
struct Add : Expr { shared_ptr<Expr> lhs, rhs; };
struct Mul : Expr { shared_ptr<Expr> lhs, rhs; };

int eval(const Expr &expr) {


if (auto p = dynamic_cast<const Int *>(&expr))
return p->value;
if (auto p = dynamic_cast<const Neg *>(&expr))
return -eval(*p->expr);
if (auto p = dynamic_cast<const Add *>(&expr))
return eval(*p->lhs) + eval(*p->rhs);
if (auto p = dynamic_cast<const Mul *>(&expr))
return eval(*p->lhs) * eval(*p->rhs);
throw logic_error("unknown expression");
}

14
Evaluating Expressions

Manually testing for a value with a sequence of


conditionals and extracting the desired components.

int eval(const Expr &expr) {


if (auto p = dynamic_cast<const Int *>(&expr))
return p->value;
if (auto p = dynamic_cast<const Neg *>(&expr))
return -eval(*p->expr);
if (auto p = dynamic_cast<const Add *>(&expr))
return eval(*p->lhs) + eval(*p->rhs);
if (auto p = dynamic_cast<const Mul *>(&expr))
return eval(*p->lhs) * eval(*p->rhs);
throw logic_error("unknown expression");
}

14
LLVM

Manually testing for a value with a sequence of


conditionals and extracting the desired components.

if (const auto *CE = dyn_cast<ImplicitCastExpr>(E))


return // ...
if (const auto *RE = dyn_cast<DeclRefExpr>(E))
return // ...
if (const auto *ME = dyn_cast<MemberExpr>(E))
return // ...
// ...
if (const auto *CE = dyn_cast<CallExpr>(E))
return // ...
if (const auto *CE = dyn_cast<CXXConstructExpr>(E))
return // ...

15
Visitor
struct Int; struct Neg; struct Add; struct Mul; int eval(const Expr &expr) {
struct Eval : Expr::Vis {
struct Expr { void operator()(const Int &that) const {
struct Vis { result = that.value;
virtual void operator()(const Int &) const = 0; }
virtual void operator()(const Neg &) const = 0; void operator()(const Neg &that) const {
virtual void operator()(const Add &) const = 0; result = -eval(*that.expr);
virtual void operator()(const Mul &) const = 0; },
}; void operator()(const Add &that) const {
result = eval(*that.lhs) + eval(*that.rhs);
virtual ~Expr() = default; }
virtual void accept(const Vis&) const = 0; void operator()(const Mul &that) {
}; result = eval(*that.lhs) * eval(*that.rhs);
}
struct Int : Expr {
void accept(const Vis& vis) const { vis(*this); } int &result;
int value; };
};
int result;
struct Neg : Expr { expr.accept(Eval{result});
void accept(const Vis& vis) const { vis(*this); } return result;
shared_ptr<Expr> expr; }
};

struct Add : Expr {


void accept(const Vis& vis) const { vis(*this); }
shared_ptr<Expr> lhs, rhs;

!
};

struct Mul : Expr {


void accept(const Vis& vis) const { vis(*this); }
shared_ptr<Expr> lhs, rhs;
};


16
Insight from Swift

“Pattern matching was probably a foregone


conclusion, but I wanted to spell out that having
ADTs in the language is what really forces our hand
because the alternatives are so bad.”

http://apple-swift.readthedocs.io/en/latest/Pattern%20Matching.html
17
Various Forms of
Pattern Matching in C++

18
Various Forms of
Pattern Matching in C++

• Matching Simple Types

• Matching Product Types

• Matching Sum Types

19
Matching Simple Types
int x = 1;

switch (x) {
switch case 1: case 2: printf("one or two\n"); break;
case 3: printf("three\n"); break;
default: printf("anything\n");
}

string s = "c";

unordered_map<string, void (*)()>{


Python
{ "a", [] { printf("A\n"); } },
Style
{ "b", [] { printf("B\n"); } },
{ "c", [] { printf("C\n"); } }
}[s]()

20
Matching Product Types

pair<X, Y> p;
values

tuple<pair<X, Y>, Z> t; patterns


variables

Destructuring Nested Destructuring

apply(
[](pair<X, Y> p, Z z) {
apply([](X x, Y y) {
apply([z](X x, X y) {
apply // ...
// ...
}, p);
}, p);
}, t);

auto [p, z] = t;
Structured auto [x, y] = p;
auto [x, y] = p;
Binding
auto [[x, y], z] = t;

21
Matching Sum Types
struct Expr;
struct Neg { shared_ptr<Expr> expr; }; values

struct Add { shared_ptr<Expr> lhs, rhs; }; patterns


struct Mul { shared_ptr<Expr> lhs, rhs; }; variables
struct Expr : variant<int, Neg, Add, Mul> {
using variant::variant;
};

int eval(const Expr &expr) {


return visit(overload(
[](int value) { return value; },
[](const Neg &n) { return -eval(*n.expr); },
[](const Add &a) { return eval(*a.lhs) + eval(*a.rhs); },
[](const Mul &m) { return eval(*m.lhs) * eval(*m.rhs); }),
expr);
}

22
MPark.Patterns

23
Main Goals

• Declarative

• Structured

• Cohesive

• Composable

24
Basic Structure
values

patterns
variables
#include <mpark/patterns.hpp>

using namespace mpark::patterns; // omitted from here on


match(<expr>...)(
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
// ...
);

25
Back to the Point
struct Point { int x; int y; };
values

auto p = Point { 7, 0 }; patterns


variables
match(p)(
pattern(ds(0 , arg)) = [](int y) {
printf("Y axis: %d\n", y);
},
pattern(ds(arg, 0 )) = [](int x) {
printf("X axis: %d\n", x);
},
pattern(ds(arg, arg)) = [](int x, int y) {
printf("%d, %d\n", x, y);
}
);

// prints: "X axis: 7"

26
Re: Evaluating Expressions
struct Expr;
values

struct Neg { shared_ptr<Expr> expr; }; patterns


struct Add { shared_ptr<Expr> lhs, rhs; }; variables
struct Mul { shared_ptr<Expr> lhs, rhs; };

struct Expr : variant<int, Neg, Add, Mul> {


using variant::variant;
};

namespace std {

template <>
struct variant_size<Expr> // Opt into `VariantLike`
: integral_constant<size_t, 4> {};

} // namespace std

27
Re: Evaluating Expressions
int eval(const Expr &expr) {
return match(expr)( values

pattern(as<int>(arg)) = [](int value) { patterns


return value;
}, variables
pattern(as<Neg>(ds(arg))) = [](auto &expr) {
return -eval(*expr);
},
pattern(as<Add>(ds(arg, arg))) = [](auto &lhs, auto &rhs) {
return eval(*lhs) + eval(*rhs);
},
pattern(as<Mul>(ds(arg, arg))) = [](auto &lhs, auto &rhs) {
return eval(*lhs) * eval(*rhs);
}
);
}

28
Optional Flag
values

optional<string> flag = "-v";


patterns
variables
match(flag)(
pattern(some(arg(anyof("-v", "--verbose")))) = [](auto &flag) {
// `flag` == "-v" or "--verbose"
},
pattern(some(arg)) = [](auto &flag) {
WHEN(starts_with(flag, "-W")) { // pattern guard!
// ...
};
},
pattern(some(_)) = [] { printf("unknown flag!"); }
pattern(none) = [] {}
);

29
Patterns So Far
Pattern Matches Example

Expression Any 0

Arg / Wildcard Any arg / _

Destructure Array, Aggregate, TupleLike ds(0, _)

As Polymorphic, VariantLike, AnyLike as<Add>(arg)

Optional PointerLike some(_), none

Alternation One of N Patterns anyof("-f", "--force")

30
Simplifying Expressions
Let's simplify the expression tree we've been evaluating.

Simplification Rules:

• -(-v) == v

• v + 0 == v

• v × 1 == v

• v × 0 == 0

31
Simplifying Expressions
struct Expr;

struct Neg { shared_ptr<Expr> expr; };


struct Add { shared_ptr<Expr> lhs, rhs; };
struct Mul { shared_ptr<Expr> lhs, rhs; };

struct Expr : variant<int, Neg, Add, Mul> {


using variant::variant;
};

namespace std {

template <>
struct variant_size<Expr> // Opt into `VariantLike`
: integral_constant<size_t, 4> {};

} // namespace std

32
Simplifying int
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
pattern(as<int>(_)) = [&] { return expr; },
// ...
);
}

result 17 expr

33
Simplifying -(-v)
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
// ...
pattern(as<Neg>(ds(some(as<Neg>(ds(arg)))))) = [](auto &e) {
return simplify(e);
},
// ...
);
} - expr

-
e

result 17

34
Simplifying v + 0
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
// ...
pattern(as<Add>(ds(some(as<int>(0)), arg))) = [](auto &r) {
return simplify(r);
},
pattern(as<Add>(ds(arg, some(as<int>(0))))) = [](auto &l) {
return simplify(l);
},
// ...
); + expr
}
l r

result 17 0

35
Simplifying v × 1
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
// ...
pattern(as<Mul>(ds(some(as<int>(1)), arg))) = [](auto &r) {
return simplify(r);
},
pattern(as<Mul>(ds(arg, some(as<int>(1))))) = [](auto &l) {
return simplify(l);
},
// ...
); × expr
}
l r

result 17 1

36
Simplifying v × 0
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
// ...
pattern(as<Mul>(ds(arg(some(as<int>(0))), _))) = [](auto &l) {
return l;
},
pattern(as<Mul>(ds(_, arg(some(as<int>(0)))))) = [](auto &r) {
return r;
},
// ...
); × expr
}
l r

17 0 result

37
Simplifying -
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
// ...
pattern(as<Neg>(ds(arg))) = [&](auto &e) {
auto simple_e = simplify(e);
return simple_e == e
? expr
: simplify(make_shared<Expr>(Neg{simple_e}));
},
// ...
); result ➖ ➖ expr
} e
result ➖ expr
e ➕

simple_e 17
simple_e 17 0

38
Simplifying +
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
// ...
pattern(as<Add>(ds(arg, arg))) = [&](auto &l, auto &r) {
auto simple_l = simplify(l), simple_r = simplify(r);
return simple_l == l && simple_r == r
? expr
: simplify(make_shared<Expr>(Add{simple_l,
simple_r}));
},
// ... result ➕ ➕ expr
); l
r
} result ➕ expr
l r

simple_l 17 23 simple_l 17
simple_r simple_r 23 0

39
Simplifying ×
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
// ...
pattern(as<Mul>(ds(arg, arg))) = [&](auto &l, auto &r) {
auto simple_l = simplify(l), simple_r = simplify(r);
return simple_l == l && simple_r == r
? expr
: simplify(make_shared<Expr>(Mul{simple_l,
simple_r}));
},
// ... result × × expr
); l
r
} result × expr
l r

simple_l 17 23 simple_l 17
simple_r simple_r 23 0

40
Putting it all together
shared_ptr<Expr> simplify(const shared_ptr<Expr> &expr) {
return match(*expr)(
pattern(as<int>(_)) = [&] { return expr; }, // v
pattern(
anyof(as<Neg>(ds(some(as<Neg>(ds(arg))))), // -(-v)
as<Add>(ds(some(as<int>(0)), arg)), // v + 0
as<Add>(ds(arg, some(as<int>(0)))),
as<Mul>(ds(some(as<int>(1)), arg)), // v * 1
as<Mul>(ds(arg, some(as<int>(1))))) = [](auto &e) {
return simplify(e);
},
pattern(
anyof(as<Mul>(ds(arg(some(as<int>(0))), _)), // v * 0
as<Mul>(ds(_, arg(some(as<int>(0)))))) = [](auto &zero) {
return zero;
},
pattern(as<Neg>(ds(arg))) = [&](auto &e) { /* ... */ },
pattern(as<Add>(ds(arg, arg))) = [&](auto &l, auto &r) { /* ... */ },
pattern(as<Mul>(ds(arg, arg))) = [&](auto &l, auto &r) { /* ... */ }
);
}

41
The real power of pattern matching is that the
patterns are built the same way as the values

42
A Few More Things...

43
Identifiers

• Typically introduced inside the pattern, but...

• Introduced as parameters of a lambda instead

• Patterns do not have any identifiers

44
Pattern Guard
int fib(int n) {
return match(n)(
pattern(arg) = [](int x) {
WHEN(x <= 0) { return 0; };
},
pattern(1) = [] { return 1; },
pattern(arg) = [](int x) {
return fib_v1(x - 1) + fib_v1(x - 2);
});
}

• Lives inside the handler... why?


• Reusing the identifier introduced in the lambda

45
Identifier Pattern
struct Point { int x; int y; };
auto p = Point { 7, 0 };

IDENTIFIERS(x, y);
match(p)(
pattern(ds(0 , y )) = [](int y) {
printf("Y axis: %d\n", y);
},
pattern(ds(x , 0 )) = [](int x) {
printf("X axis: %d\n", x);
},
pattern(ds(x , y )) = [](int x, int y) {
printf("%d, %d\n", x, y);
}
);

// prints: "X axis: 7"

46
Identifier Pattern
Repeated identifiers mean the values have to be equal!

tuple<int, int, int> t = { 101, 202, 101 };

IDENTIFIERS(x, y);
match(t)(
pattern(ds(x, x, x)) = [] { printf("all the same!"); },
pattern(ds(x, y, x)) = [] { printf("bordered!"); },
pattern(_) = [] { printf("No recognized pattern"); });

// prints: "bordered!"

47
Back to the Pattern Guard
int fib(int n) {
IDENTIFIERS(x);
return match(n)(
pattern(x).when(x <= 0) = [](int) { return 0; },
pattern(1) = [] { return 1; },
pattern(x) = [](int x) {
return fib_v1(x - 1) + fib_v1(x - 2);
});
}

48
Variadic Pattern
Syntax: variadic(<pattern>)

• Exactly once anywhere within a Destructure Pattern

• The inner pattern is repeatedly expanded as necessary

tuple<int, int, int> t = { 101, 202, 101 };

match(t)(
pattern(ds(arg, arg, arg)) = [](auto, auto, auto) {});

match(t)(
pattern(ds(variadic(arg))) = [](auto, auto, auto) {});

49
Inside of a template
template <typename Tuple>
void print(Tuple &&tuple) {
match(forward<Tuple>(tuple))(
pattern(ds(variadic(arg))) = [](auto &&... xs) {
int dummy[] = { (cout << xs << ' ', 0)... };
(void)dummy;
}
);
}

print(tuple(101, "hello", 1.1));

// prints: "101 hello 1.1 "

50
This is C++17 apply!
template <typename F, typename Tuple>
decltype(auto) apply(F &&f, Tuple &&t) {
return match(forward<T>(t))(
pattern(ds(variadic(arg))) = forward<F>(f));
}

51
almost tuple_cat
tuple<int, int, int> t = { 101, 202, 101 };

match(t)(
pattern(ds(variadic(arg))) = [](auto, auto, auto) {});

52
almost tuple_cat
template <typename... Tuples>
auto almost_tuple_cat(Tuples &&... tuples) {
return match(forward<Tuples>(tuples)...)(
pattern(variadic(ds(variadic(arg)))) = [](auto &&... xs) {
return make_tuple(forward<decltype(xs)>(xs)...);
});
}

53
Performance

54
MPark.Patterns
void fizzbuzz() {
using namespace mpark::patterns;
for (int i = 1; i <= 100; ++i) {
match(i % 3, i % 5)(
pattern(0, 0) = [] { printf("fizzbuzz\n"); },
pattern(0, _) = [] { printf("fizz\n"); },
pattern(_, 0) = [] { printf("buzz\n"); },
pattern(_, _) = [i] { printf("%d\n", i); });
}
}

55
switch

void fizzbuzz() {
for (int i = 1; i <= 100; ++i) {
switch (i % 3) {
case 0:
switch (i % 5) {
case 0: printf("fizzbuzz\n"); break;
default: printf("fizz\n"); break;
}
break;
default:
switch (i % 5) {
case 0: printf("buzz\n"); break;
default: printf("%d\n", i); break;
}
break;
}
}
}

56
fizzbuzz(): # call printf imul rax, rcx, jmp .LBB1_8
@fizzbuzz() jmp .LBB0_9 1431655766 .LBB1_6: # in Loop:
push rbx .LBB0_4: # in Loop: mov rdx, rax Header=BB1_1 Depth=1
mov ebx, 1 Header=BB0_1 Depth=1 shr rdx, 63 mov edi, .Lstr
.LBB0_1: # =>This Inner test eax, eax shr rax, 32 jmp .LBB1_8
Loop Header: Depth=1 je .LBB0_7 add eax, edx .LBB1_7: # in Loop:
movsxd rcx, ebx mov edi, .Lstr.4 lea edx, [rax + Header=BB1_1 Depth=1
imul rax, rcx, jmp .LBB0_8 2*rax] mov edi, .Lstr.5
1431655766 .LBB0_6: # in Loop: imul rax, rcx, .LBB1_8: # in Loop:
mov rdx, rax Header=BB0_1 Depth=1 1717986919 Header=BB1_1 Depth=1
shr rdx, 63 mov edi, .Lstr mov rsi, rax call puts
shr rax, 32 jmp .LBB0_8 shr rsi, 63 .LBB1_9: # in Loop:

Exactly the same


add eax, edx .LBB0_7: # in Loop: sar rax, 33 Header=BB1_1 Depth=1
lea edx, [rax + Header=BB0_1 Depth=1 add eax, esi inc ebx
2*rax] mov edi, .Lstr.5 lea esi, [rax + cmp ebx, 101
imul rax, rcx, .LBB0_8: # in Loop: 4*rax] jne .LBB1_1

generated code!
1717986919 Header=BB0_1 Depth=1 mov eax, ecx xor eax, eax
mov rsi, rax call puts sub eax, esi pop rbx
shr rsi, 63 .LBB0_9: # in Loop: cmp ecx, edx ret
sar rax, 33 Header=BB0_1 Depth=1 je .LBB1_4 .L.str.3:
add eax, esi inc ebx test eax, eax .asciz "%d\n"
lea esi, [rax + cmp ebx, 101 je .LBB1_6
4*rax] jne .LBB0_1 mov edi, .L.str.3 .Lstr:
mov eax, ecx pop rbx xor eax, eax .asciz "buzz"
sub eax, esi ret mov esi, ebx
cmp ecx, edx main: # @main call printf .Lstr.4:
je .LBB0_4 push rbx jmp .LBB1_9 .asciz "fizz"
test eax, eax mov ebx, 1 .LBB1_4: # in Loop:
je .LBB0_6 .LBB1_1: # =>This Inner Header=BB1_1 Depth=1 .Lstr.5:
mov edi, .L.str.3 Loop Header: Depth=1 test eax, eax .asciz "fizzbuzz"
xor eax, eax movsxd rcx, ebx je .LBB1_7
mov esi, ebx mov edi, .Lstr.4

57
Future Work

58
Future Work

• Determine an API for Ranges

• Experiment further with identifiers

• Exhaustiveness checking

59
MPark.Patterns
Pattern Matching in C++

https://github.com/mpark/patterns

Michael Park
@mpark
@mcypark

60
Implementation Peek

61
Structure
using namespace mpark::patterns;
IDENTIFIERS(<identifier>...); // optional
match(<expr>...)(
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
// ...
);

62
Structure
using namespace mpark::patterns;
IDENTIFIERS(<identifier>...); // optional
match(<expr>...)(
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
// ...
);
template <typename... Patterns>
struct Pattern {
template <typename F>
Case<Pattern, F> operator=(F &&f) && noexcept;

Ds<Patterns &&...> patterns;


};

62
Structure
using namespace mpark::patterns;
IDENTIFIERS(<identifier>...); // optional
match(<expr>...)(
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
// ...
);
template <typename
<typename...
Pattern,
Patterns>
typename F>
struct Case
Pattern
{ {
Pattern
template
pattern;
<typename F>
FCase<Pattern,
f; F> operator=(F &&f) && noexcept;
};
Ds<Patterns &&...> patterns;
};

62
Structure
using namespace mpark::patterns;
IDENTIFIERS(<identifier>...); // optional
match(<expr>...)(
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
pattern(<pattern>...) = [](<binding>...) { /* ... */ },
// ...
);
template
template <typename...
<typename...
<typename Values>
Pattern,
Patterns>
typename F>
struct
struct Match {
Pattern
Case { {
template
Pattern <typename
template
pattern; Pattern,
<typename F> typename F, typename... Cases>
decltype(auto) operator()(Case<Pattern, F> &&case_,
FCase<Pattern,
f; F> operator=(F &&f) && noexcept;
Cases&&... cases) && {
}; // ...
} Ds<Patterns &&...> patterns;
};
tuple<Values &&...> values;
}

62
Match::operator()

template <typename Pattern, typename F, typename... Cases>


decltype(auto) operator()(Case<Pattern, F> &&case_,
Cases&&... cases) && {
auto result = try_match(move(case_).pattern.patterns,
move(values),
move(case_).f);
if (result) {
return move(result).get();
}

if constexpr (sizeof...(Cases) == 0) {
throw match_error{};
} else {
return move(*this)(forward<Cases>(cases)...);
}
}

63
Match::operator()

template <typename Pattern, typename F, typename... Cases>


decltype(auto) operator()(Case<Pattern, F> &&case_,
Cases&&... cases) && {
auto result = try_match(move(case_).pattern.patterns,
move(values),
move(case_).f);
if (result) {
return move(result).get();
}

if constexpr (sizeof...(Cases) == 0) {
throw match_error{};
} else {
return move(*this)(forward<Cases>(cases)...);
}
}

63
match_result<T>

template <typename T>


struct match_result : optional<forwarder<T>> {
using type = T;

using super = optional<forwarder<T>>;


using super::super;

match_result(no_match_t) noexcept {}
match_result(nullopt_t) = delete;

decltype(auto) get() && {


return (*static_cast<super &&>(*this)).forward();
}
};

64
match_result-aware invoke

// `invoke`-like utility for `try_match` functions.


template <typename F, typename... Args>
auto match_invoke(F &&f, Args &&... args) {
using R = invoke_result_t<F, Args...>;
if constexpr (is_void_v<R>) {
invoke(forward<F>(f), forward<Args>(args)...);
return match_result<void>(void_{});
} else if constexpr (is_match_result_v<R>) {
return invoke(forward<F>(f), forward<Args>(args)...);
} else {
return match_result<R>(
invoke(forward<F>(f), forward<Args>(args)...));
}
}

65
Expression Pattern
template <typename ExprPattern, typename Value, typename F>
auto try_match(const ExprPattern &expr_pattern,
Value &&value,
F &&f) {
return expr_pattern == forward<Value>(value)
? match_invoke(forward<F>(f))
: no_match;
}

66
Arg Pattern
template <typename Pattern, typename Value, typename F>
auto try_match(const Arg<Pattern> &arg,
Value &&value,
F &&f) {
if constexpr (is_void_v<Pattern>) {
return match_invoke(forward<F>(f),
forward<Value>(value));
} else {
return try_match(arg.pattern,
forward<Value>(value),
forward<F>(f));
}
}

67

You might also like