<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: sentomk</title>
    <description>The latest articles on DEV Community by sentomk (@byebyyanogawa).</description>
    <link>https://dev.to/byebyyanogawa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3602381%2F23f10931-ab00-4282-a5ed-7075e66ab2df.jpg</url>
      <title>DEV Community: sentomk</title>
      <link>https://dev.to/byebyyanogawa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/byebyyanogawa"/>
    <language>en</language>
    <item>
      <title>How We Made a 128-Arm Pattern Match Run 10 Faster</title>
      <dc:creator>sentomk</dc:creator>
      <pubDate>Fri, 08 May 2026 08:39:22 +0000</pubDate>
      <link>https://dev.to/byebyyanogawa/how-we-made-a-128-arm-pattern-match-run-10-faster-4k7</link>
      <guid>https://dev.to/byebyyanogawa/how-we-made-a-128-arm-pattern-match-run-10-faster-4k7</guid>
      <description>&lt;h1&gt;
  
  
  How We Made a 128-Arm Pattern Match Run 10× Faster
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;The 128-branch dispatch problem, and the compile-time optimization system that solved it.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Pattern matching is coming to C++ — eventually. C++26 might bring it. But if you're writing C++17 today and need to match on variants, enums, or arbitrary values with guards, destructuring, and combinators, you're on your own.&lt;/p&gt;

&lt;p&gt;I've been building &lt;a href="https://github.com/sentomk/patternia" rel="noopener noreferrer"&gt;Patternia&lt;/a&gt;, a C++17 header-only pattern matching library, for the past year. The API is simple enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;ptn/patternia.hpp&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;ptn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ready&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;     &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Pending&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"error: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;                      &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"unknown"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. But the API isn't the hard part. &lt;strong&gt;The hard part is making it fast.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When you write a pattern match with 128 cases (not unusual for protocol parsers or command dispatchers), every &lt;code&gt;on()&lt;/code&gt; call constructs those 128 case objects — guards, destructuring patterns, combinators — every single time the match is evaluated.&lt;/p&gt;

&lt;p&gt;The naive approach: 10.79 nanoseconds per call.&lt;/p&gt;

&lt;p&gt;A hand-written switch with 128 branches: 1.09 nanoseconds per call.&lt;/p&gt;

&lt;p&gt;That's a 10× gap. If your library is slower than the code it replaces, nobody will use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Automatic Dispatch Plan Selection
&lt;/h2&gt;

&lt;p&gt;The core insight is that at compile time, the library knows everything about the patterns — which ones are compile-time constants, which ones match variant types, which ones have guards. So why not let the compiler pick the optimal dispatch strategy?&lt;/p&gt;

&lt;p&gt;Patternia does this with a six-tier dispatch plan hierarchy, selected at compile time via &lt;code&gt;dispatch_plan_selector&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. static_literal_dense   — O(1) array lookup for val&amp;lt;V&amp;gt; compile-time values
2. literal_runtime_dense  — O(1) for runtime lit() values
3. literal_linear         — Direct dispatch for integral/enum subjects
4. variant_simple         — Precomputed case-index table by variant alt
5. variant_alt_bucketed   — Binary search over alt-bucketed cases
6. sequential             — Linear fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selector analyzes the pattern set and picks the best strategy at compile time — no runtime overhead for the decision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;AnalysisResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;dispatch_plan_selector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;dispatch_plan_kind&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;analysis_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;can_use_static_literal_dispatch&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;dispatch_plan_kind&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;static_literal_dense&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analysis_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;can_use_runtime_literal_dense_dispatch&lt;/span&gt;
                   &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;dispatch_plan_kind&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;literal_runtime_dense&lt;/span&gt;
                   &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analysis_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;can_use_simple_literal_dispatch&lt;/span&gt;
                          &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;dispatch_plan_kind&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;literal_linear&lt;/span&gt;
                          &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analysis_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;can_use_simple_variant_dispatch&lt;/span&gt;
                                 &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;dispatch_plan_kind&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;variant_simple&lt;/span&gt;
                                 &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analysis_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;can_use_variant_alt_dispatch&lt;/span&gt;
                                        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;dispatch_plan_kind&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;variant_alt_bucketed&lt;/span&gt;
                                        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dispatch_plan_kind&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sequential&lt;/span&gt;&lt;span class="p"&gt;))));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tier applies density heuristics to decide whether to engage. For example, &lt;code&gt;static_literal_dense&lt;/code&gt; only activates when the value span is at most 4× the number of literal cases — ensuring the lookup table isn't too sparse. Tuning parameters are baked in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;k_static_literal_dense_dispatch_max_cases&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;k_static_literal_dense_dispatch_max_span&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;k_static_literal_dense_dispatch_max_density&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;k_runtime_literal_dense_dispatch_max_cases&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;k_runtime_literal_dense_dispatch_max_span&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;k_runtime_literal_dense_dispatch_max_density&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The &lt;code&gt;[[no_unique_address]]&lt;/code&gt; Bug
&lt;/h2&gt;

&lt;p&gt;Here's where it got weird. The optimization system checks whether a pattern type is "cacheable" — empty types can be skipped, reducing the case construction overhead. It used &lt;code&gt;std::is_empty&lt;/code&gt; for this check.&lt;/p&gt;

&lt;p&gt;One pattern type, &lt;code&gt;static_literal_pattern&lt;/code&gt;, holds a comparator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;Cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;equal_to&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;static_literal_pattern&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;pattern_base&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;static_literal_pattern&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Cmp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;no_unique_address&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="n"&gt;Cmp&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;std::equal_to&amp;lt;&amp;gt;&lt;/code&gt; is technically an empty type. We annotated it with &lt;code&gt;[[no_unique_address]]&lt;/code&gt;. But GCC applied the attribute inconsistently — sometimes the member was optimized away, sometimes not. When GCC left it in, &lt;code&gt;std::is_empty&lt;/code&gt; returned &lt;code&gt;false&lt;/code&gt; for the pattern type, and the cache path was silently skipped.&lt;/p&gt;

&lt;p&gt;The fix: replace &lt;code&gt;std::is_empty&lt;/code&gt; with a &lt;code&gt;sizeof &amp;lt;= 1&lt;/code&gt; heuristic. Works consistently across GCC, Clang, and MSVC.&lt;/p&gt;

&lt;p&gt;Finding this bug required reading the generated assembly for a dozen compiler versions. The lesson: &lt;code&gt;[[no_unique_address]]&lt;/code&gt; is a hint, not a guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;With automatic dispatch plan selection, the 128-branch match drops from 10.79 ns to 1.13 ns — within 4% of a hand-written switch (1.09 ns):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw &lt;code&gt;on()&lt;/code&gt; (naive)&lt;/td&gt;
&lt;td&gt;10.79 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PTN_ON macro (manual cache)&lt;/td&gt;
&lt;td&gt;1.16 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto-cache (this work)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.13 ns&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hand-written switch&lt;/td&gt;
&lt;td&gt;1.09 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key win: automatic optimization means users get the fast path without knowing dispatch plans exist. They just write &lt;code&gt;match(subject) | on(...)&lt;/code&gt; and the compile-time analyzer does the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Benchmarks
&lt;/h2&gt;

&lt;p&gt;Beyond the 128-branch microbenchmark, Patternia holds its own in realistic scenarios:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Patternia&lt;/th&gt;
&lt;th&gt;Baseline&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Command parser&lt;/td&gt;
&lt;td&gt;1.790 ns&lt;/td&gt;
&lt;td&gt;Switch (2.258 ns)&lt;/td&gt;
&lt;td&gt;+26% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocol router&lt;/td&gt;
&lt;td&gt;1.628 ns&lt;/td&gt;
&lt;td&gt;IfElse (1.866 ns)&lt;/td&gt;
&lt;td&gt;+14% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variant mixed&lt;/td&gt;
&lt;td&gt;1.997 ns&lt;/td&gt;
&lt;td&gt;std::visit (1.991 ns)&lt;/td&gt;
&lt;td&gt;Tied&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;p&gt;Patternia isn't the only C++ pattern matching library. But it brings a few things the others don't:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Patternia&lt;/th&gt;
&lt;th&gt;matchit.cpp&lt;/th&gt;
&lt;th&gt;Mach7&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline syntax&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compile-time dispatch optimization&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structural bindings&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guard expressions&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pattern combinators (any/all/neg)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C++ standard&lt;/td&gt;
&lt;td&gt;C++17&lt;/td&gt;
&lt;td&gt;C++17&lt;/td&gt;
&lt;td&gt;C++14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header-only&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The automatic dispatch optimization is the differentiator. Other libraries can match — Patternia matches &lt;em&gt;fast&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The library is a single header include away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CMake FetchContent, vcpkg, or just copy the headers&lt;/span&gt;
git clone https://github.com/sentomk/patternia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;ptn/patternia.hpp&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;ptn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Your first pattern match, automatically optimized&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;         &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"other"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compile-time optimizer picks the right dispatch strategy. You don't have to think about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/sentomk/patternia" rel="noopener noreferrer"&gt;Patternia on GitHub&lt;/a&gt;&lt;/strong&gt; — 179 stars, 438 commits, C++17 header-only.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>cpp17</category>
      <category>metaprogramming</category>
      <category>performance</category>
    </item>
    <item>
      <title>Declarative JSON Dispatch in Modern C++</title>
      <dc:creator>sentomk</dc:creator>
      <pubDate>Fri, 19 Dec 2025 19:34:50 +0000</pubDate>
      <link>https://dev.to/byebyyanogawa/declarative-json-dispatch-in-modern-c-91n</link>
      <guid>https://dev.to/byebyyanogawa/declarative-json-dispatch-in-modern-c-91n</guid>
      <description>&lt;p&gt;Working with JSON in C++ is no longer a matter of parsing bytes. Mature libraries such as &lt;em&gt;nlohmann/json&lt;/em&gt; already provide a robust, dynamic representation of JSON values. The real challenge emerges one layer above parsing: &lt;strong&gt;how to structure the control flow that interprets those values&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For small examples, a linear chain of &lt;code&gt;if&lt;/code&gt; / &lt;code&gt;else if&lt;/code&gt; checks is often sufficient. As soon as the logic becomes recursive or semantically nuanced—distinguishing empty arrays from non-empty ones, or recognizing objects with specific structural properties—the code begins to accumulate branching noise. Classification, branching, and behavior become tightly coupled, and the resulting control flow is difficult to reason about locally.&lt;/p&gt;

&lt;p&gt;This article examines that problem from a different angle. Rather than asking how to optimize conditional logic, it asks whether the dispatch itself can be described declaratively, while still operating on an existing, dynamic JSON model.&lt;/p&gt;

&lt;p&gt;The complete implementation discussed here is available at&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/sentomk/patternia/tree/main/samples/json_dispatch" rel="noopener noreferrer"&gt;https://github.com/sentomk/patternia/tree/main/samples/json_dispatch&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Only the essential fragments are shown below.&lt;/p&gt;


&lt;h2&gt;
  
  
  JSON as a semantic sum type
&lt;/h2&gt;

&lt;p&gt;Although &lt;code&gt;nlohmann::json&lt;/code&gt; is dynamically typed, its runtime behavior is effectively that of a &lt;em&gt;sum type&lt;/em&gt;. A value is exactly one of several mutually exclusive alternatives: null, boolean, number, string, array, or object. Many real-world JSON consumers already reason about values in this way, even if the code does not make that structure explicit.&lt;/p&gt;

&lt;p&gt;Once this perspective is adopted, the desired control flow becomes clearer. Each branch of interpretation should be defined by &lt;em&gt;what kind of value it matches&lt;/em&gt;, possibly refined by additional semantic constraints, and finally associated with a concrete action. The difficulty lies not in expressing any individual check, but in expressing the &lt;em&gt;structure of the dispatch itself&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Traditional approaches each fall short in different ways. Conditional chains scale poorly as cases accumulate. Visitor-style designs introduce indirection and boilerplate without addressing the semantic coupling between matching and behavior. Variant-based dispatch requires translating JSON into a closed algebraic data type, which is often impractical or undesirable.&lt;/p&gt;

&lt;p&gt;What is missing is a way to describe dispatch logic directly, as a composition of semantic cases, without replacing the underlying data model.&lt;/p&gt;


&lt;h2&gt;
  
  
  A declarative match pipeline
&lt;/h2&gt;

&lt;p&gt;The approach explored here expresses JSON interpretation as a single, explicit match pipeline. Each case states what it matches and what it does, without embedding that logic in control-flow scaffolding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;parse_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;is_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;is_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print_bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                           &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;is_uint&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                          &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print_uint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;is_float&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                         &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;is_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;is_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_t&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;   &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handle_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;has_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;                &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handle_named_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;otherwise&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;unknown&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This formulation separates three concerns that are usually intertwined: classification, value binding, and behavior. The match expression itself is a declarative description of the dispatch strategy, not an encoded sequence of branches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Explicit binding as a semantic boundary
&lt;/h2&gt;

&lt;p&gt;One notable design constraint is that &lt;strong&gt;nothing binds implicitly.&lt;/strong&gt; Binding is an explicit operation, introduced by&lt;code&gt;bind()&lt;/code&gt;. In this example, every case binds the entire JSON value as a single unit and passes it to the handler.&lt;/p&gt;

&lt;p&gt;This constraint is deliberate. It establishes a clear semantic boundary: matching determines &lt;em&gt;whether&lt;/em&gt; a case applies, binding determines what data becomes available, and handlers are free to ignore or consume that data as needed. Guards are evaluated only after binding, ensuring that predicates operate on concrete values rather than symbolic placeholders.&lt;/p&gt;

&lt;p&gt;As a result, handler signatures are stable and predictable. Each handler in this example receives exactly one argument, const json&amp;amp;, regardless of how complex the matching condition is.&lt;/p&gt;




&lt;h2&gt;
  
  
  Guards as semantic constraints
&lt;/h2&gt;

&lt;p&gt;Predicates such as is_type and has_field are ordinary functions returning callables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;is_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_t&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;has_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_object&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the match expression, however, these predicates serve a semantic role rather than a control-flow one. A guard refines the meaning of a case; it does not represent an executable branch. Read declaratively, a case states: &lt;em&gt;bind the subject, then accept this case only if it satisfies this constraint.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This distinction becomes increasingly important as predicates grow more complex or are reused across multiple cases. The match expression remains a description of intent, rather than an encoded decision tree.&lt;/p&gt;




&lt;h2&gt;
  
  
  Handlers as isolated effects
&lt;/h2&gt;

&lt;p&gt;Handlers are intentionally free of classification logic. They perform output, recursion, or side effects, but they do not decide whether they should run.&lt;/p&gt;

&lt;p&gt;For example, the null handler does not even consume the bound value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;print_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;...)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"null&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler’s signature reflects its intent: it responds to a semantic category, not to a specific data shape. This decoupling allows handlers to evolve independently of the matching structure and makes it clear which part of the system is responsible for which decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recursive structure without indirection
&lt;/h2&gt;

&lt;p&gt;Recursive traversal emerges naturally from this formulation. Arrays and objects simply invoke the same dispatcher on their elements, without requiring auxiliary state or visitor hierarchies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;handle_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"array ["&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;parse_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recursion is explicit, local, and unsurprising. There is no hidden control flow and no implicit coupling between traversal and dispatch strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this structure scales
&lt;/h2&gt;

&lt;p&gt;The advantage of this approach is not syntactic novelty. It lies in the discipline it enforces. Classification is localized to patterns, semantic constraints are expressed as guards, and behavior is confined to handlers. As new cases are introduced—additional structural distinctions, schema-like checks, or specialized interpretations—the match expression grows horizontally rather than becoming more deeply nested.&lt;/p&gt;

&lt;p&gt;The result is code that remains readable as a &lt;em&gt;description of semantics, even as complexity increases.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Toward structural extraction
&lt;/h2&gt;

&lt;p&gt;In this example, handlers inspect JSON values internally after binding. A natural extension is an extractor pattern: a pattern that validates structure and binds subcomponents directly. In a JSON context, such a pattern could match an object with specific fields and bind those fields as independent values.&lt;/p&gt;

&lt;p&gt;This would move structural decomposition out of handlers and into the pattern layer itself, further clarifying the intent of each case. The current formulation already makes this extension straightforward, because matching, binding, and handling are explicitly separated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing remarks
&lt;/h2&gt;

&lt;p&gt;This JSON dispatcher is small by design, but it is not a toy example. It demonstrates how pattern matching can serve as a control-flow language embedded in C++, even in the absence of native language support. By treating JSON as a semantic sum type and making dispatch structure explicit, the resulting code becomes easier to extend, audit, and reason about.&lt;/p&gt;

&lt;p&gt;The full implementation, including all predicates and handlers, is available at&lt;br&gt;
&lt;a href="https://github.com/sentomk/patternia/tree/main/samples/json_dispatch" rel="noopener noreferrer"&gt;https://github.com/sentomk/patternia/tree/main/samples/json_dispatch&lt;/a&gt; .&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Patternia - A zero-overhead pattern matching library for C++ with powerful features like generics and structural binding.</title>
      <dc:creator>sentomk</dc:creator>
      <pubDate>Mon, 15 Dec 2025 17:43:12 +0000</pubDate>
      <link>https://dev.to/byebyyanogawa/patternia-a-zero-overhead-pattern-matching-library-for-c-with-powerful-features-like-generics-jj7</link>
      <guid>https://dev.to/byebyyanogawa/patternia-a-zero-overhead-pattern-matching-library-for-c-with-powerful-features-like-generics-jj7</guid>
      <description>&lt;p&gt;Patternia is a high-performance, zero-overhead pattern matching library for C++ that enables developers to express complex conditional logic in a concise and type-safe manner. It provides a comprehensive system for pattern matching, including support for structural patterns, value-based conditions, and type-safe evaluation. The library allows for seamless integration with modern C++ features like lambdas and templates, providing a powerful tool for working with complex data structures without incurring performance costs. Patternia is designed to help you write clean, efficient, and maintainable code for both simple and complex pattern matching tasks.&lt;/p&gt;

&lt;p&gt;To explore how Patternia can be used, check out the sample code at &lt;a href="https://github.com/sentomk/patternia/tree/main/samples" rel="noopener noreferrer"&gt;Patternia Samples&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A pattern matching DSL for modern C++</title>
      <dc:creator>sentomk</dc:creator>
      <pubDate>Sat, 08 Nov 2025 10:11:31 +0000</pubDate>
      <link>https://dev.to/byebyyanogawa/a-pattern-matching-dsl-for-modern-c-2fo1</link>
      <guid>https://dev.to/byebyyanogawa/a-pattern-matching-dsl-for-modern-c-2fo1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hey everyone!&lt;/strong&gt; 👋&lt;br&gt;
This is my first time posting about my open-source project here — it’s called &lt;strong&gt;Patternia&lt;/strong&gt;, a header-only compile-time pattern-matching DSL for modern C++.&lt;/p&gt;

&lt;p&gt;It provides a clean and expressive syntax for writing &lt;code&gt;match&lt;/code&gt; expressions, all evaluated entirely at compile time with zero runtime overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SentoMK/patternia" rel="noopener noreferrer"&gt;🔗 GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d love to hear your feedback — especially on &lt;strong&gt;performance&lt;/strong&gt;, &lt;strong&gt;syntax feel&lt;/strong&gt;, and &lt;strong&gt;any compiler corner cases&lt;/strong&gt; you might find.&lt;/p&gt;

&lt;p&gt;More updates are coming soon as I keep tuning and polishing the internals 👻👻&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cpp</category>
    </item>
  </channel>
</rss>
