A header-only, zero-overhead pattern matching library for modern C++ that transforms branching logic into expressive, maintainable code.
- Quick Start
Install Patternia and write your first pattern match case.
- From Control Flow to Pattern Matching
Refactoringif/switchinto declarative pattern-based logic.
- Pattern Matching in Other Languages
How Patternia relates to pattern matching systems in other languages.
- Custom Predicate Guards
Defining and using custom guards inwhen(...).
-
Policy Constraint Matching
Expressing rule-based access policies as declarative constraints. -
Geometric Constraint Matching
Expressingx² + y² + z² < 1as a declarative pattern.
Pattern matching is a control-flow mechanism that selects execution paths based on the structural form, construction, or type of values. By combining discrimination, decomposition, and structured binding into a single construct, pattern matching allows programs to reason about data shapes directly, rather than through ad-hoc conditional logic. Compared to traditional if-else chains or switch statements, pattern matching offers a more declarative and data-oriented way to express branching logic.
Further reading:
- Haskell Pattern Matching
- Rust Pattern Matching
- Scala Match Expressions
- Python Structural Pattern Matching (PEP 634)
In modern C++, control flow over heterogeneous or structured data is typically expressed using a combination of if/else, switch, type checks, and manual data access. While these mechanisms are flexible and expressive, they tend to scale poorly as data structures become more complex or evolve over time. Type discrimination, structural access, and business logic are often interleaved, making the resulting control flow harder to read, maintain, and extend.
// Conventional control flow (conceptual)
if (value is TypeA) {
auto& a = as<TypeA>(value);
if (a.field > threshold) {
...
}
} else if (value is TypeB) {
auto& b = as<TypeB>(value);
...
} else {
...
}In this style, control flow is organized around conditions and checks, rather than around the structure of the data itself. The logic for discriminating types, accessing fields, and introducing local variables is scattered across branches, often leading to duplicated checks and implicit assumptions about data invariants.
With pattern matching, control flow can instead be organized around data shapes. Each branch explicitly states the structure it expects and introduces bindings only when the match succeeds. This aligns control flow more closely with the semantics of the data and makes each branch self-contained.
// Control flow with pattern matching (conceptual)
match value {
when PatternA(x) if x > threshold => {
...
}
when PatternB(y) => {
...
}
otherwise => {
...
}
}By unifying discrimination, decomposition, and binding, pattern matching allows control flow to be expressed declaratively and locally. This approach reduces boilerplate, minimizes accidental complexity, and provides a clearer foundation for reasoning about completeness and correctness as data structures evolve.
Patternia is designed to address several long-standing pain points in C++ control flow and data-oriented logic—especially in codebases that operate on evolving data structures, heterogeneous types, or complex branching rules.
In idiomatic C++, logic that depends on the shape or internal structure of data is typically expressed through a mix of:
- type checks (
std::variant,dynamic_cast, tag fields), - manual field access,
- nested conditionals,
- and ad-hoc invariants enforced implicitly by control flow.
As a result, structural assumptions about data are rarely explicit, and reasoning about correctness often requires reading across multiple branches.
Patternia allows control flow to be written in terms of structure:
match(p)
.when(bind(has<&Point::x, &Point::y>()) >> [](int x, int y) {
// explicitly operates on {x, y}
})
.otherwise(...);Each branch clearly states what shape of data it expects and what it binds, making invariants explicit and local.
In conventional C++, decomposition (extracting fields) and conditional checks are usually interleaved:
if (p.x + p.y == 0 && p.x > 0) {
...
}As conditions grow more complex—especially when they involve relationships between multiple values—this style becomes increasingly opaque.
Patternia separates these concerns:
- Patterns describe how data is decomposed.
- Guards describe constraints over the bound values.
match(p)
.when(
bind(has<&Point::x, &Point::y>())[arg<0> + arg<1> == 0] >>
[](int x, int y) { ... }
);This separation improves readability and enables richer forms of reasoning over multi-value relationships.
Traditional control flow relies on raw boolean expressions, which:
- do not compose well,
- cannot be reused independently of control flow,
- and provide no structural context.
Patternia introduces first-class guard expressions that are:
- composable (
&&,||), - expressive (relational, arithmetic, predicate-based),
- and structurally aware (single-value and multi-value guards).
bind()[_ > 0 && _ < 10]
bind(has<&A::x, &A::y>())[arg<0> * arg<1> > 100]Guards become part of the pattern language rather than incidental conditions.
C++ provides multiple mechanisms for branching:
switchfor integral values,if constexprfor compile-time decisions,std::visitfor variants,- and manual destructuring for aggregates.
These mechanisms are orthogonal and non-uniform, often forcing developers to mix paradigms within the same function.
Patternia offers a single abstraction that can express:
- literal matching,
- relational matching,
- structural decomposition,
- guarded constraints,
- and fallback behavior
within one coherent, expression-oriented model.
Patternia treats pattern matching as an expression, not just a control-flow statement:
auto result = match(n)
.when(lit(0) >> 0)
.when(lit(1) >> 1)
.otherwise([] { return compute(); });At the same time, it adheres strictly to C++’s zero-overhead principle:
- no runtime type erasure,
- no virtual dispatch,
- no heap allocation,
- no hidden control flow.
All matching logic is resolved through templates and inlined calls, allowing the compiler to optimize aggressively.
Ultimately, Patternia is not about replacing if or switch.
It is about elevating data shape—structure, relationships, and constraints—to a first-class concept in C++ control flow.
This makes Patternia particularly suitable for:
- state machines,
- protocol handling,
- geometric and numeric logic,
- AST processing,
- rule-based systems,
- and any domain where what the data looks like matters as much as what its value is.
Patternia is a header-only library that brings expressive pattern matching to modern C++.
Key Features:
- Zero-overhead abstraction with compile-time optimization
- Expression-oriented matching with value-returning capabilities
- Structural pattern matching for complex data shapes
- Composable guard system for conditional logic
- Type-safe bindings and explicit data flow
Quick Example:
#include <ptn/patternia.hpp>
int classify(int x) {
return ptn::match(x)
.when(ptn::lit(0) >> 0)
.when(ptn::lit(1) >> 1)
.otherwise(-1);
}For complete installation instructions, comprehensive examples, and in-depth tutorials, visit the Getting Started Guide.
Jump directly to a specific part of the Patternia API.
match(subject)match(subject, case_tuple(...)).when(pattern >> handler).otherwise(...).end()- Fallback Mechanisms Comparison
[]Guard Attachment_Placeholder (Single-value Guards)rng(...)Range Guardsarg<N>(Multi-value Guards)- Custom Predicate Guards (Lambda)
has<&T::member...>- Structural Constraints
- Structural Binding with
bind() - Partial Structural Matching
- Design Rationale & Guarantees
Note
This API reference documents Patternia’s language constructs, not just function signatures. Each section explains the semantic role and design intent of the API, in addition to usage examples.
Contributions are welcome.
Please read CONTRIBUTING.md before submitting issues or pull requests.
This project is governed by a Code of Conduct.