<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[In Search of Perfect Things]]></title><description><![CDATA[In Search of Perfect Things]]></description><link>https://blog.antonr.net</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 23:07:18 GMT</lastBuildDate><atom:link href="https://blog.antonr.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Turn Your Linux Server into a Windows-Friendly File Server in Minutes]]></title><description><![CDATA[If you have a Linux server at home or in a small office, chances are you also have Windows machines that need to access files on it. The traditional path — installing and configuring Samba by hand — i]]></description><link>https://blog.antonr.net/how-to-turn-your-linux-server-into-a-windows-friendly-file-server-in-minutes</link><guid isPermaLink="true">https://blog.antonr.net/how-to-turn-your-linux-server-into-a-windows-friendly-file-server-in-minutes</guid><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Thu, 09 Apr 2026 08:22:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69551dc3bba8173246bb086d/bdb2382e-2233-4268-92fd-056d06c38f3f.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you have a Linux server at home or in a small office, chances are you also have Windows machines that need to access files on it. The traditional path — installing and configuring Samba by hand — involves editing config files, managing users at the OS level, and debugging NetBIOS discovery issues. It works, but it is tedious and fragile.</p>
<p>Docker makes this dramatically simpler. You describe what you want with environment variables, run <code>docker compose up -d</code>, and it is done. No config files to hand-edit, no leftover state from previous experiments, easy to tear down and rebuild.</p>
<p>This article walks through a complete setup using two containers: <code>samba</code> for the file sharing itself, and <code>wsdd2</code> so the server appears automatically in the Windows network neighborhood.</p>
<hr />
<h2>What You Need</h2>
<ul>
<li>A Linux host with Docker and Docker Compose installed</li>
<li>The directories you want to share already existing on the host</li>
<li>Ports 445 (SMB) and 1900 (WS-Discovery) not blocked by a firewall</li>
</ul>
<hr />
<h2>The Compose File</h2>
<pre><code class="language-yaml">name: samba

services:
    samba:
        image: ghcr.io/antrv/samba
        container_name: samba
        restart: unless-stopped
        cap_add:
            - NET_ADMIN
        network_mode: host
        volumes:
            - /etc/localtime:/etc/localtime:ro
            - /host/share1:/shares/share1
            - /host/share2:/shares/share2
        environment:
            - MACHINENAME=FILES
            - MACHINETITLE=File Server
            - WORKGROUP=HOME

            - USER1_NAME=username
            - USER1_UID=1000
            - USER1_PASSWORD=password
            #- USER1_PASSWORD_FILE=/run/secrets/user1_password

            - SHARE1_NAME=share1
            - SHARE1_PATH=/shares/share1
            - SHARE1_COMMENT=Share 1
            - SHARE1_WRITE_LIST=@users
            - SHARE1_BROWSEABLE=yes
            - SHARE1_READ_ONLY=yes

            - SHARE2_NAME=share2
            - SHARE2_PATH=/shares/share2
            - SHARE2_COMMENT=Share 2
            - SHARE2_WRITE_LIST=@users
            - SHARE2_BROWSEABLE=yes
            - SHARE2_READ_ONLY=yes

    wsdd2:
        image: ghcr.io/antrv/wsdd2
        container_name: wsdd2
        restart: unless-stopped
        network_mode: host
        environment:
            - MACHINENAME=FILES
            - WORKGROUP=HOME
</code></pre>
<p>Save this as <code>docker-compose.yaml</code>, adjust the values for your environment, and bring it up:</p>
<pre><code>docker compose up -d
</code></pre>
<hr />
<h2>Walking Through the Configuration</h2>
<h3>Host networking</h3>
<p>Both containers use <code>network_mode: host</code>. This is not optional for Samba. SMB relies on NetBIOS name resolution and multicast discovery protocols that do not work correctly through Docker's default NAT bridge. With host networking the container shares the host's network stack directly, so Windows clients see it as a regular machine on the LAN.</p>
<h3>The samba container</h3>
<p><strong>Server identity</strong></p>
<pre><code>MACHINENAME=FILES        # NetBIOS name — how Windows sees the machine
MACHINETITLE=File Server # Description shown in network browser
WORKGROUP=HOME           # Must match your Windows workgroup
</code></pre>
<p>Set <code>MACHINENAME</code> to whatever you want the server to appear as in <code>\\FILES\share1</code> style paths. Keep it short, no spaces.</p>
<p><strong>Users</strong></p>
<pre><code>USER1_NAME=username
USER1_UID=1000
USER1_PASSWORD=password
</code></pre>
<p>The container creates a Linux user with this UID and registers it with Samba's own password database. The UID matters when the share directory on the host is owned by a specific user — matching UIDs avoids permission problems without <code>chmod 777</code> hacks.</p>
<p>You can add as many users as you need by incrementing the number: <code>USER2_NAME</code>, <code>USER2_PASSWORD</code>, and so on.</p>
<p><strong>Keeping passwords out of the compose file</strong></p>
<p>Putting passwords in environment variables is convenient but means they appear in <code>docker inspect</code> output and in your compose file committed to git. The image supports a file-based alternative:</p>
<pre><code># USER1_PASSWORD_FILE=/run/secrets/user1_password
</code></pre>
<p>With Docker secrets or a bind-mounted file, the container reads the password from the file instead. For a home server the inline password is fine; for anything more sensitive, use the file approach.</p>
<p><strong>Shares</strong></p>
<pre><code>SHARE1_NAME=share1          # Share name — appears as \\FILES\share1
SHARE1_PATH=/shares/share1  # Path inside the container
SHARE1_COMMENT=Share 1      # Description shown to clients
SHARE1_BROWSEABLE=yes       # Visible when browsing the network
SHARE1_READ_ONLY=yes        # yes = read-only for everyone by default
SHARE1_WRITE_LIST=@users    # Who gets write access
</code></pre>
<p><code>SHARE1_READ_ONLY=yes</code> combined with <code>SHARE1_WRITE_LIST=@users</code> means the share is read-only by default but any user in the <code>users</code> group can write. This is a safe default — only authenticated users can make changes. To make a share fully read-only for everyone, set <code>WRITE_LIST</code> to an empty value or omit it.</p>
<p>Add more shares by incrementing the number: <code>SHARE2_NAME</code>, <code>SHARE2_PATH</code>, and so on up to however many you need.</p>
<p><strong>Volumes</strong></p>
<pre><code>- /etc/localtime:/etc/localtime:ro   # Keep container clock in sync with host
- /host/share1:/shares/share1        # Mount host directory into the container
</code></pre>
<p>Replace <code>/host/share1</code> with the actual path on your Linux host. The right side (<code>/shares/share1</code>) is what you set in <code>SHARE{N}_PATH</code>.</p>
<h3>The wsdd2 container</h3>
<p>WS-Discovery is the protocol Windows 10 and later uses to find machines in the network neighborhood. Without it, your Samba server works fine — Windows clients can connect by typing <code>\\FILES</code> directly — but it will not show up automatically when browsing the network.</p>
<p>The <code>wsdd2</code> container handles this. Give it the same <code>MACHINENAME</code> and <code>WORKGROUP</code> as the samba container and it takes care of the rest.</p>
<hr />
<h2>Connecting from Windows</h2>
<p>Once the containers are running, open File Explorer on a Windows machine on the same network. Within a minute or two the server should appear under <strong>Network</strong> with the name you set in <code>MACHINENAME</code>.</p>
<p>You can also connect directly:</p>
<ol>
<li>Press <code>Win + R</code></li>
<li>Type <code>\\FILES</code> (replace with your <code>MACHINENAME</code>)</li>
<li>Press Enter</li>
</ol>
<p>Windows will prompt for credentials. Enter the username and password you configured with <code>USER1_NAME</code> and <code>USER1_PASSWORD</code>.</p>
<p>To map a share as a persistent drive letter, right-click on the share in File Explorer and choose <strong>Map network drive</strong>.</p>
<hr />
<h2>Security Notes</h2>
<p>The image enforces SMB2 as the minimum protocol version — SMB1 is disabled entirely. SMB1 has known critical vulnerabilities (it is what WannaCry exploited) and has no place on a modern network.</p>
<p>All access is authenticated. There is no guest access. The <code>@users</code> group in <code>WRITE_LIST</code> refers to Samba users you explicitly created — not arbitrary network users.</p>
<p>For a home LAN this setup is reasonable. If the server is accessible from outside your network, put it behind a VPN rather than exposing port 445 directly to the internet.</p>
<hr />
<h2>Adding More Users and Shares</h2>
<p>The numbering scheme scales linearly. To add a second user and a third share:</p>
<pre><code>USER2_NAME=anotheruser
USER2_PASSWORD=anotherpassword

SHARE3_NAME=media
SHARE3_PATH=/shares/media
SHARE3_COMMENT=Media Library
SHARE3_READ_ONLY=yes
</code></pre>
<p>Restart the container after changing environment variables:</p>
<pre><code>docker compose up -d --force-recreate samba
</code></pre>
<hr />
<h2>Conclusion</h2>
<p>Two containers, a handful of environment variables, and your Linux server is a fully functional SMB file server that Windows machines find automatically on the network. No config file editing, no system-level user management to undo later, and the whole thing lives in a single compose file you can version-control.</p>
<p>The images are available on GitHub Container Registry:</p>
<ul>
<li><code>ghcr.io/antrv/samba</code></li>
<li><code>ghcr.io/antrv/wsdd2</code></li>
</ul>
<p>Source and the example compose file are at <a href="https://github.com/antrv/docker-images">github.com/antrv/docker-images</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack]]></title><description><![CDATA[In the previous chapters, we treated our TypePack primarily as a linear sequence—a list that can be sliced, grown, and flattened. However, in many metaprogramming scenarios, a parameter pack acts more like a Set.
You might need to know if a specific ...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[type-pack]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Thu, 29 Jan 2026 10:00:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767262188817/912b3e93-17e7-4166-9b5a-37226869f90f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous chapters, we treated our <code>TypePack</code> primarily as a linear sequence—a list that can be sliced, grown, and flattened. However, in many metaprogramming scenarios, a parameter pack acts more like a <strong>Set</strong>.</p>
<p>You might need to know <em>if</em> a specific interface exists in a collection, find the specific index of a type to access a corresponding value in a <code>std::tuple</code>, or strip away redundant duplicates to create a canonical list of types.</p>
<p>In this article, we will implement three crucial set-operations: <code>contains</code> (existence), <code>index_of</code> (lookup), and <code>unique</code> (deduplication).</p>
<h3 id="heading-1-existence-the-contains-check">1. Existence: The <code>contains</code> Check</h3>
<p>Checking if a type exists within a pack used to require complex recursive inheritance or <code>std::disjunction</code>. With C++17, this becomes a trivial one-liner using <strong>Fold Expressions</strong>.</p>
<p>We add a static constexpr boolean to our main structure. This allows users to write <code>Pack::contains&lt;int&gt;</code> effectively.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePack</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-comment">// ... previous code ...</span>

    <span class="hljs-comment">// C++17 Fold Expression to check for type existence</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
    <span class="hljs-title">static</span> <span class="hljs-title">constexpr</span> <span class="hljs-title">bool</span> <span class="hljs-title">contains</span> = (<span class="hljs-title">std</span>:</span>:is_same_v&lt;T, Ts&gt; || ...);

    <span class="hljs-comment">// ...</span>
};
</code></pre>
<h3 id="heading-2-location-the-indexof-operation">2. Location: The <code>index_of</code> Operation</h3>
<p>Knowing a type exists is often only half the battle. If you are building a storage system (like a Variant or a Tuple wrapper), you often need the numerical index of that type to access memory.</p>
<p>Since parameter packs cannot be indexed at runtime, we perform a compile-time linear search. We recurse through the pack:</p>
<ol>
<li><p><strong>Found:</strong> Return 0.</p>
</li>
<li><p><strong>Not Found:</strong> Return the result of the recursion + 1.</p>
</li>
<li><p><strong>End of Pack:</strong> Return a sentinel value (max <code>size_t</code>) to indicate failure.</p>
</li>
</ol>
<p>We add this as a static member to <code>TypePack</code>:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePack</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
    <span class="hljs-title">static</span> <span class="hljs-title">constexpr</span> <span class="hljs-title">size_t</span> <span class="hljs-title">index_of</span> = <span class="hljs-title">details</span>:</span>:TypePackIndexOf&lt;T, TypePack&gt;::value;
};
</code></pre>
<p>And the implementation logic:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackIndexOf</span>;</span>

<span class="hljs-comment">// Base Case: Pack is empty, type not found. Return MAX_SIZE_T.</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackIndexOf</span>&lt;T, TypePack&lt;&gt;&gt; :</span> 
    <span class="hljs-built_in">std</span>::integral_constant&lt;<span class="hljs-keyword">size_t</span>, <span class="hljs-built_in">std</span>::numeric_limits&lt;<span class="hljs-keyword">size_t</span>&gt;::max()&gt; {};

<span class="hljs-comment">// Match Case: The head of the pack matches T. Index is 0.</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>... <span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackIndexOf</span>&lt;T, TypePack&lt;T, Ts...&gt;&gt; :</span> 
    <span class="hljs-built_in">std</span>::integral_constant&lt;<span class="hljs-keyword">size_t</span>, <span class="hljs-number">0</span>&gt; {};

<span class="hljs-comment">// Recursive Step: Head does not match. Check the tail.</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">TFirst</span>, <span class="hljs-title">class</span>... <span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackIndexOf</span>&lt;T, TypePack&lt;TFirst, Ts...&gt;&gt; :</span>
    <span class="hljs-built_in">std</span>::integral_constant&lt;<span class="hljs-keyword">size_t</span>,
        TypePackIndexOf&lt;T, TypePack&lt;Ts...&gt;&gt;::value == <span class="hljs-built_in">std</span>::numeric_limits&lt;<span class="hljs-keyword">size_t</span>&gt;::max() 
            ? <span class="hljs-built_in">std</span>::numeric_limits&lt;<span class="hljs-keyword">size_t</span>&gt;::max() 
            : TypePackIndexOf&lt;T, TypePack&lt;Ts...&gt;&gt;::value + <span class="hljs-number">1</span>
    &gt; {};
</code></pre>
<h3 id="heading-3-uniqueness-the-unique-transformation">3. Uniqueness: The <code>unique</code> Transformation</h3>
<p>When automatically generating types (e.g., deducing return types from a set of functions), you often end up with duplicates. To resolve ambiguity, you need to reduce the pack to a set of unique types.</p>
<p>This implementation relies on the <code>TypePackRemove</code> utility we built in <strong>Part 6</strong>. The strategy is "Filter and Conserve":</p>
<ol>
<li><p>Take the first type <code>T</code>.</p>
</li>
<li><p><strong>Remove all occurrences</strong> of <code>T</code> from the <em>rest</em> of the pack.</p>
</li>
<li><p>Recurse on the now-filtered tail.</p>
</li>
<li><p>Prepend <code>T</code> back to the result.</p>
</li>
</ol>
<p>This ensures order is preserved while duplicates are eliminated.</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// Public Alias</span>
<span class="hljs-keyword">template</span> &lt;IsSpecializationOf&lt;TypePack&gt; Pack&gt;
<span class="hljs-keyword">using</span> <span class="hljs-keyword">type_pack_unique_t</span> = details::TypePackUnique&lt;Pack&gt;::type;

<span class="hljs-comment">// Implementation</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackUnique</span>;</span>

<span class="hljs-comment">// Base Case: Empty pack is already unique</span>
<span class="hljs-keyword">template</span> &lt;&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackUnique</span>&lt;TypePack&lt;&gt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;&gt;&gt; {};

<span class="hljs-comment">// Recursive Step</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>... <span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackUnique</span>&lt;TypePack&lt;T, Ts...&gt;&gt; :</span>
    TypePackInsertAtFirstPosition&lt;
        T, 
        <span class="hljs-comment">// 1. Remove T from the tail (Ts...)</span>
        <span class="hljs-comment">// 2. Compute Unique on that filtered tail</span>
        <span class="hljs-keyword">typename</span> TypePackUnique&lt;
            <span class="hljs-keyword">typename</span> TypePackRemove&lt;T, TypePack&lt;Ts...&gt;&gt;::type
        &gt;::type
    &gt; {};
</code></pre>
<h3 id="heading-validation">Validation</h3>
<p>We verify our set operations with a suite of tests covering existence, valid/invalid indices, and duplicate removal.</p>
<p>C++</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// 1. Testing Contains</span>
TEST(TypePackTests, Contains)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(Pack::contains&lt;<span class="hljs-keyword">int</span>&gt;);
    <span class="hljs-keyword">static_assert</span>(Pack::contains&lt;<span class="hljs-keyword">double</span>&gt;);
    <span class="hljs-keyword">static_assert</span>(!Pack::contains&lt;<span class="hljs-keyword">char8_t</span>&gt;); <span class="hljs-comment">// Not in pack</span>

    <span class="hljs-comment">// Edge case: Empty pack</span>
    <span class="hljs-keyword">static_assert</span>(!TypePack&lt;&gt;::contains&lt;<span class="hljs-keyword">int</span>&gt;);
}

<span class="hljs-comment">// 2. Testing IndexOf</span>
TEST(TypePackTests, IndexOf)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;

    <span class="hljs-keyword">static_assert</span>(Pack::index_of&lt;<span class="hljs-keyword">int</span>&gt; == <span class="hljs-number">0</span>);
    <span class="hljs-keyword">static_assert</span>(Pack::index_of&lt;<span class="hljs-keyword">double</span>&gt; == <span class="hljs-number">2</span>);
    <span class="hljs-keyword">static_assert</span>(Pack::index_of&lt;<span class="hljs-keyword">char</span>&gt; == <span class="hljs-number">3</span>);

    <span class="hljs-comment">// Not found returns MAX</span>
    <span class="hljs-keyword">static_assert</span>(Pack::index_of&lt;<span class="hljs-keyword">char8_t</span>&gt; == <span class="hljs-built_in">std</span>::numeric_limits&lt;<span class="hljs-keyword">size_t</span>&gt;::max());
    <span class="hljs-keyword">static_assert</span>(TypePack&lt;&gt;::index_of&lt;<span class="hljs-keyword">int</span>&gt; == <span class="hljs-built_in">std</span>::numeric_limits&lt;<span class="hljs-keyword">size_t</span>&gt;::max());
}

<span class="hljs-comment">// 3. Testing Unique</span>
TEST(TypePackTests, Unique)
{
    <span class="hljs-comment">// Input contains duplicates of int, long, and char</span>
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">char</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">char</span>, <span class="hljs-keyword">char8_t</span>&gt;;
    <span class="hljs-keyword">using</span> Expected = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">char</span>, <span class="hljs-keyword">char8_t</span>&gt;;

    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;<span class="hljs-keyword">type_pack_unique_t</span>&lt;Pack&gt;, Expected&gt;);

    <span class="hljs-comment">// Already unique packs remain unchanged</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;<span class="hljs-keyword">type_pack_unique_t</span>&lt;TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;<span class="hljs-keyword">type_pack_unique_t</span>&lt;TypePack&lt;&gt;&gt;, TypePack&lt;&gt;&gt;);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>With the addition of <code>contains</code>, <code>index_of</code>, and <code>unique</code>, our <code>TypePack</code> has evolved into a fully functional compile-time container. We can now treat variadic templates not just as a list of arguments, but as a searchable, indexable, and distinct set of types.</p>
<p>You can explore the full source code for the library on GitHub, including the <a target="_blank" href="https://github.com/antrv/my-programming-language/blob/main/skarnc/src/TypePack.h">implementation details</a> and the <a target="_blank" href="https://github.com/antrv/my-programming-language/blob/main/skarnc/test/TypePackTests.cpp">comprehensive test suite</a>.</p>
<p>This concludes our deep dive into the core implementation of <code>TypePack</code>. By combining structural manipulation (Concat, Slice) with introspective logic (Find, Unique), we have built a library capable of handling complex C++ metaprogramming tasks with clean, readable syntax.</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack (Part 7 of 8)]]></title><description><![CDATA[As we build more complex template systems, we often find ourselves with "packs within packs." This usually happens when merging multiple results from different metafunctions or dealing with recursive type generators. While a nested structure has its ...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-7-of-8</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-7-of-8</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[type-pack]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Sun, 25 Jan 2026 22:00:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767201819186/b3ca7059-9d98-4b9e-a9a1-207abf594cb2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As we build more complex template systems, we often find ourselves with "packs within packs." This usually happens when merging multiple results from different metafunctions or dealing with recursive type generators. While a nested structure has its uses, many operations require a simple, flat list of types.</p>
<p>In this article, we will implement <code>type_pack_flatten_t</code>, a powerful utility that recursively collapses any level of nesting into a single, linear <code>TypePack</code>.</p>
<h3 id="heading-the-goal-structural-simplification">The Goal: Structural Simplification</h3>
<p>The objective is to take a complex, multidimensional type structure and normalize it. For example:</p>
<ul>
<li><p><strong>Input:</strong> <code>TypePack&lt;int, TypePack&lt;char, TypePack&lt;long&gt;&gt;, double&gt;</code></p>
</li>
<li><p><strong>Output:</strong> <code>TypePack&lt;int, char, long, double&gt;</code></p>
</li>
</ul>
<p>Unlike our previous operations, this utility is implemented as a standalone alias rather than a member of the <code>TypePack</code> struct, allowing it to act as a transformation gateway for any pack specialization.</p>
<h3 id="heading-the-implementation-recursive-concatenation">The Implementation: Recursive Concatenation</h3>
<p>The beauty of the <code>flatten</code> operation lies in its simplicity when paired with the <code>TypePackConcat</code> utility we built in Part 4.</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// Public API</span>
<span class="hljs-keyword">template</span> &lt;IsSpecializationOf&lt;TypePack&gt; Pack&gt;
<span class="hljs-keyword">using</span> <span class="hljs-keyword">type_pack_flatten_t</span> = details::TypePackFlatten&lt;Pack&gt;::type;

<span class="hljs-comment">// 1. Base Case: If the element is not a TypePack, wrap it in a TypePack to make it "concat-compatible"</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackFlatten</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;T&gt;&gt; {
};

<span class="hljs-comment">// 2. Recursive Step: If it is a TypePack, flatten its contents and concatenate the results</span>
<span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackFlatten</span>&lt;TypePack&lt;Ts...&gt;&gt; :</span> 
    details::TypePackConcat&lt;<span class="hljs-keyword">typename</span> TypePackFlatten&lt;Ts&gt;::type...&gt; {
};
</code></pre>
<h4 id="heading-how-it-works">How it works:</h4>
<ol>
<li><p><strong>Uniformity</strong>: Every element encountered is turned into a <code>TypePack</code>. A simple <code>int</code> becomes <code>TypePack&lt;int&gt;</code>.</p>
</li>
<li><p><strong>Expansion</strong>: The specialization for <code>TypePack&lt;Ts...&gt;</code> uses the pack expansion <code>Ts...</code> to pass every internal element into <code>TypePackFlatten</code> recursively.</p>
</li>
<li><p><strong>Merge</strong>: <code>TypePackConcat</code> takes these results and joins them. Because <code>TypePackConcat</code> is designed to handle multiple packs, it effortlessly merges the flattened "sub-packs" into one.</p>
</li>
</ol>
<h3 id="heading-validation-and-edge-cases">Validation and Edge Cases</h3>
<p>The robustness of this implementation is evident when handling empty packs or deeply nested structures. We use <code>static_assert</code> to verify that the hierarchy is correctly collapsed regardless of depth.</p>
<pre><code class="lang-cpp">TEST(TypePackTests, Flatten1)
{
    <span class="hljs-keyword">using</span> Pack1 = TypePack&lt;TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;<span class="hljs-keyword">type_pack_flatten_t</span>&lt;Pack1&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);

    <span class="hljs-keyword">using</span> Pack2 = TypePack&lt;TypePack&lt;TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;<span class="hljs-keyword">type_pack_flatten_t</span>&lt;Pack2&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);
}

TEST(TypePackTests, Flatten)
{
    <span class="hljs-comment">// A complex mix of types, empty packs, and deep nesting</span>
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;
        <span class="hljs-keyword">short</span>, 
        TypePack&lt;&gt;, 
        TypePack&lt;TypePack&lt;<span class="hljs-keyword">int</span>, TypePack&lt;TypePack&lt;<span class="hljs-keyword">long</span>&gt;&gt;&gt;, TypePack&lt;<span class="hljs-keyword">double</span>&gt;&gt;, 
        TypePack&lt;<span class="hljs-keyword">char8_t</span>, <span class="hljs-keyword">char32_t</span>&gt;, 
        <span class="hljs-keyword">char</span>
    &gt;;

    <span class="hljs-keyword">using</span> Expected = TypePack&lt;<span class="hljs-keyword">short</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char8_t</span>, <span class="hljs-keyword">char32_t</span>, <span class="hljs-keyword">char</span>&gt;;

    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;<span class="hljs-keyword">type_pack_flatten_t</span>&lt;Pack&gt;, Expected&gt;);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Flattening is the "reset button" for type containers. It allows us to perform complex transformations that might result in nesting, knowing that we can always return to a clean, flat state. By leveraging <code>TypePackConcat</code>, we’ve turned a difficult recursive problem into a concise and elegant template specialization.</p>
<p>In the next part, we will explore <strong>Set-like operations</strong>: how to check for a type's existence, find its index, and ensure all types in a pack are unique.</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack (Part 6 of 8)]]></title><description><![CDATA[In the previous chapters, we focused on "positional" operations: doing things based on indices and ranges. However, in real-world metaprogramming, we often care about what a type is, not just where it is. For example, you might want to strip all void...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-6-of-8</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-6-of-8</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[type-pack]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Wed, 21 Jan 2026 22:00:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767201772215/22225548-1a4b-4084-98d0-2929e1b095f4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous chapters, we focused on "positional" operations: doing things based on indices and ranges. However, in real-world metaprogramming, we often care about <strong>what</strong> a type is, not just <strong>where</strong> it is. For example, you might want to strip all <code>void</code> types from a pack or replace <code>float</code> with <code>double</code> for higher precision across a whole interface.</p>
<p>In this article, we will implement type-based filtering and transformation using <code>remove_t</code> and <code>replace_t</code>.</p>
<h3 id="heading-advancing-the-interface">Advancing the Interface</h3>
<p>We add two more aliases to our <code>TypePack</code>. These allow us to perform "search-and-destroy" or "search-and-replace" operations across the entire collection of types.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePack</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-comment">// ... previous definitions ...</span>

    <span class="hljs-comment">// Remove all occurrences of type T</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
    <span class="hljs-title">using</span> <span class="hljs-title">remove_t</span> = <span class="hljs-title">details</span>:</span>:TypePackRemove&lt;T, TypePack&gt;::type;

    <span class="hljs-comment">// Replace all occurrences of T with TReplacement</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">TReplacement</span>&gt;
    <span class="hljs-title">using</span> <span class="hljs-title">replace_t</span> = <span class="hljs-title">details</span>:</span>:TypePackReplace&lt;T, TReplacement, TypePack&gt;::type;
};
</code></pre>
<h3 id="heading-type-based-removal">Type-Based Removal</h3>
<p>The <code>TypePackRemove</code> utility uses recursive pattern matching to scan the pack. Unlike positional removal, this operation visits every element and decides whether to keep it or discard it.</p>
<h4 id="heading-the-logic">The Logic:</h4>
<ol>
<li><p><strong>Base Case</strong>: Removing from an empty pack results in an empty pack.</p>
</li>
<li><p><strong>Match Case</strong>: If the head of the pack matches the target type <code>T</code>, we discard it and continue with the rest of the pack.</p>
</li>
<li><p><strong>No-Match Case</strong>: If the head doesn't match, we keep it (using <code>TypePackInsertAtFirstPosition</code>) and recurse into the tail.</p>
</li>
</ol>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackRemove</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackRemove</span>&lt;T, TypePack&lt;&gt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;&gt;&gt; {
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackRemove</span>&lt;T, TypePack&lt;T, Ts...&gt;&gt; :</span> TypePackRemove&lt;T, TypePack&lt;Ts...&gt;&gt; {
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">TFirst</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackRemove</span>&lt;T, TypePack&lt;TFirst, Ts...&gt;&gt; :</span>
    TypePackInsertAtFirstPosition&lt;TFirst, <span class="hljs-keyword">typename</span> TypePackRemove&lt;T, TypePack&lt;Ts...&gt;&gt;::type&gt; {
};
</code></pre>
<h3 id="heading-universal-replacement">Universal Replacement</h3>
<p><code>TypePackReplace</code> follows a similar recursive structure but with a twist: instead of discarding a matching type, we swap it for a new one.</p>
<h4 id="heading-the-logic-1">The Logic:</h4>
<ul>
<li><p>If <code>Head == Target</code>, the new pack starts with <code>Replacement</code>.</p>
</li>
<li><p>If <code>Head != Target</code>, the new pack starts with the original <code>Head</code>.</p>
</li>
<li><p>Repeat until the pack is empty.</p>
</li>
</ul>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">TReplacement</span>, <span class="hljs-title">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackReplace</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">TReplacement</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackReplace</span>&lt;T, TReplacement, TypePack&lt;&gt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;&gt;&gt; {
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">TReplacement</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackReplace</span>&lt;T, TReplacement, TypePack&lt;T, Ts...&gt;&gt; :</span>
    TypePackInsertAtFirstPosition&lt;TReplacement, <span class="hljs-keyword">typename</span> TypePackReplace&lt;T, TReplacement, TypePack&lt;Ts...&gt;&gt;::type&gt; {
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">TReplacement</span>, <span class="hljs-title">class</span> <span class="hljs-title">TFirst</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackReplace</span>&lt;T, TReplacement, TypePack&lt;TFirst, Ts...&gt;&gt; :</span>
    TypePackInsertAtFirstPosition&lt;TFirst, <span class="hljs-keyword">typename</span> TypePackReplace&lt;T, TReplacement, TypePack&lt;Ts...&gt;&gt;::type&gt; {
};
</code></pre>
<h3 id="heading-validation-with-staticassert">Validation with <code>static_assert</code></h3>
<p>Our tests demonstrate that these operations work globally—affecting multiple occurrences of the same type throughout the pack.</p>
<pre><code class="lang-cpp">TEST(TypePackTests, RemoveType)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">int</span>&gt;;

    <span class="hljs-comment">// Removes ALL 'int' instances</span>
    <span class="hljs-keyword">using</span> Result = Pack::<span class="hljs-keyword">remove_t</span>&lt;<span class="hljs-keyword">int</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Result, TypePack&lt;<span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>&gt;&gt;);

    <span class="hljs-comment">// If the type isn't there, the pack remains unchanged</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">remove_t</span>&lt;<span class="hljs-keyword">char</span>&gt;, Pack&gt;);
}

TEST(TypePackTests, ReplaceType)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">int</span>&gt;;

    <span class="hljs-comment">// Replaces ALL 'int' with 'unsigned'</span>
    <span class="hljs-keyword">using</span> Result = Pack::<span class="hljs-keyword">replace_t</span>&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">unsigned</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Result, TypePack&lt;<span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">unsigned</span>&gt;&gt;);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>By moving from positional to type-based manipulation, we've given our <code>TypePack</code> the ability to "understand" its contents. These recursive patterns are the engine behind powerful library features, such as filtering out unsupported types or automatically upgrading types in a template-heavy API.</p>
<p>In the next part, we will look at how to handle nested structures by implementing an operation to <strong>flatten</strong> a pack</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack (Part 5 of 8)]]></title><description><![CDATA[Part 5: Refining the Pack — Deletion and Range Removal
In the previous chapters, we focused on building and expanding our TypePack. However, effective type manipulation often requires surgical precision in removing unwanted elements. Whether you're s...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-5-of-8</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-5-of-8</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[type-pack]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Mon, 19 Jan 2026 00:00:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767201759602/3a72a4ae-58d0-431a-a2f0-a2b20cd51ddb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-part-5-refining-the-pack-deletion-and-range-removal">Part 5: Refining the Pack — Deletion and Range Removal</h3>
<p>In the previous chapters, we focused on building and expanding our <code>TypePack</code>. However, effective type manipulation often requires surgical precision in removing unwanted elements. Whether you're stripping specific traits or trimming a pack to fit a function signature, you need a reliable way to "cut" types out.</p>
<p>In this article, we will implement <code>remove_at_t</code> and the more general <code>remove_range_t</code>.</p>
<h3 id="heading-streamlining-the-interface">Streamlining the Interface</h3>
<p>By now, you might notice a pattern: our complex operations are built by composing simpler ones. We add two new aliases to <code>TypePack</code> that leverage a unified removal logic.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePack</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-comment">// ... previous definitions ...</span>

    <span class="hljs-comment">// Remove a single type at Index</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index&gt;
    <span class="hljs-keyword">using</span> <span class="hljs-keyword">remove_at_t</span> = details::TypePackRemoveRange&lt;Index, <span class="hljs-number">1</span>, TypePack&gt;::type;

    <span class="hljs-comment">// Remove a chunk of 'Size' types starting at Index</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size&gt;
    <span class="hljs-keyword">using</span> <span class="hljs-keyword">remove_range_t</span> = details::TypePackRemoveRange&lt;Index, Size, TypePack&gt;::type;
};
</code></pre>
<h3 id="heading-the-surgical-logic-typepackremoverange">The Surgical Logic: <code>TypePackRemoveRange</code></h3>
<p>The logic for removing a range is the conceptual inverse of the "Subpack" operation we wrote in Part 3. To remove a range, we don't keep the middle; we keep the <strong>ends</strong>.</p>
<h4 id="heading-how-it-works">How it works:</h4>
<ol>
<li><p><strong>Prefix</strong>: We take the first <code>Index</code> elements.</p>
</li>
<li><p><strong>Suffix</strong>: We skip everything from the start up to the end of the unwanted range (<code>Index + Size</code>).</p>
</li>
<li><p><strong>Join</strong>: We concatenate the prefix and the suffix, effectively "stitching" the pack back together over the hole.</p>
</li>
<li><p>We use <code>requires</code> clauses and <code>static_assert</code> to ensure that we never attempt to remove elements outside the pack's boundaries.</p>
</li>
</ol>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackRemoveRange</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size, class...Ts&gt;
<span class="hljs-keyword">requires</span> (Index &gt; <span class="hljs-keyword">sizeof</span>...(Ts) || Size &gt; <span class="hljs-keyword">sizeof</span>...(Ts) || Index + Size &gt; <span class="hljs-keyword">sizeof</span>...(Ts))
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackRemoveRange</span>&lt;Index, Size, TypePack&lt;Ts...&gt;&gt; {</span>
    <span class="hljs-keyword">static_assert</span>(Index &lt;= <span class="hljs-keyword">sizeof</span>...(Ts), <span class="hljs-string">"Index out of range"</span>);
    <span class="hljs-keyword">static_assert</span>(Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts), <span class="hljs-string">"Size out of range"</span>);
    <span class="hljs-keyword">static_assert</span>(Index + Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts), <span class="hljs-string">"Index + Size out of range"</span>);
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size, class...Ts&gt;
<span class="hljs-keyword">requires</span> (Index &lt;= <span class="hljs-keyword">sizeof</span>...(Ts) &amp;&amp; Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts) &amp;&amp; Index + Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts))
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackRemoveRange</span>&lt;Index, Size, TypePack&lt;Ts...&gt;&gt; :</span>
    TypePackConcat&lt;<span class="hljs-keyword">typename</span> TypePackFirstN&lt;Index, TypePack&lt;Ts...&gt;&gt;::type, <span class="hljs-keyword">typename</span> TypePackSkipN&lt;Index + Size, TypePack&lt;Ts...&gt;&gt;::type&gt; {
};
</code></pre>
<h3 id="heading-validation-with-staticassert">Validation with <code>static_assert</code></h3>
<p>Our tests cover both the removal of individual elements and various range sizes, ensuring the "stitching" logic is perfect.</p>
<pre><code class="lang-cpp">TEST(TypePackTests, RemoveAt)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;

    <span class="hljs-comment">// Removing the first element</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">remove_at_t</span>&lt;<span class="hljs-number">0</span>&gt;, TypePack&lt;<span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);

    <span class="hljs-comment">// Removing a middle element</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">remove_at_t</span>&lt;<span class="hljs-number">2</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
}

TEST(TypePackTests, RemoveRange)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;

    <span class="hljs-comment">// Removing a range of 0 elements leaves the pack unchanged</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">remove_range_t</span>&lt;<span class="hljs-number">1</span>, <span class="hljs-number">0</span>&gt;, Pack&gt;);

    <span class="hljs-comment">// Removing a range in the middle</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">remove_range_t</span>&lt;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">char</span>&gt;&gt;);

    <span class="hljs-comment">// Removing everything</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">remove_range_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">4</span>&gt;, TypePack&lt;&gt;&gt;);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>We have now completed the structural toolkit for our <code>TypePack</code>. We can create, measure, index, slice, grow, and shrink our type containers. This composition-based approach—where <code>RemoveRange</code> is simply a combination of <code>FirstN</code>, <code>SkipN</code>, and <code>Concat</code>—demonstrates the power of building a clean foundation.</p>
<p>In the next part, we will move beyond indices and ranges to work with types directly: we will learn how to remove all occurrences of a specific type and how to replace one type with another across the entire pack.</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack (Part 4 of 8)]]></title><description><![CDATA[In the previous parts, we learned how to encapsulate a variadic pack and how to slice it into smaller pieces. However, a truly flexible meta-container must also be able to grow. In this article, we will implement the ability to merge multiple TypePac...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-4-of-8</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-4-of-8</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[type-pack]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Thu, 15 Jan 2026 10:00:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767201748151/3f795a81-db82-4bd4-a891-b9c916e9233a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous parts, we learned how to encapsulate a variadic pack and how to slice it into smaller pieces. However, a truly flexible meta-container must also be able to grow. In this article, we will implement the ability to merge multiple <code>TypePack</code> instances and insert new types at any arbitrary position.</p>
<h3 id="heading-expanding-the-interface">Expanding the Interface</h3>
<p>We introduce two powerful aliases to the <code>TypePack</code> struct: <code>concat_t</code> for merging and <code>insert_at_t</code> for precise placement.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePack</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-comment">// ... previous definitions (size, element_t, etc.) ...</span>

    <span class="hljs-comment">// Merge this pack with any number of other TypePacks</span>
    <span class="hljs-keyword">template</span> &lt;class... Packs&gt;
    <span class="hljs-keyword">using</span> <span class="hljs-keyword">concat_t</span> = details::TypePackConcat&lt;TypePack, Packs...&gt;::type;

    <span class="hljs-comment">// Insert a single type T at a specific Index</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
    <span class="hljs-title">using</span> <span class="hljs-title">insert_at_t</span> = <span class="hljs-title">details</span>:</span>:TypePackInsertAt&lt;T, Index, TypePack&gt;::type; 
};
</code></pre>
<h3 id="heading-merging-packs-the-typepackconcat-logic">Merging Packs: The <code>TypePackConcat</code> Logic</h3>
<p>To ensure type safety, we use the <code>IsSpecializationOf</code> concept. This prevents the user from accidentally trying to concatenate a <code>TypePack</code> with a <code>std::tuple</code> or other unrelated types.</p>
<h4 id="heading-the-implementation">The Implementation</h4>
<p>Concatenation is achieved by recursively expanding the internal packs and folding them into a single <code>TypePack</code>.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;IsSpecializationOf&lt;TypePack&gt;...Packs&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackConcat</span>;</span>

<span class="hljs-keyword">template</span> &lt;&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackConcat</span>&lt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;&gt;&gt; {
};

<span class="hljs-keyword">template</span> &lt;class...Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackConcat</span>&lt;TypePack&lt;Ts...&gt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
};

<span class="hljs-keyword">template</span> &lt;class...Ts, class...Ts2, IsSpecializationOf&lt;TypePack&gt;...Packs&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackConcat</span>&lt;TypePack&lt;Ts...&gt;, TypePack&lt;Ts2...&gt;, Packs...&gt; :</span>
    TypePackConcat&lt;TypePack&lt;Ts..., Ts2...&gt;, Packs...&gt; {
};
</code></pre>
<p>This recursive approach allows us to pass an unlimited number of packs to <code>concat_t</code>, flattening them all into one flat structure.</p>
<h3 id="heading-precise-insertion-the-typepackinsertat-logic">Precise Insertion: The <code>TypePackInsertAt</code> Logic</h3>
<p>Inserting an element at an arbitrary index is a perfect demonstration of the "Slicing" tools we built in Part 3. Instead of writing a complex new recursion, we simply:</p>
<ol>
<li><p><strong>Slice</strong> the pack into two halves (before and after the index).</p>
</li>
<li><p><strong>Concatenate</strong> the first half, the new type, and the second half.</p>
</li>
</ol>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">size_t</span> <span class="hljs-title">Index</span>, <span class="hljs-title">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackInsertAt</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">size_t</span> <span class="hljs-title">Index</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">requires</span> (<span class="hljs-title">Index</span> &gt; <span class="hljs-title">sizeof</span>...(<span class="hljs-title">Ts</span>))
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackInsertAt</span>&lt;T, Index, TypePack&lt;Ts...&gt;&gt; {</span>
    <span class="hljs-keyword">static_assert</span>(AlwaysFalse&lt;<span class="hljs-built_in">std</span>::integral_constant&lt;<span class="hljs-keyword">size_t</span>, Index&gt;&gt;, <span class="hljs-string">"Index out of range"</span>);
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">size_t</span> <span class="hljs-title">Index</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">requires</span> (<span class="hljs-title">Index</span> &lt;= sizeof...(Ts))
struct TypePackInsertAt&lt;T, Index, TypePack&lt;Ts...&gt;&gt; :
    TypePackConcat&lt;
        typename TypePackFirstN&lt;Index, TypePack&lt;Ts...&gt;&gt;::type, 
        TypePack&lt;T&gt;, 
        typename TypePackSkipN&lt;Index, TypePack&lt;Ts...&gt;&gt;::type&gt; {
};</span>
</code></pre>
<p>This "Lego-block" approach to metaprogramming makes the code significantly easier to maintain and reason about.</p>
<h3 id="heading-validation-with-staticassert">Validation with <code>static_assert</code></h3>
<p>Our tests verify that concatenation handles empty packs correctly and that insertion works at the boundaries (index 0 and index <code>size</code>).</p>
<pre><code class="lang-cpp">TEST(TypePackTests, Concat)
{
    <span class="hljs-keyword">using</span> Pack0 = TypePack&lt;&gt;;
    <span class="hljs-keyword">using</span> Pack1 = TypePack&lt;<span class="hljs-keyword">float</span>&gt;;
    <span class="hljs-keyword">using</span> Pack2 = TypePack&lt;<span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>&gt;;
    <span class="hljs-keyword">using</span> Pack4 = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack0::<span class="hljs-keyword">concat_t</span>&lt;Pack0&gt;, TypePack&lt;&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack0::<span class="hljs-keyword">concat_t</span>&lt;Pack1&gt;, TypePack&lt;<span class="hljs-keyword">float</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack0::<span class="hljs-keyword">concat_t</span>&lt;Pack2&gt;, TypePack&lt;<span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack0::<span class="hljs-keyword">concat_t</span>&lt;Pack4&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack1::<span class="hljs-keyword">concat_t</span>&lt;Pack0&gt;, TypePack&lt;<span class="hljs-keyword">float</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack1::<span class="hljs-keyword">concat_t</span>&lt;Pack1&gt;, TypePack&lt;<span class="hljs-keyword">float</span>, <span class="hljs-keyword">float</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack1::<span class="hljs-keyword">concat_t</span>&lt;Pack2&gt;, TypePack&lt;<span class="hljs-keyword">float</span>, <span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack1::<span class="hljs-keyword">concat_t</span>&lt;Pack4&gt;, TypePack&lt;<span class="hljs-keyword">float</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack2::<span class="hljs-keyword">concat_t</span>&lt;Pack0&gt;, TypePack&lt;<span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack2::<span class="hljs-keyword">concat_t</span>&lt;Pack1&gt;, TypePack&lt;<span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>, <span class="hljs-keyword">float</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack2::<span class="hljs-keyword">concat_t</span>&lt;Pack2&gt;, TypePack&lt;<span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>, <span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack2::<span class="hljs-keyword">concat_t</span>&lt;Pack4&gt;, TypePack&lt;<span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack4::<span class="hljs-keyword">concat_t</span>&lt;Pack0&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack4::<span class="hljs-keyword">concat_t</span>&lt;Pack1&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>, <span class="hljs-keyword">float</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack4::<span class="hljs-keyword">concat_t</span>&lt;Pack2&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>, <span class="hljs-keyword">unsigned</span>, <span class="hljs-keyword">char8_t</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack4::<span class="hljs-keyword">concat_t</span>&lt;Pack4&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
}

TEST(TypePackTests, InsertAt)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">insert_at_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-keyword">float</span>&gt;, TypePack&lt;<span class="hljs-keyword">float</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">insert_at_t</span>&lt;<span class="hljs-number">1</span>, <span class="hljs-keyword">float</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">float</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">insert_at_t</span>&lt;<span class="hljs-number">2</span>, <span class="hljs-keyword">float</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">float</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">insert_at_t</span>&lt;<span class="hljs-number">3</span>, <span class="hljs-keyword">float</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">float</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">insert_at_t</span>&lt;<span class="hljs-number">4</span>, <span class="hljs-keyword">float</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>, <span class="hljs-keyword">float</span>&gt;&gt;);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>With the addition of concatenation and insertion, our <code>TypePack</code> is no longer a static snapshot of types. It is now a dynamic structure that can be merged, extended, and rebuilt at will. We have successfully moved from basic introspection to active structural transformation.</p>
<p>In the next part, we will explore how to shrink our containers: removing specific elements and deleting entire ranges from the pack.</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack (Part 3 of 8)]]></title><description><![CDATA[In the previous part, we learned how to measure a TypePack and pick a single element. However, when building complex template libraries, we often need more than just one type; we need to extract entire chunks of data. Whether you are stripping away m...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-3-of-8</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-3-of-8</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[type-pack]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Sun, 11 Jan 2026 22:00:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767201738818/e280f3eb-b981-40d8-b451-048afd944764.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous part, we learned how to measure a <code>TypePack</code> and pick a single element. However, when building complex template libraries, we often need more than just one type; we need to extract entire chunks of data. Whether you are stripping away metadata or isolating a specific range of arguments, "slicing" is a critical skill.</p>
<p>In this article, we will implement <code>first_n_t</code>, <code>skip_n_t</code>, and the versatile <code>subpack_t</code>.</p>
<h3 id="heading-expanding-the-typepack-interface">Expanding the <code>TypePack</code> Interface</h3>
<p>We add three new alias templates to our core <code>TypePack</code> structure. These act as the public API for our slicing operations.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePack</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">size_t</span> size = <span class="hljs-keyword">sizeof</span>...(Ts);

    <span class="hljs-comment">// ... previous definitions ...</span>

    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Size&gt;
    <span class="hljs-keyword">using</span> <span class="hljs-keyword">first_n_t</span> = details::TypePackFirstN&lt;Size, TypePack&gt;::type;

    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Size&gt;
    <span class="hljs-keyword">using</span> <span class="hljs-keyword">skip_n_t</span> = details::TypePackSkipN&lt;Size, TypePack&gt;::type;

    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size&gt;
    <span class="hljs-keyword">using</span> <span class="hljs-keyword">subpack_t</span> = details::TypePackSubPack&lt;Index, Size, TypePack&gt;::type;
};
</code></pre>
<h3 id="heading-the-supporting-machinery">The Supporting Machinery</h3>
<p>To implement these, we need helper utilities to rebuild packs. The most basic operations are inserting a type at the beginning or the end of an existing <code>TypePack</code>.</p>
<h4 id="heading-1-internal-helpers-insertion">1. Internal Helpers: Insertion</h4>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackInsertAtFirstPosition</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>... <span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackInsertAtFirstPosition</span>&lt;T, TypePack&lt;Ts...&gt;&gt; 
    :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;T, Ts...&gt;&gt; {};
</code></pre>
<h4 id="heading-2-taking-the-first-n-types">2. Taking the First N Types</h4>
<p>To get the first N types, we use recursion. We "take" the head of the pack and prepend it to the result of taking N-1 types from the tail.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> N, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackFirstN</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> N&gt;
<span class="hljs-keyword">requires</span> (N != <span class="hljs-number">0</span>)
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackFirstN</span>&lt;N, TypePack&lt;&gt;&gt; {</span>
    <span class="hljs-keyword">static_assert</span>(AlwaysFalse&lt;<span class="hljs-built_in">std</span>::integral_constant&lt;<span class="hljs-keyword">size_t</span>, N&gt;&gt;, <span class="hljs-string">"N is out of range"</span>);
};

<span class="hljs-keyword">template</span> &lt;class...Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackFirstN</span>&lt;0, TypePack&lt;Ts...&gt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;&gt;&gt; {
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> N, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">requires</span> (<span class="hljs-title">N</span> != 0)
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackFirstN</span>&lt;N, TypePack&lt;T, Ts...&gt;&gt; :</span>
    TypePackInsertAtFirstPosition&lt;T, <span class="hljs-keyword">typename</span> TypePackFirstN&lt;N - <span class="hljs-number">1</span>, TypePack&lt;Ts...&gt;&gt;::type&gt; {
};
</code></pre>
<h4 id="heading-3-skipping-n-types">3. Skipping N Types</h4>
<p>Skipping is simpler than taking. We don't need to rebuild the pack; we simply discard the head until N reaches zero.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> N, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackSkipN</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> N&gt;
<span class="hljs-keyword">requires</span> (N != <span class="hljs-number">0</span>)
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackSkipN</span>&lt;Size, TypePack&lt;&gt;&gt; {</span>
    <span class="hljs-keyword">static_assert</span>(AlwaysFalse&lt;<span class="hljs-built_in">std</span>::integral_constant&lt;<span class="hljs-keyword">size_t</span>, N&gt;&gt;, <span class="hljs-string">"N is out of range"</span>);
};

<span class="hljs-keyword">template</span> &lt;class...Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackSkipN</span>&lt;0, TypePack&lt;Ts...&gt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> N, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>...<span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">requires</span> (<span class="hljs-title">N</span> != 0)
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackSkipN</span>&lt;N, TypePack&lt;T, Ts...&gt;&gt; :</span> TypePackSkipN&lt;N - <span class="hljs-number">1</span>, TypePack&lt;Ts...&gt;&gt; {
};
</code></pre>
<h4 id="heading-4-the-general-subpack-slice">4. The General Subpack (Slice)</h4>
<p>The <code>subpack_t</code> operation is a beautiful example of composition. To get a range starting at <code>Index</code> with a specific <code>Size</code>, we first <strong>skip</strong> the prefix and then <strong>take</strong> the requested number of elements from the remainder.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackSubPack</span>;</span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size, class...Ts&gt;
<span class="hljs-keyword">requires</span> (Index &gt; <span class="hljs-keyword">sizeof</span>...(Ts) || Size &gt; <span class="hljs-keyword">sizeof</span>...(Ts) || Index + Size &gt; <span class="hljs-keyword">sizeof</span>...(Ts))
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackSubPack</span>&lt;Index, Size, TypePack&lt;Ts...&gt;&gt; {</span>
    <span class="hljs-keyword">static_assert</span>(Index &lt;= <span class="hljs-keyword">sizeof</span>...(Ts), <span class="hljs-string">"Index out of range"</span>);
    <span class="hljs-keyword">static_assert</span>(Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts), <span class="hljs-string">"Size out of range"</span>);
    <span class="hljs-keyword">static_assert</span>(Index + Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts), <span class="hljs-string">"Index + Size out of range"</span>);
};

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-keyword">size_t</span> Size, class...Ts&gt;
<span class="hljs-keyword">requires</span> (Index &lt;= <span class="hljs-keyword">sizeof</span>...(Ts) &amp;&amp; Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts) &amp;&amp; Index + Size &lt;= <span class="hljs-keyword">sizeof</span>...(Ts))
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackSubPack</span>&lt;Index, Size, TypePack&lt;Ts...&gt;&gt; :</span>
    TypePackFirstN&lt;Size, <span class="hljs-keyword">typename</span> TypePackSkipN&lt;Index, TypePack&lt;Ts...&gt;&gt;::type&gt; {
};
</code></pre>
<h3 id="heading-validation-with-staticassert">Validation with <code>static_assert</code></h3>
<p>Our tests ensure that slicing works on empty packs, single-element packs, and larger collections.</p>
<pre><code class="lang-cpp">TEST(TypePackTests, FirstN0)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
}

TEST(TypePackTests, FirstN1)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);
}

TEST(TypePackTests, FirstN)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">2</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">3</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">first_n_t</span>&lt;<span class="hljs-number">4</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
}

TEST(TypePackTests, SkipN0)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
}

TEST(TypePackTests, SkipN1)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">0</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">1</span>&gt;, TypePack&lt;&gt;&gt;);
}

TEST(TypePackTests, SkipN)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">0</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">2</span>&gt;, TypePack&lt;<span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">3</span>&gt;, TypePack&lt;<span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">skip_n_t</span>&lt;<span class="hljs-number">4</span>&gt;, TypePack&lt;&gt;&gt;);
}

TEST(TypePackTests, SubPack0)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
}

TEST(TypePackTests, SubPack1)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">1</span>, <span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
}

TEST(TypePackTests, SubPack)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">0</span>&gt;, TypePack&lt;&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">1</span>, <span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">long</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">2</span>, <span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">double</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">3</span>, <span class="hljs-number">1</span>&gt;, TypePack&lt;<span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">2</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>&gt;, TypePack&lt;<span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">2</span>, <span class="hljs-number">2</span>&gt;, TypePack&lt;<span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">3</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">1</span>, <span class="hljs-number">3</span>&gt;, TypePack&lt;<span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">subpack_t</span>&lt;<span class="hljs-number">0</span>, <span class="hljs-number">4</span>&gt;, TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;&gt;);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>We have now transitioned from merely looking at types to actively reshaping our type containers. By combining recursion with basic "Head/Tail" manipulation, we created a powerful slicing engine that allows us to isolate any subset of types.</p>
<p>In the next part, we will explore how to grow our containers: inserting new elements into a pack and merging multiple packs into one.</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack (Part 2 of 8)]]></title><description><![CDATA[In the first part of this series, we introduced the TypePack as a container to "capture" variadic template arguments. However, a container is only useful if we can inspect its contents. In this article, we will implement two fundamental operations: q...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-2-of-8</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-2-of-8</guid><category><![CDATA[type-pack]]></category><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Wed, 07 Jan 2026 22:00:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767201729059/0775a0ca-313c-4835-baee-85409b4da3b7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the first part of this series, we introduced the <code>TypePack</code> as a container to "capture" variadic template arguments. However, a container is only useful if we can inspect its contents. In this article, we will implement two fundamental operations: querying the number of types in a pack and retrieving a specific type by its index.</p>
<h3 id="heading-the-implementation-size-and-elementt">The Implementation: <code>size</code> and <code>element_t</code></h3>
<p>To make <code>TypePack</code> useful, we need to provide a way to access its metadata at compile-time. We achieve this by adding a <code>static constexpr</code> size and a template alias for indexing.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;class... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePack</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-comment">// 1. Determine the number of elements in the pack</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">size_t</span> size = <span class="hljs-keyword">sizeof</span>...(Ts);

    <span class="hljs-comment">// 2. Access a specific type by index</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index&gt;
    <span class="hljs-keyword">using</span> <span class="hljs-keyword">element_t</span> = details::TypePackElement&lt;Index, TypePack&gt;::type;
};
</code></pre>
<h4 id="heading-1-the-size-property">1. The <code>size</code> Property</h4>
<p>C++ provides the <code>sizeof...</code> operator specifically for parameter packs. It returns a <code>size_t</code> representing the number of types in the pack. By baking this into the <code>TypePack</code> struct, we allow users to check the pack's length without needing to expand it themselves.</p>
<h4 id="heading-2-the-elementt-logic-recursive-traversal">2. The <code>element_t</code> Logic (Recursive Traversal)</h4>
<p>Accessing an element at a specific index is more complex. Unlike a runtime array, you cannot use <code>Ts[i]</code>. Instead, we must use <strong>recursive template specialization</strong> to "peel off" types from the front of the pack until we reach the desired index.</p>
<p>Here is the supporting logic (usually placed in a <code>details</code> namespace):</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// Primary template</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pack</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackElement</span>;</span>

<span class="hljs-comment">// Base Case 1: Error handling for empty packs or out-of-bounds</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TypePackElement</span>&lt;Index, TypePack&lt;&gt;&gt; {</span>
    <span class="hljs-keyword">static_assert</span>(AlwaysFalse&lt;<span class="hljs-built_in">std</span>::integral_constant&lt;<span class="hljs-keyword">size_t</span>, Index&gt;&gt;, <span class="hljs-string">"Index out of range"</span>);
};

<span class="hljs-comment">// Base Case 2: Found the type (Index is 0)</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>... <span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">TypePackElement</span>&lt;0, TypePack&lt;T, Ts...&gt;&gt; :</span> <span class="hljs-built_in">std</span>::type_identity&lt;T&gt; {
};

<span class="hljs-comment">// Recursive Step: Reduce the index and move to the next type</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">size_t</span> Index, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">class</span>... <span class="hljs-title">Ts</span>&gt;
<span class="hljs-title">requires</span> (<span class="hljs-title">Index</span> &gt; 0 &amp;&amp; <span class="hljs-title">Index</span> &lt;= sizeof...(Ts))
struct TypePackElement&lt;Index, TypePack&lt;T, Ts...&gt;&gt; : TypePackElement&lt;Index - 1, TypePack&lt;Ts...&gt;&gt; {
};</span>
</code></pre>
<ul>
<li><strong>How it works:</strong> If we ask for index <code>2</code> of <code>&lt;int, long, double&gt;</code>, the compiler matches the recursive step. It then looks for index <code>1</code> of <code>&lt;long, double&gt;</code>, and finally index <code>0</code> of <code>&lt;double&gt;</code>. At index <code>0</code>, the specialization extracts <code>double</code> as the <code>type</code>.</li>
</ul>
<h3 id="heading-validation-with-staticassert">Validation with <code>static_assert</code></h3>
<p>We use <code>static_assert</code> within our tests to prove that our indexing logic is accurate and that the <code>size</code> constant correctly reflects the pack's length.</p>
<pre><code class="lang-cpp">TEST(TypePackTests, ElementType)
{
    <span class="hljs-keyword">using</span> Pack = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">long</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt;;

    <span class="hljs-comment">// Verify the size</span>
    <span class="hljs-keyword">static_assert</span>(Pack::size == <span class="hljs-number">4</span>);

    <span class="hljs-comment">// Verify self-identity</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::type, Pack&gt;);

    <span class="hljs-comment">// Verify individual element access</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">element_t</span>&lt;<span class="hljs-number">0</span>&gt;, <span class="hljs-keyword">int</span>&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">element_t</span>&lt;<span class="hljs-number">1</span>&gt;, <span class="hljs-keyword">long</span>&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">element_t</span>&lt;<span class="hljs-number">2</span>&gt;, <span class="hljs-keyword">double</span>&gt;);
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Pack::<span class="hljs-keyword">element_t</span>&lt;<span class="hljs-number">3</span>&gt;, <span class="hljs-keyword">char</span>&gt;);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>By implementing <code>size</code> and <code>element_t</code>, we have turned a raw pack into a structured list that we can navigate. These tools are the building blocks for more advanced operations like searching, filtering, and transforming packs.</p>
<p>In the next part, we will look at how to extract subsets of our <code>TypePack</code>: retrieving the first N elements, the trailing elements, or a specific range of types.</p>
]]></content:encoded></item><item><title><![CDATA[Template Alchemy: Mastering Variadic Packs with TypePack (Part 1 of 8)]]></title><description><![CDATA[In modern C++, variadic templates allow us to work with an arbitrary number of template arguments. However, anyone who has delved deep into Template Metaprogramming (TMP) knows that "parameter packs" (the Ts... in a template) are quite elusive. They ...]]></description><link>https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-1-of-8</link><guid isPermaLink="true">https://blog.antonr.net/template-alchemy-mastering-variadic-packs-with-typepack-part-1-of-8</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[Types]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Sun, 04 Jan 2026 18:17:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767201827696/e23fdd17-b413-4627-8b1b-910deb55fc44.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In modern C++, variadic templates allow us to work with an arbitrary number of template arguments. However, anyone who has delved deep into Template Metaprogramming (TMP) knows that "parameter packs" (the <code>Ts...</code> in a template) are quite elusive. They aren't objects, they aren't types, and they aren't quite arrays. They are a language construct that can only be expanded in specific contexts.</p>
<p>To harness the power of these packs, we need a way to "capture" them, move them around, and manipulate them without immediate expansion. This is where <code>TypePack</code> comes in.</p>
<h3 id="heading-the-problem-why-cant-we-just-use-packs">The Problem: Why Can't We Just Use Packs?</h3>
<p>A pack of types is not a first-class citizen in C++. You cannot store a pack in a variable, nor can you create a <code>using</code> alias for a raw pack.</p>
<p>Consider the following illegal code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span>... Ts&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Error</span> {</span>
    <span class="hljs-keyword">using</span> MyPack = Ts...; <span class="hljs-comment">// ERROR: A parameter pack cannot be used as an alias</span>
};
</code></pre>
<p>Because of this limitation, if you want to pass a collection of types to another metafunction or store them for later use, you must wrap them in a <strong>container</strong>.</p>
<h4 id="heading-the-stdtuple-example">The <code>std::tuple</code> Example</h4>
<p>The most common example of a pack container in the Standard Library is <code>std::tuple</code>.</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// std::tuple "captures" the pack Ts into a single type</span>
<span class="hljs-built_in">std</span>::tuple&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">double</span>, <span class="hljs-keyword">char</span>&gt; myData;
</code></pre>
<p>While <code>std::tuple</code> is great for storing <em>values</em> of different types, it carries overhead because it’s a concrete class designed for runtime use. For pure compile-time metaprogramming, we need something lighter—a "type-only" container.</p>
<h3 id="heading-the-implementation-typepack">The Implementation: <code>TypePack</code></h3>
<p><code>TypePack</code> is a minimalist structure designed to hold a pack of types. It inherits from <code>std::type_identity</code> to make it easier to refer to the pack's own type in complex transformations.</p>
<pre><code class="lang-csharp"><span class="hljs-meta">#include &lt;type_traits&gt;</span>

template &lt;class... Ts&gt;
<span class="hljs-keyword">struct</span> TypePack : std::type_identity&lt;TypePack&lt;Ts...&gt;&gt; {
    <span class="hljs-comment">// This structure intentionally has no data members.</span>
    <span class="hljs-comment">// Its only purpose is to carry the pack 'Ts...' in its signature.</span>
};
</code></pre>
<p>By wrapping <code>Ts...</code> inside <code>TypePack</code>, the pack becomes part of a single, concrete type. You can now pass this <code>TypePack</code> to other templates, nest it, or return it from a metafunction.</p>
<h3 id="heading-validation-and-examples">Validation and Examples</h3>
<p>To ensure our <code>TypePack</code> behaves as expected, we can use <code>static_assert</code>. These tests verify that different instances of <code>TypePack</code> are recognized as unique types and that they correctly inherit their own identity.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// 1. Verify that empty packs are valid</span>
<span class="hljs-keyword">using</span> EmptyPack = TypePack&lt;&gt;;
static_assert(!std::is_same_v&lt;EmptyPack, TypePack&lt;<span class="hljs-keyword">int</span>&gt;&gt;);

<span class="hljs-comment">// 2. Verify identity inheritance</span>
<span class="hljs-keyword">using</span> MyTypes = TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">float</span>, <span class="hljs-keyword">double</span>&gt;;
static_assert(std::is_same_v&lt;MyTypes::type, MyTypes&gt;);

<span class="hljs-comment">// 3. Nested TypePacks (TypePacks can hold other TypePacks)</span>
<span class="hljs-keyword">using</span> Nested = TypePack&lt;TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>&gt;, <span class="hljs-keyword">char</span>&gt;;
static_assert(std::is_same_v&lt;Nested::type, TypePack&lt;TypePack&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>&gt;, <span class="hljs-keyword">char</span>&gt;&gt;);
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p><code>TypePack</code> is the foundation of our library. By wrapping a variadic pack into a struct, we bypass the language limitations that prevent us from treating packs as entities. We have moved from a raw, "unstable" pack to a stable, nameable type.</p>
<p>In the next part, we will explore how to calculate the size of a <code>TypePack</code> and how to get individual types from it.</p>
]]></content:encoded></item><item><title><![CDATA[🚀 Mastering Type Constraints: The IsSpecializationOf Concept in C++20]]></title><description><![CDATA[I’ve always had a passion for C++ metaprogramming. There’s something incredibly satisfying about moving logic from runtime to compile-time, creating interfaces that are both flexible and type-safe.
With C++20 Concepts, the game has changed, allowing ...]]></description><link>https://blog.antonr.net/mastering-type-constraints-the-isspecializationof-concept-in-c20</link><guid isPermaLink="true">https://blog.antonr.net/mastering-type-constraints-the-isspecializationof-concept-in-c20</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Tue, 30 Dec 2025 10:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767263798463/17c19c54-b5cd-406f-99b8-767e34175ee9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I’ve always had a passion for C++ metaprogramming. There’s something incredibly satisfying about moving logic from runtime to compile-time, creating interfaces that are both flexible and type-safe.</p>
<p>With <strong>C++20 Concepts</strong>, the game has changed, allowing us to express intent more clearly than ever. Today, I want to share a pattern I find particularly useful: the <code>IsSpecializationOf</code> concept.</p>
<h3 id="heading-the-challenge">The Challenge</h3>
<p>If you want to sum a <code>std::vector</code> of any numeric type, you might start with a basic template. While it works, things get complicated when you need to handle multiple vectors of different types simultaneously or restrict template arguments to specific container shapes.</p>
<p>To gain more control, we need a way to check if a type is a specialization of a specific template (like <code>std::vector</code>).</p>
<h3 id="heading-the-implementation">The Implementation</h3>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;vector&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;numeric&gt;</span></span>

<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">sum_vector</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;T&gt;&amp; vec)</span> -&gt; T </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">std</span>::accumulate(vec.begin(), vec.end(), T{});
}

<span class="hljs-keyword">namespace</span> details {
    <span class="hljs-comment">// Primary template: defaults to false</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span>, <span class="hljs-title">template</span> &lt;class...&gt; <span class="hljs-title">class</span>&gt;
    <span class="hljs-title">inline</span> <span class="hljs-title">constexpr</span> <span class="hljs-title">bool</span> <span class="hljs-title">isSpecializationOf</span> = <span class="hljs-title">false</span>;</span>

    <span class="hljs-comment">// Partial specialization: matches if the type is Template&lt;Ts...&gt;</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">template</span> &lt;class...&gt; <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Template</span>, <span class="hljs-title">class</span>... <span class="hljs-title">Ts</span>&gt;
    <span class="hljs-title">inline</span> <span class="hljs-title">constexpr</span> <span class="hljs-title">bool</span> <span class="hljs-title">isSpecializationOf</span>&lt;Template&lt;Ts...&gt;, Template&gt; = <span class="hljs-title">true</span>;</span>
} <span class="hljs-comment">// namespace details</span>

<span class="hljs-comment">// The C++20 Concept</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">template</span> &lt;class...&gt; <span class="hljs-title">class</span> <span class="hljs-title">Template</span>&gt;
<span class="hljs-title">concept</span> <span class="hljs-title">IsSpecializationOf</span> = <span class="hljs-title">details</span>:</span>:isSpecializationOf&lt;T, Template&gt;;

<span class="hljs-comment">// Constrain the parameter pack to only allow std::vector specializations</span>
<span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">sum_all_vectors</span><span class="hljs-params">(<span class="hljs-keyword">const</span> IsSpecializationOf&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&gt; <span class="hljs-keyword">auto</span>&amp; ... vecs)</span>
<span class="hljs-comment">// Ensure all vectors contain arithmetic types (int, float, etc.)</span>
<span class="hljs-title">requires</span> <span class="hljs-params">((<span class="hljs-built_in">std</span>::is_arithmetic_v&lt;<span class="hljs-keyword">typename</span> <span class="hljs-built_in">std</span>::<span class="hljs-keyword">decay_t</span>&lt;<span class="hljs-keyword">decltype</span>(vecs)&gt;::value_type&gt; &amp;&amp; ...))</span> </span>{
    <span class="hljs-comment">// Use a fold expression to sum everything into a double</span>
    <span class="hljs-keyword">return</span> (<span class="hljs-built_in">std</span>::accumulate(vecs.begin(), vecs.end(), <span class="hljs-number">0.0</span>) + ...);
}

<span class="hljs-comment">// Usage:</span>
<span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;<span class="hljs-keyword">int</span>&gt;    v1{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>};
<span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;<span class="hljs-keyword">float</span>&gt;  v2{<span class="hljs-number">3.5f</span>, <span class="hljs-number">4.5f</span>};
<span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;<span class="hljs-keyword">double</span>&gt; v3{<span class="hljs-number">10.0</span>, <span class="hljs-number">20.0</span>};

<span class="hljs-keyword">double</span> total = sum_all_vectors(v1, v2, v3); <span class="hljs-comment">// Result: 41.0</span>
</code></pre>
<h3 id="heading-why-i-love-this-approach">Why I love this approach:</h3>
<ul>
<li><p><strong>Intent-Based Programming:</strong> Your function signature explicitly states: "I only accept things that are specializations of <code>std::vector</code>."</p>
</li>
<li><p><strong>Variadic Beauty:</strong> It leverages <strong>fold expressions</strong> and concepts to handle an arbitrary number of containers cleanly.</p>
</li>
<li><p><strong>Better Errors:</strong> If you accidentally pass a <code>std::list</code>, the compiler gives a clear error about the failed concept requirement rather than a cryptic error deep inside the function body.</p>
</li>
</ul>
<p>Are you exploring C++20 Concepts in your current projects? What are your favorite metaprogramming patterns? Let's talk in the comments!</p>
]]></content:encoded></item><item><title><![CDATA[C++ Type Loophole: Breaking the Limits of Compile-Time Reflection]]></title><description><![CDATA[The C++ Type Loophole is a clever metaprogramming technique that allows developers to capture and retrieve type information at compile time, despite C++ lacking native reflection. It works by exploiting standard-compliant (though subtle) behaviors in...]]></description><link>https://blog.antonr.net/c-type-loophole-breaking-the-limits-of-compile-time-reflection</link><guid isPermaLink="true">https://blog.antonr.net/c-type-loophole-breaking-the-limits-of-compile-time-reflection</guid><category><![CDATA[C++]]></category><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[reflection]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Wed, 17 Dec 2025 10:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767263837657/105d5a08-2b69-46df-b9b9-d67aedc44756.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The <strong>C++ Type Loophole</strong> is a clever metaprogramming technique that allows developers to capture and retrieve type information at compile time, despite C++ lacking native reflection. It works by exploiting standard-compliant (though subtle) behaviors involving templates and <code>friend</code> functions.</p>
<p>First introduced by <strong>Alexandr Poltavsky</strong> in 2017, this technique opened the door to advanced type introspection, such as extracting member types from structs, creating compile-time type lists, or implementing custom memory layouts for tuple-like structures — all without relying on compiler extensions.</p>
<h3 id="heading-how-it-works">How it works</h3>
<p>The loophole exploits the fact that a <code>friend</code> function can be <strong>declared</strong> in a template class and later <strong>defined</strong> by an instantiation of another template. Because the definition happens during template instantiation, it can "capture" a type provided at that moment and make it available for later retrieval via ADL (Argument Dependent Lookup).</p>
<p>While the original implementation is notoriously difficult to read, here is a modernized, <strong>C++20-ready version</strong> that provides a cleaner interface for everyday metaprogramming.</p>
<h3 id="heading-implementation">Implementation</h3>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;type_traits&gt;</span></span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Loophole</span> <span class="hljs-title">final</span>
{</span>
    <span class="hljs-comment">// Decl: Generates a friend declaration with an 'auto' return type.</span>
    <span class="hljs-comment">// This act creates the "slot" where the type will be stored.</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span>&gt;
    <span class="hljs-title">struct</span> <span class="hljs-title">Decl</span> <span class="hljs-title">final</span>
    {</span>
        <span class="hljs-function"><span class="hljs-keyword">friend</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">loophole</span><span class="hljs-params">(Decl key)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">consteval</span> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">loopholeDetect</span><span class="hljs-params">(Decl key)</span> <span class="hljs-keyword">noexcept</span></span>;
    };

    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;
    <span class="hljs-title">struct</span> <span class="hljs-title">ReturnType</span> <span class="hljs-title">final</span> :</span> <span class="hljs-built_in">std</span>::type_identity&lt;T&gt; {};

    <span class="hljs-comment">// Def: Instantiates the friend functions.</span>
    <span class="hljs-comment">// This "plugs" the type into the previously declared slot.</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Key</span>, <span class="hljs-title">class</span> <span class="hljs-title">Value</span>, <span class="hljs-title">bool</span> <span class="hljs-title">IsDefined</span>&gt;
    <span class="hljs-title">struct</span> <span class="hljs-title">Def</span> <span class="hljs-title">final</span>
    {</span>
        <span class="hljs-function"><span class="hljs-keyword">friend</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">loophole</span><span class="hljs-params">([[maybe_unused]] <span class="hljs-keyword">const</span> Decl&lt;Key&gt; key)</span>
        </span>{
            <span class="hljs-keyword">return</span> ReturnType&lt;Value&gt;{};
        }

        <span class="hljs-function"><span class="hljs-keyword">consteval</span> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">loopholeDetect</span><span class="hljs-params">([[maybe_unused]] <span class="hljs-keyword">const</span> Decl&lt;Key&gt; key)</span> <span class="hljs-keyword">noexcept</span>
        </span>{
            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    };

    <span class="hljs-comment">// Specialization to prevent multiple-definition errors</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Key</span>, <span class="hljs-title">class</span> <span class="hljs-title">Value</span>&gt;
    <span class="hljs-title">struct</span> <span class="hljs-title">Def</span>&lt;Key, Value, true&gt; <span class="hljs-title">final</span> {</span>};

    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Key</span>&gt;
    <span class="hljs-title">struct</span> <span class="hljs-title">Setter</span>
    {</span>
        <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt; <span class="hljs-title">static</span> <span class="hljs-title">int</span> <span class="hljs-title">helper</span>(...);</span>
        <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">bool</span> = <span class="hljs-title">loopholeDetect</span>(<span class="hljs-title">T</span>{</span>})&gt; <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">char</span> <span class="hljs-title">helper</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span></span>;

        <span class="hljs-comment">// This method is used in a non-evaluated context to trigger instantiation</span>
        <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Value</span>,
                  <span class="hljs-title">int</span> = <span class="hljs-title">sizeof</span>(<span class="hljs-title">Def</span>&lt;Key, Value, sizeof(helper&lt;Decl&lt;Key&gt;&gt;(0)) == sizeof(char)&gt;)&gt;
        <span class="hljs-title">static</span> <span class="hljs-title">consteval</span> <span class="hljs-title">void</span> <span class="hljs-title">set</span>() {</span>}
    };

    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Key</span>, <span class="hljs-title">class</span> <span class="hljs-title">Value</span>&gt;
    <span class="hljs-title">static</span> <span class="hljs-title">consteval</span> <span class="hljs-title">void</span> <span class="hljs-title">set</span>()
    {</span>
        Setter&lt;Key&gt;::<span class="hljs-keyword">template</span> <span class="hljs-built_in">set</span>&lt;Value&gt;();
    }

    <span class="hljs-comment">// Value: Retrieves the stored type by calling the 'loophole' function</span>
    <span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Key</span>&gt;
    <span class="hljs-title">using</span> <span class="hljs-title">Value</span> = <span class="hljs-title">typename</span> <span class="hljs-title">decltype</span>(<span class="hljs-title">loophole</span>(<span class="hljs-title">Decl</span>&lt;Key&gt;{</span>}))::type;
};

<span class="hljs-comment">// Unique tags for different loophole instances</span>
<span class="hljs-keyword">template</span> &lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">size_t</span> <span class="hljs-title">Index</span>&gt;
<span class="hljs-title">struct</span> <span class="hljs-title">Tag</span>;</span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> 
</span>{
    <span class="hljs-keyword">using</span> Key = Tag&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>, <span class="hljs-number">0</span>&gt;;
    <span class="hljs-keyword">using</span> Value = <span class="hljs-keyword">int</span>;

    <span class="hljs-comment">// Capture the relationship at compile time</span>
    Loophole::<span class="hljs-built_in">set</span>&lt;Key, Value&gt;();

    <span class="hljs-comment">// Retrieve the relationship later</span>
    <span class="hljs-keyword">static_assert</span>(<span class="hljs-built_in">std</span>::is_same_v&lt;Loophole::Value&lt;Key&gt;, Value&gt;);
}
</code></pre>
<h3 id="heading-why-use-this-version">Why use this version?</h3>
<ol>
<li><p><strong>Safety:</strong> It uses <code>consteval</code> and <code>std::type_identity</code> to ensure all operations happen at compile time without runtime overhead.</p>
</li>
<li><p><strong>C++20 Cleanliness:</strong> Leveraging modern template mechanics makes the "detection" phase (checking if a type is already set) more robust.</p>
</li>
<li><p><strong>Readability:</strong> The separation into <code>Decl</code>, <code>Def</code>, and <code>Setter</code> makes the logical flow of "Declare -&gt; Check -&gt; Define" much easier to follow.</p>
</li>
</ol>
<h3 id="heading-sources">Sources</h3>
<ul>
<li><p><a target="_blank" href="https://alexpolt.github.io/type-loophole.html">Alexandr Poltavsky: C++ Type Loophole</a></p>
</li>
<li><p><a target="_blank" href="https://www.google.com/search?q=https://gist.github.com/deni64k/c5728d0596f8f1640318b357701f43e6">Deni64k: Simplified Reflection Gist</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Setting Up Remote Boot (PXE) at Home with iVentoy]]></title><description><![CDATA[If you are familiar with Ventoy, you know it’s a game-changer for creating multiboot USB drives. You simply copy ISO files onto a flash drive, and the tool handles the rest.
iVentoy takes this concept to the next level: it allows you to boot and inst...]]></description><link>https://blog.antonr.net/setting-up-remote-boot-pxe-at-home-with-iventoy</link><guid isPermaLink="true">https://blog.antonr.net/setting-up-remote-boot-pxe-at-home-with-iventoy</guid><category><![CDATA[pxe-boot]]></category><category><![CDATA[iventoy]]></category><category><![CDATA[deployment]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Fri, 01 Sep 2023 09:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767264041468/1f64c1b5-f71d-48be-b691-386eb129866d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you are familiar with <strong>Ventoy</strong>, you know it’s a game-changer for creating multiboot USB drives. You simply copy ISO files onto a flash drive, and the tool handles the rest.</p>
<p><strong>iVentoy</strong> takes this concept to the next level: it allows you to boot and install operating systems on multiple machines over the network with virtually zero configuration. The workflow is just as simple—drop your ISO files into a folder and select PXE boot on the client machine.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767263910745/e615c546-5a66-47de-9612-f36c50b3400e.gif" alt class="image--center mx-auto" /></p>
<p>Here is how I deployed iVentoy using Docker to streamline my home lab.</p>
<h3 id="heading-1-router-configuration-openwrt">1. Router Configuration (OpenWrt)</h3>
<p>To make PXE work, your network needs to know where the boot server is. I configured the <code>dnsmasq</code> DHCP server on my <strong>OpenWrt</strong> router to provide the necessary PXE parameters to all devices.</p>
<p>Add the following to your <code>/etc/config/dhcp</code> (or via LuCI):</p>
<pre><code class="lang-bash">config boot
    option servername <span class="hljs-string">'server'</span>
    option serveraddress <span class="hljs-string">'192.168.1.20'</span> <span class="hljs-comment"># IP of your iVentoy host</span>
    option filename <span class="hljs-string">'iventoy_loader_16000'</span>
</code></pre>
<h3 id="heading-2-crafting-the-docker-image">2. Crafting the Docker Image</h3>
<p>I created a lightweight Docker image based on Debian to host the iVentoy binaries.</p>
<p><strong>Dockerfile:</strong></p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> debian:bookworm-slim
<span class="hljs-keyword">ARG</span> IVENTOY_VER=<span class="hljs-number">1.0</span>.<span class="hljs-number">19</span>

<span class="hljs-comment"># Download and extract iVentoy</span>
<span class="hljs-keyword">ADD</span><span class="bash"> https://github.com/ventoy/PXE/releases/download/v<span class="hljs-variable">$IVENTOY_VER</span>/iventoy-<span class="hljs-variable">$IVENTOY_VER</span>-linux-free.tar.gz /tmp/iventoy.tar.gz</span>
<span class="hljs-keyword">RUN</span><span class="bash"> tar -xf /tmp/iventoy.tar.gz -C /opt &amp;&amp; \
    mv /opt/iventoy-<span class="hljs-variable">$IVENTOY_VER</span> /opt/iventoy</span>

<span class="hljs-keyword">VOLUME</span><span class="bash"> /opt/iventoy/iso</span>
<span class="hljs-comment"># Ports for DHCP, TFTP, NBD, and Web UI</span>
<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">68</span>/udp <span class="hljs-number">69</span>/udp <span class="hljs-number">10809</span>/tcp <span class="hljs-number">16000</span>/tcp <span class="hljs-number">26000</span>/tcp

<span class="hljs-keyword">ENTRYPOINT</span><span class="bash"> [<span class="hljs-string">"bash"</span>, <span class="hljs-string">"-c"</span>, <span class="hljs-string">"cd /opt/iventoy &amp;&amp; ./iventoy.sh -R start &amp;&amp; sleep 5 &amp;&amp; tail -f log/log.txt"</span>]</span>
</code></pre>
<h3 id="heading-3-deployment-with-docker-compose">3. Deployment with Docker Compose</h3>
<p>Using <code>docker-compose</code> makes managing the volumes and network modes much easier. Note that <code>network_mode: host</code> is critical for PXE to intercept DHCP requests correctly.</p>
<p><strong>docker-compose.yml:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.7"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">iventoy:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">iventoy</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span> <span class="hljs-comment"># Path to the Dockerfile</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">iventoy</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span>
    <span class="hljs-attr">privileged:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">cap_add:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NET_ADMIN</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">iventoy</span>
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">host</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/share/Boot-Images:/opt/iventoy/iso:ro</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/share/container-data/iventoy/config.dat:/opt/iventoy/data/config.dat</span>
</code></pre>
<h3 id="heading-4-management-and-testing">4. Management and Testing</h3>
<p>Once the container is running, the web interface is accessible at <code>http://&lt;your_ip&gt;:26000</code>. From there, you can customize the boot screen, manage ISOs, and adjust deployment settings.</p>
<p>I tested this setup by booting an <strong>openSUSE</strong> installation ISO on a Proxmox VM. The client picked up the IP, loaded the iVentoy menu, and started the installation flawlessly!</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>If you frequently reinstall operating systems or manage a home lab, iVentoy is a must-have. It eliminates the need for physical USB sticks and makes OS deployment as simple as copying a file.</p>
]]></content:encoded></item><item><title><![CDATA[Setting Up Windows Terminal for a Productive Workflow]]></title><description><![CDATA[A well-configured terminal is the heart of a developer's productivity. Here is my personal guide to transforming the standard Windows Terminal into a high-performance environment using PowerShell Core and modern CLI tools.
1. Prerequisites
First, ens...]]></description><link>https://blog.antonr.net/setting-up-windows-terminal-for-a-productive-workflow</link><guid isPermaLink="true">https://blog.antonr.net/setting-up-windows-terminal-for-a-productive-workflow</guid><category><![CDATA[terminal]]></category><category><![CDATA[setup]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Tue, 01 Aug 2023 09:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767264117722/cadfebc8-561f-4238-a80f-81d14d0a20df.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A well-configured terminal is the heart of a developer's productivity. Here is my personal guide to transforming the standard Windows Terminal into a high-performance environment using PowerShell Core and modern CLI tools.</p>
<h3 id="heading-1-prerequisites">1. Prerequisites</h3>
<p>First, ensure you have the latest versions of the essential components installed:</p>
<ul>
<li><p><strong>Windows Terminal</strong> (available via Microsoft Store or GitHub).</p>
</li>
<li><p><strong>PowerShell Core (pwsh)</strong>: The cross-platform version of PowerShell is faster and more feature-rich than the built-in Windows PowerShell.</p>
</li>
</ul>
<h3 id="heading-2-profile-configuration">2. Profile Configuration</h3>
<p>Organize your profiles in the Windows Terminal settings (<code>Settings &gt; Startup</code>).</p>
<ul>
<li><p><strong>Set Default Profile:</strong> Set PowerShell Core as your default.</p>
</li>
<li><p><strong>Ordering:</strong> Place your most-used environments at the top. For instance, I keep <strong>PowerShell Core</strong> first and my <strong>WSL distribution</strong> (openSUSE Tumbleweed) second. This allows for quick access via <code>Ctrl+Shift+1</code>, <code>Ctrl+Shift+2</code>, etc.</p>
</li>
</ul>
<h3 id="heading-3-typography-and-nerd-fonts">3. Typography and Nerd Fonts</h3>
<p>To render icons and glyphs correctly in the terminal, you need a <strong>Nerd Font</strong> (patched fonts containing extra symbols).</p>
<ol>
<li><p>Download a font from <a target="_blank" href="https://www.nerdfonts.com/">Nerd Fonts</a>. My personal favorites are <strong>Cascadia Code</strong> and <strong>JetBrains Mono</strong>.</p>
</li>
<li><p>Install the font and set it in Windows Terminal: <code>Settings &gt; Profiles &gt; PowerShell &gt; Appearance &gt; Font face</code>.</p>
</li>
</ol>
<hr />
<h3 id="heading-4-essential-powershell-plugins">4. Essential PowerShell Plugins</h3>
<p>Run these commands in PowerShell Core to install the core productivity stack:</p>
<p><strong>a) Oh-My-Posh</strong> (Theming engine)</p>
<pre><code class="lang-powershell">winget install JanDeDobbeleer.OhMyPosh
</code></pre>
<p><strong>b) Posh-Git</strong> (Git status in your prompt)</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">Install-Module</span> posh<span class="hljs-literal">-git</span> <span class="hljs-literal">-Confirm</span>:<span class="hljs-variable">$False</span> <span class="hljs-literal">-Force</span>
</code></pre>
<p><strong>c) Terminal-Icons</strong> (Adds icons to <code>ls</code> / <code>dir</code> outputs)</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">Install-Module</span> Terminal<span class="hljs-literal">-Icons</span> <span class="hljs-literal">-Confirm</span>:<span class="hljs-variable">$False</span> <span class="hljs-literal">-Force</span>
</code></pre>
<p><strong>d) PSFzf</strong> (Fuzzy search for files and history)</p>
<pre><code class="lang-powershell">winget install junegunn.fzf
<span class="hljs-built_in">Install-Module</span> PSFzf <span class="hljs-literal">-Confirm</span>:<span class="hljs-variable">$False</span> <span class="hljs-literal">-Force</span>
</code></pre>
<p><strong>e) Zoxide</strong> (A smarter <code>cd</code> command)</p>
<pre><code class="lang-powershell">winget install ajeetdsouza.zoxide
</code></pre>
<h3 id="heading-5-configuring-oh-my-posh">5. Configuring Oh-My-Posh</h3>
<p>Initialize Oh-My-Posh to see available themes:</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">oh</span><span class="hljs-literal">-my</span><span class="hljs-literal">-posh</span> init pwsh | <span class="hljs-built_in">Invoke-Expression</span>
<span class="hljs-built_in">Get-PoshThemes</span>
</code></pre>
<p>You can choose from dozens of built-in styles. Personally, I use a custom modified version of the <strong>"hotstick.minimal"</strong> theme.</p>
<h3 id="heading-6-finalizing-your-profile-script">6. Finalizing Your Profile Script</h3>
<p>The PowerShell profile is a script that runs every time you start the terminal. Open it by typing:</p>
<pre><code class="lang-powershell">notepad <span class="hljs-variable">$PROFILE</span>
</code></pre>
<p>Paste your configuration into this file. You can use <a target="_blank" href="https://pastebin.com/raw/5GkJ1Rdp">my reference profile here</a> as a template. Make sure to update the theme initialization line:</p>
<pre><code class="lang-powershell"><span class="hljs-comment"># Example theme initialization</span>
<span class="hljs-built_in">oh</span><span class="hljs-literal">-my</span><span class="hljs-literal">-posh</span> init pwsh -<span class="hljs-literal">-config</span> <span class="hljs-string">"<span class="hljs-variable">$env:POSH_THEMES_PATH</span>/mytheme.omp.json"</span> | <span class="hljs-built_in">Invoke-Expression</span>

<span class="hljs-comment"># Add your custom aliases here</span>
<span class="hljs-built_in">Set-Alias</span> v nvim
</code></pre>
<h3 id="heading-key-benefits-of-this-setup">Key Benefits of This Setup</h3>
<p>By following these steps, your terminal will support:</p>
<ul>
<li><p><strong>Rich Visuals:</strong> A prompt showing Git branch status and execution time.</p>
</li>
<li><p><strong>Visual File Navigation:</strong> Colorful icons in file listings.</p>
</li>
<li><p><strong>Intelligence:</strong> Suggestions based on your command history.</p>
</li>
<li><p><strong>Speed:</strong> Fuzzy search for files (<code>Ctrl+F</code>) and history (<code>Ctrl+R</code>).</p>
</li>
<li><p><strong>Efficiency:</strong> Smart directory jumping with the <code>z</code> command and custom aliases.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767191125195/3dcb1613-1ed3-4b3f-a939-0492f362c1ef.gif" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[AV1 vs. HEVC: Efficiently Archiving Ghibli Classics]]></title><description><![CDATA[I recently set out to optimize my Studio Ghibli collection by re-encoding Blu-ray discs into the AV1 format. My goal was to significantly reduce file sizes without compromising visual quality. AV1 was the ideal candidate due to its high compression e...]]></description><link>https://blog.antonr.net/av1-vs-hevc-efficiently-archiving-ghibli-classics</link><guid isPermaLink="true">https://blog.antonr.net/av1-vs-hevc-efficiently-archiving-ghibli-classics</guid><category><![CDATA[AV1]]></category><category><![CDATA[video]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Sat, 01 Jul 2023 09:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767264193302/62d211c1-59c7-4921-8773-1ae39fed3473.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently set out to optimize my Studio Ghibli collection by re-encoding Blu-ray discs into the <strong>AV1</strong> format. My goal was to significantly reduce file sizes without compromising visual quality. AV1 was the ideal candidate due to its high compression efficiency, royalty-free licensing, and growing hardware support.</p>
<p>For this experiment, I chose the 1991 film <em>"Only Yesterday"</em>. It is notoriously difficult to transcode due to persistent picture jitter and film grain (noise). Using <strong>FFmpeg 6.0</strong>, I compared HEVC and AV1, finding that a <strong>10-bit</strong> depth and <strong>Constant Rate Factor (CRF)</strong> yielded the best results for both.</p>
<h3 id="heading-the-results">The Results</h3>
<p>The AV1 encode was remarkably <strong>four times smaller</strong> (645 MB) than the HEVC version (2563 MB), despite maintaining nearly identical quality metrics. However, the trade-off was encoding time: using <code>libaom</code>, it took approximately <strong>three days</strong> to process the two-hour movie.</p>
<h3 id="heading-encoding-parameters">Encoding Parameters</h3>
<p>HEVC (x265):</p>
<p>-c:v libx265 -preset slow -tune animation -profile:v main10 -pix_fmt yuv420p10le -crf 22 -x265-params crf=22:no-sao=1:no-fast-intra=1</p>
<p>AV1 (libaom):</p>
<p>-c:v libaom-av1 -pix_fmt yuv420p10le -b:v 0 -crf 30 -threads 8 -cpu-used 4 -aq-mode 1 -tune ssim -lag-in-frames 35 -arnr-max-frames 15 -arnr-strength 4 -aom-params tune=ssim:cq-level=30:cpu-used=4:noise-sensitivity=2:tune-content=default:arnr-strength=4:arnr-maxframes=15:enable-qm=1:enable-chroma-deltaq=1:quant-b-adapt=1</p>
<h3 id="heading-quality-metrics-comparison">Quality Metrics Comparison</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Metric</strong></td><td><strong>HEVC (2.5 GB)</strong></td><td><strong>AV1 (0.6 GB)</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>PSNR (Average)</strong></td><td>42.35 dB</td><td>42.10 dB</td></tr>
<tr>
<td><strong>SSIM (All)</strong></td><td>0.9634</td><td>0.9635</td></tr>
</tbody>
</table>
</div><p>The metrics confirm that AV1 achieved the same visual fidelity as HEVC while using only <strong>25% of the disk space</strong>. For archival purposes where storage is at a premium and encoding time is not a concern, AV1 is the clear winner.</p>
]]></content:encoded></item><item><title><![CDATA[Micro-optimizations in .NET (x86/x64)]]></title><description><![CDATA[This is the second post in the "Micro-optimizations in .NET" series. If you missed the first one, check out Conversion from Boolean to Integer.
Example 2: Integer Comparison (Branchless Design)
As discussed previously, conditional branches are costly...]]></description><link>https://blog.antonr.net/micro-optimizations-in-net-x86x64-1</link><guid isPermaLink="true">https://blog.antonr.net/micro-optimizations-in-net-x86x64-1</guid><category><![CDATA[branchless]]></category><category><![CDATA[C#]]></category><category><![CDATA[optimization]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Fri, 09 Jun 2023 09:00:00 GMT</pubDate><content:encoded><![CDATA[<p>This is the second post in the "Micro-optimizations in .NET" series. If you missed the first one, check out <a target="_blank" href="https://hashnode.com/post/cmju30g8c000302jybuc678w8">Conversion from Boolean to Integer</a>.</p>
<h3 id="heading-example-2-integer-comparison-branchless-design">Example 2: Integer Comparison (Branchless Design)</h3>
<p>As discussed previously, conditional branches are costly for modern processors when the <strong>branch predictor</strong> fails. Branch misprediction occurs frequently unless the data is homogeneous (e.g., all values are identical). The core idea of optimizing such algorithms is to "straighten" the execution flow by removing conditional jumps.</p>
<p>Let's examine a standard integer comparison function. It returns <code>-1</code> if a &lt; b, <code>1</code> if a &gt; b, and <code>0</code> if a = b.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">CompareIntegers</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b</span>)</span>
{
    <span class="hljs-keyword">if</span> (a &lt; b) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
    <span class="hljs-keyword">if</span> (a &gt; b) <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>In the worst-case scenario (when operands are equal), the CPU must evaluate two conditional jumps. The assembly listing confirms this:</p>
<pre><code class="lang-csharp">; Examples.CompareIntegers(Int32, Int32)
    L0000: cmp ecx, edx        ; Compare a and b
    L0002: jge <span class="hljs-keyword">short</span> L000a     ; Jump <span class="hljs-keyword">if</span> a &gt;= b
    L0004: mov eax, <span class="hljs-number">0xffffffff</span> ; Result = <span class="hljs-number">-1</span>
    L0009: ret
    L000a: cmp ecx, edx        ; Compare again
    L000c: jle <span class="hljs-keyword">short</span> L0014     ; Jump <span class="hljs-keyword">if</span> a &lt;= b
    L000e: mov eax, <span class="hljs-number">1</span>          ; Result = <span class="hljs-number">1</span>
    L0013: ret
    L0014: xor eax, eax        ; Result = <span class="hljs-number">0</span>
    L0016: ret
</code></pre>
<h3 id="heading-the-optimized-branchless-approach">The Optimized Branchless Approach</h3>
<p>We can eliminate branches by recognizing that the result is essentially the difference between two boolean states. Instead of <code>if</code> statements, we use the <code>setg</code> (set if greater) and <code>setl</code> (set if less) instructions, which don't trigger a jump.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">CompareIntegersOptimized</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b</span>)</span>
{
    <span class="hljs-keyword">bool</span> isGreater = a &gt; b;
    <span class="hljs-keyword">bool</span> isLess = a &lt; b;

    <span class="hljs-comment">// Using the trick from Part 1</span>
    <span class="hljs-keyword">return</span> ConvertBooleanToIntOptimized(isGreater) - 
           ConvertBooleanToIntOptimized(isLess);
}
</code></pre>
<p>The generated machine code is now linear:</p>
<pre><code class="lang-csharp">; Examples.CompareIntegersOptimized(Int32, Int32)
    L0000: xor eax, eax   ; Clear EAX
    L0002: cmp ecx, edx   ; Compare a and b
    L0004: setg al        ; AL = (a &gt; b) ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>
    L0007: cmp ecx, edx   ; Compare again
    L0009: setl dl        ; DL = (a &lt; b) ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>
    L000c: movzx edx, dl  ; Zero-extend DL to EDX
    L000f: sub eax, edx   ; EAX = isGreater - isLess
    L0011: ret
</code></pre>
<h3 id="heading-performance-benchmarks">Performance Benchmarks</h3>
<p>The impact of branch prediction is clearly visible when comparing random data vs. constant data.</p>
<p>1. Random Data (Unpredictable):</p>
<p>The branchless version is ~3x faster because it avoids the massive penalty of mispredictions.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Method</strong></td><td><strong>Mean</strong></td><td><strong>Error</strong></td><td><strong>StdDev</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>CompareIntegers</strong></td><td>3.855 ms</td><td>0.0654 ms</td><td>0.0580 ms</td></tr>
<tr>
<td><strong>CompareIntegersOptimized</strong></td><td><strong>1.203 ms</strong></td><td>0.0342 ms</td><td>0.0970 ms</td></tr>
</tbody>
</table>
</div><p>2. Constant Data (Predictable Zeroes):</p>
<p>Interestingly, the branchless version is ~20% slower here. When the CPU can perfectly predict the branch, the standard if version wins because it executes only 6 instructions versus the 8 instructions in our optimized version.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Method</strong></td><td><strong>Mean</strong></td><td><strong>Error</strong></td><td><strong>StdDev</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>CompareIntegers</strong></td><td><strong>885.9 μs</strong></td><td>15.62 μs</td><td>13.84 μs</td></tr>
<tr>
<td><strong>CompareIntegersOptimized</strong></td><td>1,070.7 μs</td><td>14.5<a target="_blank" href="https://www.google.com/search?q=%23">8 μs</a></td><td>12.93 μs</td></tr>
</tbody>
</table>
</div><h3 id="heading-generic-implementation">Generic Implementation</h3>
<p>Thanks to <strong>Static Abstract Members in Interfaces</strong>, we can make this logic generic for any type that supports comparison operators:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">CompareGeneric</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">T left, T right</span>) 
    <span class="hljs-keyword">where</span> T : IComparisonOperators&lt;T, T, <span class="hljs-keyword">bool</span>&gt;</span>
{
    <span class="hljs-keyword">return</span> ConvertBooleanToIntOptimized(left &gt; right) - 
           ConvertBooleanToIntOptimized(left &lt; right);
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Branchless code is a powerful tool for processing random or "noisy" data. However, as shown, it's not a silver bullet. Always profile your specific data distribution before committing to such micro-optimizations.</p>
]]></content:encoded></item><item><title><![CDATA[Micro-optimizations in .NET (x86/x64)]]></title><description><![CDATA[First of all, I want to thank the developers of SharpLab for providing such an amazing tool to explore the machine code generated by the .NET JIT compiler. All examples in this post are based on .NET 7/8.
Conversion from Boolean to Integer
This is a ...]]></description><link>https://blog.antonr.net/micro-optimizations-in-net-x86x64</link><guid isPermaLink="true">https://blog.antonr.net/micro-optimizations-in-net-x86x64</guid><category><![CDATA[C#]]></category><category><![CDATA[optimization]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[Assembly]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Mon, 05 Jun 2023 09:00:00 GMT</pubDate><content:encoded><![CDATA[<p>First of all, I want to thank the developers of <strong>SharpLab</strong> for providing such an amazing tool to explore the machine code generated by the .NET JIT compiler. All examples in this post are based on <strong>.NET 7/8</strong>.</p>
<h3 id="heading-conversion-from-boolean-to-integer">Conversion from Boolean to Integer</h3>
<p>This is a popular and straightforward example of a micro-optimization. Suppose we want to convert a <code>bool</code> to an <code>int</code>: <code>1</code> for <code>true</code> and <code>0</code> for <code>false</code>.</p>
<p>The most obvious implementation uses a ternary operator:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">MethodImpl(MethodImplOptions.AggressiveInlining)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ConvertBooleanToInt</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> <span class="hljs-keyword">value</span></span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">value</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>;
}
</code></pre>
<p>If you examine the generated machine code, you will see a <strong>conditional branch</strong> (<code>jne</code>):</p>
<pre><code class="lang-csharp">; Examples.ConvertBooleanToInt(Boolean)
    L0000: test cl, cl
    L0002: jne <span class="hljs-keyword">short</span> L0007
    L0004: xor eax, eax     ; result = <span class="hljs-number">0</span>
    L0006: ret
    L0007: mov eax, <span class="hljs-number">1</span>       ; result = <span class="hljs-number">1</span>
    L000c: ret
</code></pre>
<p>Conditional branches can be expensive due to potential <strong>branch mispredictions</strong>. However, in the CLI, a <code>bool</code> is physically stored as a 1-byte value (<code>0</code> for false, <code>1</code> for true). We can exploit this by reinterpreting the memory directly.</p>
<h3 id="heading-using-unsafe-pointers">Using Unsafe Pointers</h3>
<p>You can treat the address of the boolean as a pointer to a byte. <em>Note: In your original snippet,</em> <code>return (byte*)&amp;value</code> would return the memory address. To get the value, we must dereference it:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">MethodImpl(MethodImplOptions.AggressiveInlining)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ConvertBooleanToIntUnsafe</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> <span class="hljs-keyword">value</span></span>)</span> 
{
    <span class="hljs-keyword">return</span> *(<span class="hljs-keyword">byte</span>*)&amp;<span class="hljs-keyword">value</span>;
}
</code></pre>
<h3 id="heading-the-modern-way-unsafeashttpunsafeas">The Modern Way: <a target="_blank" href="http://Unsafe.As">Unsafe.As</a></h3>
<p>A cleaner way to achieve the same result without manual pointer manipulation is using the <code>Unsafe</code> class. This produces the same optimized machine code:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">MethodImpl(MethodImplOptions.AggressiveInlining)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ConvertBooleanToIntOptimized</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> <span class="hljs-keyword">value</span></span>)</span> 
{
    <span class="hljs-keyword">return</span> Unsafe.As&lt;<span class="hljs-keyword">bool</span>, <span class="hljs-keyword">byte</span>&gt;(<span class="hljs-keyword">ref</span> <span class="hljs-keyword">value</span>);
}
</code></pre>
<p><strong>Generated Machine Code:</strong></p>
<pre><code class="lang-csharp">; Examples.ConvertBooleanToIntOptimized(Boolean)
    L0000: movzx eax, cl
    L0003: ret
</code></pre>
<p>The JIT compiler now uses a single <code>movzx</code> (move with zero-extend) instruction. This completely eliminates the branch, making the code deterministic and faster in tight loops.</p>
<h3 id="heading-update-improvements-in-net-9">Update: Improvements in .NET 9</h3>
<p>It is worth noting that starting with <strong>.NET 9</strong>, the JIT compiler has become much smarter at handling this specific pattern. The community and the Microsoft team implemented an optimization that recognizes the ternary conversion <code>value ? 1 : 0</code> and automatically transforms it into a branchless <code>movzx</code> instruction.</p>
<p>This means that in .NET 9, the "obvious" version and the "optimized" version now produce identical, high-performance machine code:</p>
<pre><code class="lang-csharp">; Examples.ConvertBooleanToInt(Boolean) <span class="hljs-keyword">in</span> .NET <span class="hljs-number">9</span>
    L0000: movzx eax, cl
    L0003: ret
</code></pre>
<h3 id="heading-should-you-still-use-unsafeashttpunsafeas">Should you still use <a target="_blank" href="http://Unsafe.As"><code>Unsafe.As</code></a>?</h3>
<p>While .NET 9 handles the simple <code>1 : 0</code> case, <a target="_blank" href="http://Unsafe.As"><code>Unsafe.As</code></a> or pointer manipulation remains a valuable tool for:</p>
<ol>
<li><p><strong>Older Runtime Versions:</strong> If your library supports .NET 6, 7, or 8.</p>
</li>
<li><p><strong>Complex Logic:</strong> When the mapping isn't a simple <code>1</code> or <code>0</code>, or when you are reinterpreting <code>bool</code> as part of a larger struct layout.</p>
</li>
<li><p><strong>Educational purposes:</strong> Understanding how data is represented in memory is key to writing high-performance code.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Interchangeability of Integral Type Arrays]]></title><description><![CDATA[In .NET, an array of a simple integral type can be cast to an array of another integral type, provided they have the same element size. This is possible because the CLR treats these arrays as having a compatible memory layout.
int[] arrayOfInts = new...]]></description><link>https://blog.antonr.net/interchangeability-of-integral-type-arrays</link><guid isPermaLink="true">https://blog.antonr.net/interchangeability-of-integral-type-arrays</guid><category><![CDATA[C#]]></category><category><![CDATA[array]]></category><category><![CDATA[tricks]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Wed, 21 Oct 2015 09:00:00 GMT</pubDate><content:encoded><![CDATA[<p>In .NET, an array of a simple integral type can be cast to an array of another integral type, provided they have the same element size. This is possible because the CLR treats these arrays as having a compatible memory layout.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">int</span>[] arrayOfInts = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">5</span>];
arrayOfInts[<span class="hljs-number">0</span>] = <span class="hljs-number">-12</span>;

<span class="hljs-comment">// Cast via object or Array to bypass the compiler check</span>
<span class="hljs-keyword">uint</span>[] arrayOfUints = (<span class="hljs-keyword">uint</span>[])(<span class="hljs-keyword">object</span>)arrayOfInts;

Console.WriteLine(arrayOfUints[<span class="hljs-number">0</span>]); <span class="hljs-comment">// Output: 4294967284 (binary representation of -12)</span>
</code></pre>
<h3 id="heading-key-points">Key Points:</h3>
<ul>
<li><p><strong>Size Identity:</strong> This works between types of the same bit-width, such as <code>int[]</code> and <code>uint[]</code>, or <code>short[]</code> and <code>ushort[]</code>.</p>
</li>
<li><p><strong>Pointer Types:</strong> It also applies to <code>IntPtr[]</code> and <code>UIntPtr[]</code>.</p>
</li>
<li><p><strong>Platform Dependency:</strong> You can cast <code>IntPtr[]</code> to <code>int[]</code> on x86, or to <code>long[]</code> on x64, because their sizes match on those specific architectures.</p>
</li>
<li><p><strong>Why it works:</strong> The CLR does not perform a conversion of the data; it simply reinterprets the existing memory. This is similar to a <code>reinterpret_cast</code> in C++.</p>
</li>
</ul>
<blockquote>
<p><strong>Note:</strong> While powerful for high-performance scenarios, use this with caution as it bypasses the type safety usually expected in C#.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Executing Native Code in .NET Without External DLLs]]></title><description><![CDATA[This example demonstrates how to execute native x86 machine code directly from .NET and, conversely, how to call a managed .NET method from that native code — all without loading any external libraries.
How it works
The core idea is to allocate a chu...]]></description><link>https://blog.antonr.net/executing-native-code-in-net-without-external-dlls</link><guid isPermaLink="true">https://blog.antonr.net/executing-native-code-in-net-without-external-dlls</guid><category><![CDATA[C#]]></category><category><![CDATA[interoperability]]></category><category><![CDATA[Assembly]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Mon, 13 May 2013 09:00:00 GMT</pubDate><content:encoded><![CDATA[<p>This example demonstrates how to execute native x86 machine code directly from .NET and, conversely, how to call a managed .NET method from that native code — all without loading any external libraries.</p>
<h3 id="heading-how-it-works">How it works</h3>
<p>The core idea is to allocate a chunk of memory, mark it as executable, and then point a delegate to it.</p>
<ol>
<li><p><strong>Get a Pointer to Managed Code:</strong> We use <code>MethodHandle.GetFunctionPointer()</code> to get the memory address of <code>Math.Max</code>.</p>
</li>
<li><p><strong>Allocate Executable Memory:</strong> Standard .NET memory is not executable for security reasons (DEP). We use the Win32 API <code>VirtualAlloc</code> to request a region with <code>ExecuteReadWrite</code> permissions.</p>
</li>
<li><p><strong>Calculate Relative Offsets:</strong> The x86 <code>CALL</code> instruction (0xE8) uses relative addressing. We calculate the distance between our native buffer and the target <code>Math.Max</code> function.</p>
</li>
<li><p><strong>Write Shellcode:</strong> We manually write the x86 opcodes into the allocated memory.</p>
</li>
<li><p><strong>Execute via Delegate:</strong> <code>Marshal.GetDelegateForFunctionPointer</code> wraps our raw memory address into a callable .NET delegate.</p>
</li>
</ol>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Reflection;
<span class="hljs-keyword">using</span> System.Runtime.InteropServices;
<span class="hljs-keyword">using</span> System.Text;

<span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
    [<span class="hljs-meta">UnmanagedFunctionPointer(CallingConvention.Cdecl)</span>]
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">delegate</span> <span class="hljs-keyword">ulong</span> <span class="hljs-title">LongFunction</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> arg1, <span class="hljs-keyword">uint</span> arg2</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span>
    {
        <span class="hljs-comment">// 1. Get the address of the managed method Math.Max(uint, uint)</span>
        MethodInfo methodInfo = <span class="hljs-keyword">typeof</span>(Math).GetMethod(<span class="hljs-string">"Max"</span>,
            BindingFlags.Static | BindingFlags.Public, <span class="hljs-literal">null</span>,
            <span class="hljs-keyword">new</span> Type[] { <span class="hljs-keyword">typeof</span>(<span class="hljs-keyword">uint</span>), <span class="hljs-keyword">typeof</span>(<span class="hljs-keyword">uint</span>) }, <span class="hljs-literal">null</span>);

        IntPtr functionPtr = methodInfo.MethodHandle.GetFunctionPointer();
        Console.WriteLine(<span class="hljs-string">$"Math.Max address: 0x<span class="hljs-subst">{functionPtr.ToInt64():X}</span>"</span>);

        <span class="hljs-comment">// 2. Allocate executable memory (x86 architecture)</span>
        IntPtr region = VirtualAlloc(IntPtr.Zero, (UIntPtr)<span class="hljs-number">4096</span>,
            AllocationType.Commit, MemoryProtection.ExecuteReadWrite);

        <span class="hljs-comment">// 3. Calculate relative address for the 'call' instruction</span>
        <span class="hljs-comment">// The offset is: TargetAddress - (CurrentInstructionAddress + InstructionLength)</span>
        <span class="hljs-comment">// 14 is the offset from the start of our code to the instruction following 'call'</span>
        <span class="hljs-keyword">uint</span> relativeAddr = (<span class="hljs-keyword">uint</span>)functionPtr - (<span class="hljs-keyword">uint</span>)region - <span class="hljs-number">14</span>;
        <span class="hljs-keyword">byte</span>[] addrBytes = BitConverter.GetBytes(relativeAddr);

        <span class="hljs-comment">// 4. Prepare x86 machine code: f(x, y) = max(x, y) * max(x, y)</span>
        <span class="hljs-keyword">byte</span>[] code = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[]
        {
            <span class="hljs-number">0x55</span>,                         <span class="hljs-comment">// push ebp</span>
            <span class="hljs-number">0x89</span>, <span class="hljs-number">0xE5</span>,                   <span class="hljs-comment">// mov ebp, esp</span>
            <span class="hljs-number">0x8B</span>, <span class="hljs-number">0x4D</span>, <span class="hljs-number">0x08</span>,             <span class="hljs-comment">// mov ecx, [ebp + 8]  (arg1)</span>
            <span class="hljs-number">0x8B</span>, <span class="hljs-number">0x55</span>, <span class="hljs-number">0x0C</span>,             <span class="hljs-comment">// mov edx, [ebp + 12] (arg2)</span>
            <span class="hljs-number">0xE8</span>, addrBytes[<span class="hljs-number">0</span>], addrBytes[<span class="hljs-number">1</span>], addrBytes[<span class="hljs-number">2</span>], addrBytes[<span class="hljs-number">3</span>], <span class="hljs-comment">// call Math.Max</span>
            <span class="hljs-number">0xF7</span>, <span class="hljs-number">0xE0</span>,                   <span class="hljs-comment">// mul eax (square the result of Max)</span>
            <span class="hljs-number">0x5D</span>,                         <span class="hljs-comment">// pop ebp</span>
            <span class="hljs-number">0xC3</span>                          <span class="hljs-comment">// ret</span>
        };

        <span class="hljs-comment">// 5. Copy code to allocated memory and create a delegate</span>
        Marshal.Copy(code, <span class="hljs-number">0</span>, region, code.Length);
        Console.WriteLine(<span class="hljs-string">$"Native code memory: 0x<span class="hljs-subst">{region.ToInt64():X}</span>"</span>);

        LongFunction func = (LongFunction)Marshal.GetDelegateForFunctionPointer(region, <span class="hljs-keyword">typeof</span>(LongFunction));

        <span class="hljs-comment">// 6. Execute the magic</span>
        <span class="hljs-keyword">ulong</span> result = func(<span class="hljs-number">0x10</span>, <span class="hljs-number">0x20</span>); <span class="hljs-comment">// max(16, 32)^2 = 32^2 = 1024</span>
        Console.WriteLine(<span class="hljs-string">$"Result: <span class="hljs-subst">{result}</span>"</span>);

        <span class="hljs-comment">// Cleanup</span>
        VirtualFree(region, UIntPtr.Zero, ReleaseType.Release);
    }

    [<span class="hljs-meta">DllImport(<span class="hljs-meta-string">"kernel32.dll"</span>, SetLastError = true)</span>]
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">extern</span> IntPtr <span class="hljs-title">VirtualAlloc</span>(<span class="hljs-params">IntPtr address, UIntPtr length, AllocationType allocationType, MemoryProtection memoryProtection</span>)</span>;

    [<span class="hljs-meta">DllImport(<span class="hljs-meta-string">"kernel32.dll"</span>, SetLastError = true)</span>]
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">extern</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">VirtualFree</span>(<span class="hljs-params">IntPtr address, UIntPtr length, ReleaseType releaseType</span>)</span>;

    [<span class="hljs-meta">Flags</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> AllocationType : <span class="hljs-keyword">uint</span> 
    { 
        Commit = <span class="hljs-number">0x1000</span> 
    }

    [<span class="hljs-meta">Flags</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> ReleaseType : <span class="hljs-keyword">uint</span> 
    { 
        Release = <span class="hljs-number">0x8000</span>
    }

    [<span class="hljs-meta">Flags</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> MemoryProtection : <span class="hljs-keyword">uint</span> 
    { 
        ExecuteReadWrite = <span class="hljs-number">0x40</span> 
    }
}
</code></pre>
<h3 id="heading-important-considerations">Important Considerations</h3>
<ul>
<li><p><strong>Architecture:</strong> This specific shellcode is <strong>x86 (32-bit)</strong>. It will crash on x64 due to different calling conventions and register sizes (e.g., <code>RAX</code> instead of <code>EAX</code>).</p>
</li>
<li><p><strong>JIT Inlining:</strong> In a real-world scenario, the JIT compiler might inline <code>Math.Max</code>, or its function pointer might change. For a stable "managed-to-native" bridge, you usually use <code>RuntimeHelpers.PrepareMethod</code>.</p>
</li>
<li><p><strong>Security:</strong> Writing to executable memory is a common technique in exploits. Modern systems with <strong>Arbitrary Code Execution (ACE)</strong> protection might block such operations.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Pure Managed Code and AccessViolationException]]></title><description><![CDATA[Be careful with explicit struct layout. Even when executing purely managed code, it is possible to trigger an AccessViolationException. Before I encountered this case, I was under the impression that the CLR (Common Language Runtime) prevented such m...]]></description><link>https://blog.antonr.net/pure-managed-code-and-accessviolationexception</link><guid isPermaLink="true">https://blog.antonr.net/pure-managed-code-and-accessviolationexception</guid><category><![CDATA[C#]]></category><category><![CDATA[struct]]></category><category><![CDATA[layout]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Tue, 13 Nov 2012 10:00:00 GMT</pubDate><content:encoded><![CDATA[<p><strong>Be careful with explicit struct layout.</strong> Even when executing purely managed code, it is possible to trigger an <code>AccessViolationException</code>. Before I encountered this case, I was under the impression that the CLR (Common Language Runtime) prevented such memory corruption in a managed environment.</p>
<h3 id="heading-the-problem">The Problem</h3>
<p>By using <code>[StructLayout(LayoutKind.Explicit)]</code>, you can overlap fields of different reference types at the same memory offset. In the example below, both a <code>string</code> and a <code>StringBuilder</code> occupy the same memory address:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Runtime.InteropServices;
<span class="hljs-keyword">using</span> System.Text;

[<span class="hljs-meta">StructLayout(LayoutKind.Explicit)</span>]
<span class="hljs-keyword">struct</span> MyStruct
{
    [<span class="hljs-meta">FieldOffset(0)</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Str;

    [<span class="hljs-meta">FieldOffset(0)</span>]
    <span class="hljs-keyword">public</span> StringBuilder Builder;
}

<span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
    <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>)</span>
    {
        MyStruct mystery = <span class="hljs-keyword">new</span> MyStruct();

        <span class="hljs-comment">// We assign a string reference to the memory location</span>
        mystery.Str = <span class="hljs-string">"hello"</span>;
        Console.WriteLine(<span class="hljs-string">$"String value: <span class="hljs-subst">{mystery.Str}</span>"</span>);

        <span class="hljs-comment">// Here is the catch: we treat that same memory location as a StringBuilder</span>
        StringBuilder sb = mystery.Builder;

        <span class="hljs-comment">// AccessViolationException! </span>
        <span class="hljs-comment">// The runtime tries to call a StringBuilder method on a String object's memory.</span>
        sb.Append(<span class="hljs-string">"123"</span>); 
    }
}
</code></pre>
<h3 id="heading-why-does-this-happen">Why does this happen?</h3>
<p>When you set <code>FieldOffset(0)</code> for both fields, you are essentially performing an unsafe cast without using the <code>unsafe</code> keyword.</p>
<ol>
<li><p>The variable <code>sb</code> now holds a reference to a memory block that is actually a <code>System.String</code>.</p>
</li>
<li><p>When you call <code>.Append()</code>, the CLR attempts to access the internal fields of what it <em>thinks</em> is a <code>StringBuilder</code> (like its internal buffer or capacity).</p>
</li>
<li><p>Since the internal memory structure of a <code>String</code> is completely different, the CPU tries to read or write to an invalid memory address, resulting in an <code>AccessViolationException</code>.</p>
</li>
</ol>
<h3 id="heading-summary">Summary</h3>
<p>While the CLR provides a "sandbox" for managed code, <code>LayoutKind.Explicit</code> is a powerful and potentially dangerous tool. It allows you to break the type system, leading to low-level crashes that are usually associated with C++ or unmanaged code.</p>
<p><strong>Rule of thumb:</strong> Never overlap reference types in an explicit layout unless you are absolutely sure of the memory implications.</p>
]]></content:encoded></item><item><title><![CDATA[Non-blocking Queue]]></title><description><![CDATA[It is no secret that thread synchronization mechanisms in multithreaded applications consume a lot of resources. In .NET, starting with version 3.5, some synchronization primitives were introduced that take into account the fact that calls to Windows...]]></description><link>https://blog.antonr.net/non-blocking-queue</link><guid isPermaLink="true">https://blog.antonr.net/non-blocking-queue</guid><category><![CDATA[C#]]></category><category><![CDATA[lock-free]]></category><category><![CDATA[algorithms]]></category><dc:creator><![CDATA[Anton Ryzhov]]></dc:creator><pubDate>Sat, 15 Sep 2012 09:00:00 GMT</pubDate><content:encoded><![CDATA[<p>It is no secret that thread synchronization mechanisms in multithreaded applications consume a lot of resources. In .NET, starting with version 3.5, some synchronization primitives were introduced that take into account the fact that calls to Windows system functions are very expensive. For example, the <code>ReaderWriterLockSlim</code> class is a replacement for <code>ReaderWriterLock</code>. The <code>ReaderWriterLockSlim</code> class uses a simple spin loop with checks and only after some time does it fall back to calling Windows functions. Despite 100% CPU core utilization during spinning, this implementation is considered more efficient for tasks where locking and thread waiting are short-lived.</p>
<p>In .NET 4, thread-safe collections such as <code>ConcurrentQueue</code>, <code>ConcurrentDictionary</code>, and <code>ConcurrentHashSet</code> appeared. These collections use locks, and therefore their use is considered inefficient.</p>
<p>When building non-blocking collections, several points must be taken into account. All changes performed by a thread must be atomic. If the changes are not atomic, there must be some atomic initiating action that allows threads, first, to distinguish their own changes from those made by other threads, and second, to compete for the right to perform an operation on the collection. Such an atomic action can be changing the value of an integer variable, for example, from 0 to a unique thread identifier. In this case, another thread, after checking the value of the variable, can decide whether to wait or to perform an operation on the collection.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">while</span> (syncVar != threadId)
    <span class="hljs-keyword">if</span> (syncVar == <span class="hljs-number">0</span>)
        syncVar = threadId;
... <span class="hljs-comment">// perform operation</span>
syncVar = <span class="hljs-number">0</span>;
</code></pre>
<p>The problem here is that checking and setting the value must be an atomic operation. That is, if you write it directly as in the code above, there is no guarantee that the system will not preempt the thread between the check and the assignment.</p>
<p>Fortunately, Intel IA32 and AMD64 architectures provide the <code>CMPXCHG</code> instruction, which performs a comparison and assignment if the comparison succeeds. When used with the <code>LOCK</code> prefix, it executes atomically, guaranteeing that no other thread can access the same memory location. The equivalent of this instruction in .NET is the <code>Interlocked.CompareExchange</code> method, which is translated by JIT and AOT compilers into the same <code>CMPXCHG</code> instruction with the <code>LOCK</code> prefix. Using <code>Interlocked.CompareExchange</code>, the code above would look like this.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">while</span> (syncVar != threadId)
    Interlocked.CompareExchange(<span class="hljs-keyword">ref</span> syncVar, threadId, <span class="hljs-number">0</span>); <span class="hljs-comment">// compare with 0 and replace with threadId if syncVar == 0</span>
... <span class="hljs-comment">// perform operation</span>
syncVar = <span class="hljs-number">0</span>;
</code></pre>
<p>Now we can proceed to building a non-blocking queue. To do this, we define a class representing a queue element, which will store the value and a reference to the next element.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">class</span> <span class="hljs-title">QueueItem</span>
{
    <span class="hljs-keyword">public</span> T Value;
    <span class="hljs-keyword">public</span> QueueItem Next;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">QueueItem</span>(<span class="hljs-params"></span>)</span>
    {
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">QueueItem</span>(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span>
    {
        Value = <span class="hljs-keyword">value</span>;
    }
}
</code></pre>
<p>In the queue, we need to create one empty element so that when adding and removing elements we do not need to perform extra null checks (when the queue is empty or becomes empty).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">LockFreeQueue</span>&lt;<span class="hljs-title">T</span>&gt;
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> _count;
    <span class="hljs-keyword">private</span> QueueItem _head;
    <span class="hljs-keyword">private</span> QueueItem _tail;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">LockFreeQueue</span>(<span class="hljs-params"></span>)</span>
    {
        _head = _tail = <span class="hljs-keyword">new</span> QueueItem(); <span class="hljs-comment">// empty element</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Enqueue</span>(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span>
    {
        QueueItem item = <span class="hljs-keyword">new</span> QueueItem(<span class="hljs-keyword">value</span>);
        <span class="hljs-comment">// TODO</span>
    }
}
</code></pre>
<p>For thread synchronization, the <code>_</code><a target="_blank" href="http://tail.Next"><code>tail.Next</code></a> variable will be used—the reference to the next element in the queue after the tail. In the normal state, it is <code>null</code>, since <code>_tail</code> points to the end of the queue. Also, each thread entering the <code>Enqueue</code> method will create its own local instance of the <code>QueueItem</code> class; this object can be used instead of a thread identifier. The <code>Interlocked.CompareExchange</code> method returns the value of the first argument if it was not changed, or the previous value of the first argument if it was changed. This previous value will be <code>null</code> in the state where no thread has yet entered the competition to add an element. Thus, we can write a check and a contention loop.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Enqueue</span>(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span>
{
    QueueItem item = <span class="hljs-keyword">new</span> QueueItem(<span class="hljs-keyword">value</span>);
    <span class="hljs-keyword">while</span> (Interlocked.CompareExchange(<span class="hljs-keyword">ref</span> _tail.Next, item, <span class="hljs-literal">null</span>) != <span class="hljs-literal">null</span>) ;
    <span class="hljs-comment">// TODO</span>
}
</code></pre>
<p>However, this is not all. After the condition in the loop becomes false, this means that the current thread has won the contention, and the value in <code>_</code><a target="_blank" href="http://tail.Next"><code>tail.Next</code></a> has been successfully replaced with <code>item</code>. To complete the add operation, we need to move the queue tail to the new element and increment the counter.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Enqueue</span>(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span>
{
    QueueItem item = <span class="hljs-keyword">new</span> QueueItem(<span class="hljs-keyword">value</span>);
    <span class="hljs-keyword">while</span> (Interlocked.CompareExchange(<span class="hljs-keyword">ref</span> _tail.Next, item, <span class="hljs-literal">null</span>) != <span class="hljs-literal">null</span>) ;
    _tail = item;
    Interlocked.Increment(<span class="hljs-keyword">ref</span> _count);
}
</code></pre>
<p>The operation of adding an element to the queue is now implemented.</p>
<p>Now for removal. Since elements are added at the end and removed from the beginning, the add operation will not interfere with the remove operation. In removal, we need to check the case when the queue is empty, that is, when <code>_tail</code> equals <code>_head</code>.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">Dequeue</span>(<span class="hljs-params"><span class="hljs-keyword">out</span> T <span class="hljs-keyword">value</span></span>)</span>
{
    <span class="hljs-keyword">if</span> (_tail != _head)
    {
        <span class="hljs-comment">// TODO</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
    <span class="hljs-keyword">value</span> = <span class="hljs-keyword">default</span>(T);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
</code></pre>
<p>We need to save the head of the queue, compare it with the tail, and move the head of the queue forward to the next element. All of this must be done in a loop, because some other thread may remove an element in the middle of the removal operation in the current thread. Moving the head of the queue forward to the next element must be done using <code>Interlocked.CompareExchange</code>, since we need to be sure that the head value was not changed by another thread. We obtain the final version of the element removal method.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">Dequeue</span>(<span class="hljs-params"><span class="hljs-keyword">out</span> T <span class="hljs-keyword">value</span></span>)</span>
{
    <span class="hljs-keyword">bool</span> success = <span class="hljs-literal">false</span>;
    QueueItem item = _head;
    <span class="hljs-keyword">while</span> (!success)
    {
        item = _head;
        <span class="hljs-keyword">if</span> (item == _tail)
        {
            <span class="hljs-keyword">value</span> = <span class="hljs-keyword">default</span>(T);
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }
        success = Interlocked.CompareExchange(<span class="hljs-keyword">ref</span> _head, item.Next, item) == item;
    }
    Interlocked.Decrement(<span class="hljs-keyword">ref</span> _count);
    <span class="hljs-keyword">value</span> = item.Next.Value;
    item.Next.Value = <span class="hljs-keyword">default</span>(T);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<p>Such a queue is efficient in a multithreaded application and does not use additional resources. In the worst case, when N threads contend in the add and remove procedures, each thread goes through N−1 iterations, which corresponds to 2 × (N−1) CPU instructions in the case of adding an element and 5 × (N−1) CPU instructions in the case of removing an element. This is negligible compared to calling WinAPI functions.</p>
]]></content:encoded></item></channel></rss>