<?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>Forem: sentomk</title>
    <description>The latest articles on Forem by sentomk (@byebyyanogawa).</description>
    <link>https://forem.com/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>Forem: sentomk</title>
      <link>https://forem.com/byebyyanogawa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/byebyyanogawa"/>
    <language>en</language>
    <item>
      <title>The 128-arm pattern dispatch problem and how we got from 11ns to 1.1ns</title>
      <dc:creator>sentomk</dc:creator>
      <pubDate>Sun, 03 May 2026 12:06:18 +0000</pubDate>
      <link>https://forem.com/byebyyanogawa/the-128-arm-pattern-dispatch-problem-and-how-we-got-from-11ns-to-11ns-l71</link>
      <guid>https://forem.com/byebyyanogawa/the-128-arm-pattern-dispatch-problem-and-how-we-got-from-11ns-to-11ns-l71</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Pattern matching on a large set of literal values looks clean in code but hits a wall at runtime. Every &lt;code&gt;on()&lt;/code&gt; call constructs case objects for every arm.&lt;/p&gt;

&lt;p&gt;With 128 arms, that is 128 object constructions per match call. At 11ns per call, this is fine for one-off use. Inside a hot loop, it is a disaster.&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="c1"&gt;// Clean syntax, 128 case objects constructed per call&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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_0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 126 more arms&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="n"&gt;default_handler&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cases are compile-time constants. The values do not change. But the library has to construct them every time because the &lt;code&gt;on()&lt;/code&gt; call receives them as function arguments, and C++ evaluates arguments at the call site before any optimization pass can intervene.&lt;/p&gt;

&lt;p&gt;This is not a bug in the library design. It is a structural cost of the pipeline syntax &lt;code&gt;match(x) | on(...)&lt;/code&gt;. The syntax gives you readable, composable matching. The cost is that the case list is a runtime object.&lt;/p&gt;

&lt;p&gt;Or it was, until we found a way to make it not.&lt;/p&gt;

&lt;h2&gt;
  
  
  First attempt: static caching with &lt;code&gt;is_constant_evaluated()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The idea was simple. If the match expression appears in a &lt;code&gt;constexpr&lt;/code&gt; context, the compiler already knows the subject value and all the case values. We can unfold the entire match at compile time and bypass the runtime machinery entirely.&lt;/p&gt;

&lt;p&gt;C++20 gave us &lt;code&gt;std::is_constant_evaluated()&lt;/code&gt;. When this returns true, the compiler is evaluating this code in a constexpr context. We can take the static path:&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;Plan&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;Subject&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;CasesTuple&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CasesTuple&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&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;is_constant_evaluated&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Compile-time path: unfold cases statically&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;static_dispatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Plan&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;subject&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;forward&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CasesTuple&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;cases&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Runtime path: use the normal evaluation engine&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;runtime_dispatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Plan&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;subject&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;forward&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CasesTuple&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;cases&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;Nice in theory. In practice, this only helps when the entire call site is constexpr. For runtime matches with compile-time-known case values, the cases are still constructed. The constexpr branch does not fire.&lt;/p&gt;

&lt;p&gt;We needed something more aggressive: detect at compile time that every case in the list is a static literal, then build a dense dispatch table that the runtime evaluator can use as an O(1) lookup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: &lt;code&gt;[[no_unique_address]]&lt;/code&gt; and &lt;code&gt;std::is_empty&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Static literal patterns look like empty types:&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="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="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;V&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="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 an empty type. &lt;code&gt;[[no_unique_address]]&lt;/code&gt; should allow the compiler to optimize &lt;code&gt;cmp&lt;/code&gt; to zero size, making the whole pattern trivially small.&lt;/p&gt;

&lt;p&gt;We used &lt;code&gt;std::is_empty&lt;/code&gt; as the gate for static caching eligibility:&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="c1"&gt;// Naive check: if the pattern is empty, we can cache it&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;constexpr&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;is_empty_v&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pattern_t&lt;/span&gt;&lt;span class="o"&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="c1"&gt;// ... static cache path&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This broke on GCC. The reason: &lt;code&gt;[[no_unique_address]]&lt;/code&gt; is a permission, not a requirement. GCC does not apply it to non-empty base classes, even when the member is not a base class. The &lt;code&gt;Cmp&lt;/code&gt; member, despite being an empty type, prevents the pattern from satisfying &lt;code&gt;std::is_empty&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The static cache path was silently skipped. The dense dispatch table was never built. The 128-case match stayed at 11ns.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: &lt;code&gt;sizeof &amp;lt;= 1&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The insight was that we do not actually need the type to be empty. We need it to be small enough that caching it is worthwhile. One byte is the smallest addressable unit. If a type is one byte, it can be treated as a tag with no runtime state.&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="c1"&gt;// Fixed: check if the pattern is trivial to cache&lt;/span&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;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;is_cacheable_pattern_v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because &lt;code&gt;static_literal_pattern&lt;/code&gt; has one static data member (&lt;code&gt;value&lt;/code&gt;) and one potentially-zero-size member (&lt;code&gt;cmp&lt;/code&gt;). On GCC, with &lt;code&gt;[[no_unique_address]]&lt;/code&gt; not applied, the pattern might be 1 byte. On Clang, with the attribute applied, it might be 0 bytes (empty base optimization). Either way, &lt;code&gt;sizeof &amp;lt;= 1&lt;/code&gt; is true. The check is portable across GCC, Clang, and MSVC.&lt;/p&gt;

&lt;p&gt;With this fix, the dispatch planner correctly identifies that all 128 cases are static literals. It builds a compile-time index table mapping subject values to case indices. The runtime evaluator becomes:&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="c1"&gt;// Runtime: O(1) lookup with bounds check&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;min_value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="p"&gt;)&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;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;case_index_table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;min_value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;k_invalid_case_index&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;invoke_handler&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;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;idx&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;cases&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="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="nf"&gt;otherwise_handler&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;Benchmarked on an Intel i7-13700K, GCC 13.2, &lt;code&gt;-O3 -march=native&lt;/code&gt;. Subject type: &lt;code&gt;int&lt;/code&gt;, 128 literal arms, uniformly random input.&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;th&gt;vs. switch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Original &lt;code&gt;on()&lt;/code&gt; (constructs all cases)&lt;/td&gt;
&lt;td&gt;10.79 ns&lt;/td&gt;
&lt;td&gt;9.9x slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PTN_ON&lt;/code&gt; macro (manual static, best practice)&lt;/td&gt;
&lt;td&gt;1.16 ns&lt;/td&gt;
&lt;td&gt;1.06x slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto-cache (this fix)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.13 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.04x slower&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hand-written &lt;code&gt;switch&lt;/code&gt; (theoretical lower bound)&lt;/td&gt;
&lt;td&gt;1.09 ns&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The auto-cache approach matches the previous best practice (the &lt;code&gt;PTN_ON&lt;/code&gt; macro) and comes within 0.04ns of a hand-written switch statement. That 0.04ns gap comes from a branch predictor mispredict on the bounds check, which is not under library control.&lt;/p&gt;

&lt;p&gt;For context: the original implementation was 9.9x slower than a hand-written switch. The fix brings it to 1.04x. A 10x improvement from changing one condition in the type traits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this says about library design
&lt;/h2&gt;

&lt;p&gt;The interesting part is not the optimization itself. It is that the entire improvement came from a type trait that was answering the wrong question.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;std::is_empty&lt;/code&gt; asks "does this type have zero size?" That is a property of the type in the abstract C++ machine. But the actual question we needed to answer was "can this pattern be cached without measurable overhead?" That is an engineering question, not a type theory one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sizeof &amp;lt;= 1&lt;/code&gt; answers the engineering question. It says: this type fits in one byte, which is the smallest unit the hardware can address. Whether the compiler achieves this through &lt;code&gt;[[no_unique_address]]&lt;/code&gt;, empty base optimization, or something else — we do not care. The result is portable and measurable.&lt;/p&gt;

&lt;p&gt;This is a pattern I have seen across performance work: the clean theoretical property is not the one that matters. The dirty empirical threshold is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The implementation is open source at &lt;a href="https://github.com/sentomk/patternia" rel="noopener noreferrer"&gt;github.com/sentomk/patternia&lt;/a&gt;. The dispatch planner lives in &lt;code&gt;include/ptn/core/common/optimize.hpp&lt;/code&gt;. The static literal cache logic is in &lt;code&gt;include/ptn/core/common/eval.hpp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Header-only, C++17, no dependencies beyond the standard library.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>performance</category>
      <category>programming</category>
      <category>tutorial</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://forem.com/byebyyanogawa/declarative-json-dispatch-in-modern-c-91n</link>
      <guid>https://forem.com/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://forem.com/byebyyanogawa/patternia-a-zero-overhead-pattern-matching-library-for-c-with-powerful-features-like-generics-jj7</link>
      <guid>https://forem.com/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://forem.com/byebyyanogawa/a-pattern-matching-dsl-for-modern-c-2fo1</link>
      <guid>https://forem.com/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>
