<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://cognosnotes.com/blog</id>
    <title>Cognos Notes Blog</title>
    <updated>2026-07-31T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://cognosnotes.com/blog"/>
    <subtitle>Cognos Notes Blog</subtitle>
    <icon>https://cognosnotes.com/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[pop_range Addendum: Revisions from Reviewer Feedback]]></title>
        <id>https://cognosnotes.com/blog/cpp-pop-range-addendum</id>
        <link href="https://cognosnotes.com/blog/cpp-pop-range-addendum"/>
        <updated>2026-07-31T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This document supplements, rather than replaces, the [original]]></summary>
        <content type="html"><![CDATA[<p>This document supplements, rather than replaces, the <a class="" href="https://cognosnotes.com/blog/cpp-pop-range">original
<code>pop_range</code> design document</a>. It covers three points a reviewer raised
after that document was published, the verification done for each, the
resulting code changes, and the measured before/after behavior. The
original document's design rationale, history section, and prior-art
research are unchanged and still apply; only the three areas below have
moved since that write-up.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="0-context">0. Context<a href="https://cognosnotes.com/blog/cpp-pop-range-addendum#0-context" class="hash-link" aria-label="Direct link to 0. Context" title="Direct link to 0. Context" translate="no">​</a></h2>
<p>The original <code>pop_view&lt;A&gt;::operator*</code> returned a copy (<code>value_type</code>), the
supported container shape was fixed to <code>pop()</code> + <code>top()</code>/<code>front()</code> (i.e.
only <code>stack</code>, <code>queue</code>, <code>priority_queue</code>), and <code>size()</code>/<code>sized_range</code> was
deliberately left unimplemented. A reviewer pointed out that all three of
these were more conservative than necessary. Each point below was checked
independently — against the actual wording of the relevant concepts, and
against a compiled, sanitizer-clean test — before being adopted.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-operator-doesnt-need-to-copy">1. <code>operator*</code> doesn't need to copy<a href="https://cognosnotes.com/blog/cpp-pop-range-addendum#1-operator-doesnt-need-to-copy" class="hash-link" aria-label="Direct link to 1-operator-doesnt-need-to-copy" title="Direct link to 1-operator-doesnt-need-to-copy" translate="no">​</a></h2>
<p><strong>The claim.</strong> <code>pop_iterator</code> only models <code>input_iterator</code>. Nothing in
that concept requires a previous <code>*it</code> result to remain valid once the
iterator has been incremented — that guarantee belongs to
<code>forward_iterator</code>, not <code>input_iterator</code>. So <code>operator*</code> can return an
rvalue reference straight into the container instead of a copy, the same
technique <code>std::move_iterator</code> uses for the same reason.</p>
<p><strong>Why the claim holds up.</strong> The concept that actually constrains
<code>operator*</code>'s return type is <code>indirectly_readable</code>, via
<code>iter_reference_t</code>. It requires <code>common_reference_with</code> relationships
between <code>iter_reference_t</code>, <code>iter_value_t&amp;</code>, and
<code>iter_rvalue_reference_t</code> — it does not require <code>iter_reference_t</code> to be
a prvalue, and <code>std::move_iterator</code> is a standing precedent for returning
an rvalue reference and still satisfying every iterator concept it claims
to model. There's no requirement anywhere in <code>input_iterator</code>'s
definition that a dereferenced value keep being valid across <code>++</code>.</p>
<p><strong>The change:</strong></p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// before</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">value_type </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">peek</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">adaptor_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// after</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">decltype</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">auto</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">peek</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">adaptor_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>What this changes for callers.</strong> <code>auto v = *it;</code> (including a
range-based for's <code>auto v</code>) now selects <code>v</code>'s move constructor instead of
its copy constructor, whenever the underlying peek function
(<code>top()</code>/<code>front()</code>/<code>back()</code>) returns a mutable reference. <code>const auto&amp; v = *it;</code> or <code>auto&amp;&amp; v = *it;</code> read the element in place with no copy or
move at all, same as before.</p>
<p><strong>The one case that doesn't change: <code>priority_queue</code>.</strong> Its <code>top()</code> is
<code>const</code>-qualified — it has to be, since mutating the top element in place
could break the heap invariant that guarantees the top actually is the
largest (or smallest, per the comparator) element. <code>std::move()</code> applied
to a <code>const T&amp;</code> still only binds to a copy constructor (a move
constructor takes a non-<code>const</code> rvalue reference), so exactly one copy
per element remains structurally unavoidable for <code>priority_queue</code>
specifically — not a shortcoming of <code>pop_range</code>.</p>
<p><strong>Measured proof.</strong> An instrumented <code>probe</code> type (counting copies and
moves separately in its constructors) confirmed the following, with the
counters reset immediately before draining (so numbers exclude the pushes
that built the container):</p>
<table><thead><tr><th>Container</th><th>Copies</th><th>Moves</th><th>Notes</th></tr></thead><tbody><tr><td><code>stack&lt;probe&gt;</code> (5 elements)</td><td>0</td><td>5</td><td><code>top()</code> is mutable; every extraction moves</td></tr><tr><td><code>priority_queue&lt;probe&gt;</code> (4 elements)</td><td>4</td><td>18</td><td>4 copies = exactly one per element, forced by <code>top()</code> being <code>const</code>. The 18 moves come from <code>priority_queue::pop()</code>'s own internal heap-reordering (<code>std::pop_heap</code>'s sift-down) — that work happens regardless of <code>pop_range</code> and isn't something we introduced or could remove.</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-generalizing-beyond-the-three-container-adaptors">2. Generalizing beyond the three container adaptors<a href="https://cognosnotes.com/blog/cpp-pop-range-addendum#2-generalizing-beyond-the-three-container-adaptors" class="hash-link" aria-label="Direct link to 2. Generalizing beyond the three container adaptors" title="Direct link to 2. Generalizing beyond the three container adaptors" translate="no">​</a></h2>
<p><strong>The claim.</strong> The same pattern works for anything exposing <code>pop_front()</code>
and/or <code>pop_back()</code>, not just the <code>pop()</code>+<code>top()</code>/<code>front()</code> shape that
<code>stack</code>, <code>queue</code>, and <code>priority_queue</code> share — so <code>std::vector</code>,
<code>std::deque</code>, and <code>std::list</code> could be drained the same way.</p>
<p><strong>The change.</strong> The concept was widened from one recognized shape to
three, with a strict, shared priority order so that a type satisfying
more than one shape (e.g. <code>deque</code> and <code>list</code>, which have both ends)
can't have the "peek" half and the "consume" half of the code disagree
about which end is in play:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token keyword" style="color:#00009f">typename</span><span class="token plain"> </span><span class="token class-name">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">concept</span><span class="token plain"> </span><span class="token class-name">poppable_adaptor</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">requires</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">typename</span><span class="token plain"> </span><span class="token class-name">A</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">value_type</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">convertible_to</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token keyword" style="color:#00009f">bool</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_nullary_pop</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_top</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> has_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_pop_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> has_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_pop_back</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> has_back</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">decltype</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">auto</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">peek</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_nullary_pop</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> has_top</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">top</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_nullary_pop</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> has_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">front</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_pop_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> has_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">front</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain">                                                     </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">back</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">consume</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_nullary_pop</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                      a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">pop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">has_pop_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> has_front</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">pop_front</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain">                                                     a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">pop_back</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The priority order (nullary <code>pop()</code> first, then <code>pop_front()</code>, then
<code>pop_back()</code>) means: the three original adaptors are unaffected;
<code>vector</code> — which only ever has <code>pop_back()</code> — drains from the back;
<code>deque</code>/<code>list</code> — which have both — drain FIFO from the front by default,
since <code>pop_front()</code> is checked before <code>pop_back()</code>.</p>
<p><strong>Measured proof.</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">vector (back()/pop_back(), LIFO order):  5 4 3 2 1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">deque (front()/pop_front(), FIFO order): 1 2 3 4 5</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">list (front()/pop_front(), FIFO order):  1 2 3 4 5</span><br></div></code></pre></div></div>
<p>All three drained to <code>empty()</code> afterward, as expected.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-sized_range-was-more-available-than-the-original-design-assumed">3. <code>sized_range</code> was more available than the original design assumed<a href="https://cognosnotes.com/blog/cpp-pop-range-addendum#3-sized_range-was-more-available-than-the-original-design-assumed" class="hash-link" aria-label="Direct link to 3-sized_range-was-more-available-than-the-original-design-assumed" title="Direct link to 3-sized_range-was-more-available-than-the-original-design-assumed" translate="no">​</a></h2>
<p><strong>The claim.</strong> <code>sized_range</code>'s requirement that <code>ranges::size(t)</code> be
well-defined independent of evaluating <code>ranges::begin(t)</code> is scoped, in
the standard's own wording, to types that <em>also</em> model <code>forward_range</code>.
<code>pop_view</code> deliberately doesn't model <code>forward_range</code>. So that stability
requirement never actually constrains <code>pop_view</code>, and there's no real
obstacle to giving it a <code>size()</code>.</p>
<p><strong>Why the original design got this wrong.</strong> The first draft avoided
<code>size()</code> out of a blanket worry that a shrinking, mutation-sensitive
count would violate <code>sized_range</code>'s contract in some general sense. On
rereading the concept's actual definition, that concern is specifically
tied to <code>forward_range</code> — for a plain, single-pass <code>input_range</code>, there's
no such invariance clause to violate. "How many elements are left to pop
right now" is exactly the semantics <code>sized_range</code> expects from a
non-<code>forward</code> range.</p>
<p><strong>The change:</strong></p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">nodiscard</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">requires</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">requires</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">using</span><span class="token plain"> size_type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">decltype</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">declval</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name keyword" style="color:#00009f">const</span><span class="token generic-function generic class-name"> A</span><span class="token generic-function generic class-name operator" style="color:#393A34">&amp;</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> ptr_ </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">static_cast</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">size_type</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ptr_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> size_type</span><span class="token punctuation" style="color:#393A34">{</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Constrained with a <code>requires</code> clause on the member itself, so <code>pop_view&lt;A&gt;</code>
only gains <code>size()</code> — and only models <code>sized_range</code> — for an <code>A</code> that
actually has a <code>.size()</code> member (true for all of <code>stack</code>, <code>queue</code>,
<code>priority_queue</code>, <code>vector</code>, <code>deque</code>, <code>list</code>).</p>
<p><strong>Measured proof.</strong> For a <code>stack&lt;int&gt;</code> with 4 elements: <code>view.size() == 4</code> before any iteration. After manually advancing the iterator twice
(popping two elements): <code>view.size() == 2</code>. Extended the concept
<code>static_assert</code>s to confirm <code>sized_range&lt;pop_view&lt;std::stack&lt;int&gt;&gt;&gt;</code> and
<code>sized_range&lt;pop_view&lt;std::vector&lt;int&gt;&gt;&gt;</code> both hold, alongside the
pre-existing <code>input_iterator</code>/<code>input_range</code>/<code>view</code>/<code>!forward_range</code>
checks, which are unaffected.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-net-result">4. Net result<a href="https://cognosnotes.com/blog/cpp-pop-range-addendum#4-net-result" class="hash-link" aria-label="Direct link to 4. Net result" title="Direct link to 4. Net result" translate="no">​</a></h2>
<p>All three changes were verified together — not just individually — by
compiling and running the full test suite (<code>g++ 13.3.0</code>, <code>-std=c++20</code> and
<code>-std=c++23</code>, <code>-Wall -Wextra -fsanitize=address,undefined</code>) after all
three were applied: zero warnings, zero sanitizer reports, all assertions
passing, including the new copy/move counters, the three new container
types, and the two new <code>sized_range</code> <code>static_assert</code>s.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-where-to-find-the-updated-code">5. Where to find the updated code<a href="https://cognosnotes.com/blog/cpp-pop-range-addendum#5-where-to-find-the-updated-code" class="hash-link" aria-label="Direct link to 5. Where to find the updated code" title="Direct link to 5. Where to find the updated code" translate="no">​</a></h2>
<p>The revised <a href="https://cognosnotes.com/assets/files/pop_range-95585782502ff601a165a0934b69ab9a.hpp" target="_blank" class=""><code>pop_range.hpp</code></a> and its expanded <a href="https://cognosnotes.com/assets/files/main-2fd8b2fccd7d4fc576972c50164612cd.cpp" target="_blank" class=""><code>main.cpp</code></a> test suite
(reflecting all three changes above) are the versions delivered alongside
this addendum. The original design document remains the reference for
the library's overall history, motivation, and rationale sections, which
are unaffected by this round of changes.</p>]]></content>
        <author>
            <name>Austin Kim</name>
            <uri>https://blog.anthonykim.net/</uri>
        </author>
        <category label="C++" term="C++"/>
        <category label="C++20" term="C++20"/>
        <category label="C++23" term="C++23"/>
        <category label="Code" term="Code"/>
        <category label="PipeOperator" term="PipeOperator"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Extending Ranges and Views to Non-Iterable Container Adaptors]]></title>
        <id>https://cognosnotes.com/blog/cpp-pop-range</id>
        <link href="https://cognosnotes.com/blog/cpp-pop-range"/>
        <updated>2026-07-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[1. Motivation]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-motivation">1. Motivation<a href="https://cognosnotes.com/blog/cpp-pop-range#1-motivation" class="hash-link" aria-label="Direct link to 1. Motivation" title="Direct link to 1. Motivation" translate="no">​</a></h2>
<p><code>std::stack</code>, <code>std::queue</code>, and <code>std::priority_queue</code> are <em>container adaptors</em>: thin
wrappers around an underlying sequence container (<code>vector</code>, <code>deque</code>, <code>list</code>) that
expose a deliberately restricted interface — <code>push</code>, <code>pop</code>, <code>top</code>/<code>front</code>, <code>empty</code>,
<code>size</code>. None of them expose <code>begin()</code>/<code>end()</code>. This is not an oversight; it is a
design decision inherited from the original SGI STL, whose documentation states
outright that <code>priority_queue</code> "does not allow iteration through its elements."
The rationale is that the adaptor's whole contract is "you may only ever see the
current top/front element," and exposing arbitrary iteration would leak the
underlying container's storage order (<code>vector</code>'s heap layout, in the case of
<code>priority_queue</code>), which is not part of the abstraction.</p>
<p>The consequence is that these three types cannot participate in the modern
iteration and composition idioms C++ has accumulated since C++11 — range-based
<code>for</code>, and since C++20, the <code>&lt;ranges&gt;</code> view/pipe machinery — because both require
some notion of <code>begin()</code>/<code>end()</code>. This document describes <code>pop_range</code>, a small
header-only library that closes that gap by giving these adaptors a well-defined,
honestly-named <em>destructive</em> view: iterating it doesn't just read the adaptor, it
drains it, one <code>pop()</code> per increment.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-a-short-history-of-iteration-idioms-in-c">2. A Short History of Iteration Idioms in C++<a href="https://cognosnotes.com/blog/cpp-pop-range#2-a-short-history-of-iteration-idioms-in-c" class="hash-link" aria-label="Direct link to 2. A Short History of Iteration Idioms in C++" title="Direct link to 2. A Short History of Iteration Idioms in C++" translate="no">​</a></h2>
<p>It's worth being precise about which feature arrived when, since the two
headline features referenced in this document — range-based <code>for</code> and the pipe
operator — are often conflated with the wrong standard versions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-c11--the-range-based-for-loop">2.1 C++11 — the range-based <code>for</code> loop<a href="https://cognosnotes.com/blog/cpp-pop-range#21-c11--the-range-based-for-loop" class="hash-link" aria-label="Direct link to 21-c11--the-range-based-for-loop" title="Direct link to 21-c11--the-range-based-for-loop" translate="no">​</a></h3>
<p>Range-based <code>for</code> (<code>for (auto&amp; x : container) ...</code>) was introduced in C++11
(originally proposed as N2930). It desugars to roughly:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> __range </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> range_expression</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> __it </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">begin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">__range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> __end </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">__range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> __it </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> __end</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">++</span><span class="token plain">__it</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> x </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">__it</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        loop_statement</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The requirement, as originally specified, was that <code>begin(__range)</code> and
<code>end(__range)</code> produce values of the <em>same type</em>, because the loop compares
them directly with <code>operator!=</code>. This is a strict requirement: the "end" has to
be an actual iterator of the same type as "begin," not merely something
comparable to it.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-c17--generalizing-range-based-for-sentinels">2.2 C++17 — generalizing range-based <code>for</code> (sentinels)<a href="https://cognosnotes.com/blog/cpp-pop-range#22-c17--generalizing-range-based-for-sentinels" class="hash-link" aria-label="Direct link to 22-c17--generalizing-range-based-for-sentinels" title="Direct link to 22-c17--generalizing-range-based-for-sentinels" translate="no">​</a></h3>
<p>C++17 didn't introduce range-based <code>for</code> — it <em>generalized</em> it, via
<a href="https://wg21.link/P0184R0" target="_blank" rel="noopener noreferrer" class="">P0184R0, "Generalizing the Range-Based For Loop"</a>.
The requirement that <code>begin()</code> and <code>end()</code> return the same type was relaxed to
only require that they be comparable with <code>!=</code>. This is what allows an <code>end()</code>
to be a lightweight <em>sentinel</em> — a distinct type carrying no state, or carrying
just enough state to answer "are we done?" — instead of a full iterator.</p>
<p>This sounds like a small wording change, but it's the piece of infrastructure
that later made the C++20 ranges model possible: a <code>sentinel_for&lt;S, I&gt;</code> no
longer has to be an <code>I</code>. It's also directly used in this project's own
iterator: <code>pop_iterator</code>'s "end" is <code>std::default_sentinel_t</code>, a stateless tag
type, compared against the iterator via a <code>friend operator==</code>. Without the
C++17 relaxation, we'd have had to give the sentinel the same type as the
iterator (e.g., by giving <code>pop_iterator</code> a null/"end" state and constructing
two of them), which is exactly the kind of awkwardness sentinels exist to
avoid.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-c20--ranges-views-concepts-and-the-pipe-operator">2.3 C++20 — <code>&lt;ranges&gt;</code>, views, concepts, and the pipe operator<a href="https://cognosnotes.com/blog/cpp-pop-range#23-c20--ranges-views-concepts-and-the-pipe-operator" class="hash-link" aria-label="Direct link to 23-c20--ranges-views-concepts-and-the-pipe-operator" title="Direct link to 23-c20--ranges-views-concepts-and-the-pipe-operator" translate="no">​</a></h3>
<p>C++20 (via <a href="https://wg21.link/P0896R4" target="_blank" rel="noopener noreferrer" class="">P0896R4, "The One Ranges Proposal"</a>)
introduced the <code>&lt;ranges&gt;</code> library proper:</p>
<ul>
<li class="">A hierarchy of range/iterator <strong>concepts</strong> (<code>range</code>, <code>view</code>, <code>input_iterator</code>,
<code>forward_range</code>, <code>sized_range</code>, <code>common_range</code>, ...) that formally describe
<em>what guarantees</em> a given range provides, rather than just "it has begin/end."</li>
<li class=""><strong>Views</strong>: lightweight, non-owning (or cheaply-movable), lazily-evaluated
ranges, distinguished from containers by the <code>view</code> concept.</li>
<li class=""><strong>Range adaptors</strong> (<code>views::filter</code>, <code>views::transform</code>, <code>views::take</code>, ...)
and the <strong>pipe operator</strong> (<code>|</code>) for composing them: <code>data | views::filter(p) | views::transform(f)</code>.</li>
</ul>
<p>The pipe operator is not magic — it's <code>operator|</code> overloaded for a range on
the left and an <em>adaptor closure object</em> on the right, which simply calls the
adaptor with the range as its argument. Built-in adaptors like <code>views::filter</code>
have supported <code>|</code> since C++20. What C++20 did <em>not</em> provide was a public,
standard base class that lets <em>user-defined</em> adaptors opt into <code>|</code> for free —
that arrived a version later.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-c23--range_adaptor_closure-and-friends">2.4 C++23 — <code>range_adaptor_closure</code> and friends<a href="https://cognosnotes.com/blog/cpp-pop-range#24-c23--range_adaptor_closure-and-friends" class="hash-link" aria-label="Direct link to 24-c23--range_adaptor_closure-and-friends" title="Direct link to 24-c23--range_adaptor_closure-and-friends" translate="no">​</a></h3>
<p>C++23 added <code>std::ranges::range_adaptor_closure&lt;D&gt;</code>, a CRTP base class that
gives a user's own closure type the <code>|</code> operator automatically, plus
<code>ranges::to&lt;Container&gt;()</code> for materializing a view into a concrete container,
<code>views::zip</code>, and a few other conveniences. This is directly relevant here:
our own <code>views::pop</code> needed <code>operator|</code> hand-written, because targeting plain
C++20 meant <code>range_adaptor_closure</code> wasn't available (confirmed by compiling
against libstdc++ 13: the type doesn't exist under <code>-std=c++20</code>, only under
<code>-std=c++23</code>).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-summary-table">2.5 Summary table<a href="https://cognosnotes.com/blog/cpp-pop-range#25-summary-table" class="hash-link" aria-label="Direct link to 2.5 Summary table" title="Direct link to 2.5 Summary table" translate="no">​</a></h3>
<table><thead><tr><th>Year</th><th>Feature</th><th>What it enabled</th></tr></thead><tbody><tr><td>C++11</td><td>Range-based <code>for</code></td><td>Iterate any type with matching <code>begin()</code>/<code>end()</code></td></tr><tr><td>C++17</td><td>Generalized range-based <code>for</code> (P0184)</td><td><code>begin()</code>/<code>end()</code> may differ in type → sentinels</td></tr><tr><td>C++20</td><td><code>&lt;ranges&gt;</code>, views, concepts, adaptors, <code>|</code></td><td>Lazy composition of standard containers/views</td></tr><tr><td>C++23</td><td><code>range_adaptor_closure</code>, <code>ranges::to</code>, <code>zip</code></td><td><code>|</code> support for <em>user-defined</em> adaptors, for free</td></tr></tbody></table>
<p>The common thread: every one of these features assumes the underlying type
already exposes <em>some</em> notion of iteration. <code>stack</code>, <code>queue</code>, and
<code>priority_queue</code> are conspicuous holdouts — by design, not oversight — which is
the gap <code>pop_range</code> targets.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-design-goals">3. Design Goals<a href="https://cognosnotes.com/blog/cpp-pop-range#3-design-goals" class="hash-link" aria-label="Direct link to 3. Design Goals" title="Direct link to 3. Design Goals" translate="no">​</a></h2>
<ol>
<li class=""><strong>Honesty about semantics.</strong> Iterating must visibly and unambiguously
consume the adaptor. The name <code>pop</code> was chosen specifically so nobody
mistakes this for a peek-only, repeatable view.</li>
<li class=""><strong>Minimal surface, maximal reuse.</strong> One adaptor should work for <code>stack</code>,
<code>queue</code>, <code>priority_queue</code>, and any user type with the same shape — via a
concept, not three copy-pasted specializations.</li>
<li class=""><strong>Correct concept membership.</strong> The result should satisfy exactly the
concepts its behavior supports: <code>input_range</code> and <code>view</code>, and <em>not</em>
<code>forward_range</code>, because a <code>forward_range</code> is required to let you traverse
the same elements more than once, which is structurally impossible here.</li>
<li class=""><strong>Interoperability.</strong> It must compose with existing standard range
adaptors (<code>views::take</code>, <code>views::filter</code>, ranges-based algorithms, etc.)
without special-casing.</li>
<li class=""><strong>Two ownership modes.</strong> Adapting an existing, named container adaptor
in place (<code>pop(my_stack)</code>), and adapting a temporary that the view itself
should own (<code>pop(make_stack())</code>), so the pattern is convenient at a call
site as well as reusable on an existing variable.</li>
<li class=""><strong>Plain C++20 portability</strong>, with graceful use of C++23 facilities where
available, rather than a hard C++23 requirement.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-implementation">4. Implementation<a href="https://cognosnotes.com/blog/cpp-pop-range#4-implementation" class="hash-link" aria-label="Direct link to 4. Implementation" title="Direct link to 4. Implementation" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-the-adaptor-concept">4.1 The adaptor concept<a href="https://cognosnotes.com/blog/cpp-pop-range#41-the-adaptor-concept" class="hash-link" aria-label="Direct link to 4.1 The adaptor concept" title="Direct link to 4.1 The adaptor concept" translate="no">​</a></h3>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token keyword" style="color:#00009f">typename</span><span class="token plain"> </span><span class="token class-name">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">concept</span><span class="token plain"> </span><span class="token class-name">poppable_adaptor</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">requires</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">typename</span><span class="token plain"> </span><span class="token class-name">A</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">value_type</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">convertible_to</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token keyword" style="color:#00009f">bool</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">pop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">requires</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">top</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">requires</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">front</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>This is the entire "interface requirement." It's deliberately structural
(duck-typed via <code>requires</code>), not a <code>stack</code>/<code>queue</code>/<code>priority_queue</code>
allow-list, so any type shaped like a container adaptor — including
user-defined ones — is automatically supported. A small helper dispatches to
whichever of <code>top()</code>/<code>front()</code> the type actually has:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">decltype</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">auto</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">peek</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">requires</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">top</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">top</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">front</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-the-iterator-single-pass-destructive-forward-only-in-motion">4.2 The iterator: single-pass, destructive, forward-only-in-motion<a href="https://cognosnotes.com/blog/cpp-pop-range#42-the-iterator-single-pass-destructive-forward-only-in-motion" class="hash-link" aria-label="Direct link to 4.2 The iterator: single-pass, destructive, forward-only-in-motion" title="Direct link to 4.2 The iterator: single-pass, destructive, forward-only-in-motion" translate="no">​</a></h3>
<p><code>pop_iterator&lt;A&gt;</code> models <code>std::input_iterator</code> — the weakest of the standard
iterator concepts, and the only choice here. Key points:</p>
<ul>
<li class=""><code>operator*()</code> returns a <strong>copy</strong> of the element (<code>value_type</code>, not a
reference). <code>top()</code>/<code>front()</code> return a reference <em>into</em> the adaptor's
storage, which becomes dangling the instant <code>pop()</code> runs — so the iterator
must copy out the value before it can be safely incremented.</li>
<li class=""><code>operator++()</code> calls <code>A::pop()</code> and then checks <code>empty()</code>; once empty, the
iterator's internal pointer is set to <code>nullptr</code>, which is also the "end"
state.</li>
<li class="">Comparison against the end is against <code>std::default_sentinel_t</code> — a
stateless tag from <code>&lt;iterator&gt;</code> — rather than another <code>pop_iterator</code>. This
is the sentinel model that C++17's relaxation of range-based <code>for</code> made
legal, and which C++20 ranges formalize via the <code>sentinel_for</code> concept.</li>
<li class=""><code>operator++(int)</code> (postfix) returns <code>void</code>. This looks unusual coming from
classic STL iterators, but it's conformant: <code>std::weakly_incrementable</code>
(which every iterator concept builds on) only requires postfix <code>++</code> to be a
valid expression, not that it return anything in particular — a carve-out
that exists precisely to accommodate input iterators like this one, which
have nothing meaningful to return a copy of after the value has been
consumed.</li>
</ul>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">pop_iterator</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">using</span><span class="token plain"> value_type       </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">typename</span><span class="token plain"> </span><span class="token class-name">A</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">value_type</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">using</span><span class="token plain"> difference_type  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">ptrdiff_t</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">using</span><span class="token plain"> iterator_concept </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">input_iterator_tag</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">pop_iterator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">explicit</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pop_iterator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">noexcept</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">adaptor_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor_ </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> adaptor_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> adaptor_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    value_type </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">peek</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">adaptor_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pop_iterator</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        adaptor_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">pop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> adaptor_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">friend</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">bool</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">==</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pop_iterator</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">default_sentinel_t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">noexcept</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">adaptor_ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">private</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    A</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> adaptor_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-the-view-reference-or-ownership-stable-across-moves">4.3 The view: reference or ownership, stable across moves<a href="https://cognosnotes.com/blog/cpp-pop-range#43-the-view-reference-or-ownership-stable-across-moves" class="hash-link" aria-label="Direct link to 4.3 The view: reference or ownership, stable across moves" title="Direct link to 4.3 The view: reference or ownership, stable across moves" translate="no">​</a></h3>
<p><code>pop_view&lt;A&gt;</code> derives from <code>std::ranges::view_interface&lt;pop_view&lt;A&gt;&gt;</code> (for the
usual free functions like <code>empty()</code>/<code>front()</code> that <code>view_interface</code> derives
from <code>begin()</code>/<code>end()</code>), and supports two constructors:</p>
<ul>
<li class=""><code>pop_view(A&amp;)</code> — adapt an existing, named adaptor. The caller keeps
ownership and must keep it alive for as long as the view/iterators are
used (an ordinary borrowing convention, same as <code>std::span</code>).</li>
<li class=""><code>pop_view(A&amp;&amp;)</code> — take ownership of a temporary. The adaptor is moved into
a <code>std::unique_ptr&lt;A&gt;</code>, specifically so that its address stays stable even
if the <em>view itself</em> is later moved — iterators hold a raw pointer to the
adaptor, not to the view, so a <code>unique_ptr</code> indirection is what prevents
those iterators from dangling across a view move.</li>
</ul>
<p>The view is move-only. It deliberately has no copy constructor: copying it
would either have to deep-copy the entire underlying container (surprising
and expensive for something that looks like a lightweight view) or alias the
same adaptor from two independent views that would then race to <code>pop()</code> it.
Move-only is the only model that doesn't quietly misbehave.</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">pop_view</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="color:#00009f">public</span><span class="token base-clause"> std</span><span class="token base-clause double-colon punctuation" style="color:#393A34">::</span><span class="token base-clause">ranges</span><span class="token base-clause double-colon punctuation" style="color:#393A34">::</span><span class="token base-clause class-name">view_interface</span><span class="token base-clause operator" style="color:#393A34">&lt;</span><span class="token base-clause class-name">pop_view</span><span class="token base-clause operator" style="color:#393A34">&lt;</span><span class="token base-clause class-name">A</span><span class="token base-clause operator" style="color:#393A34">&gt;&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">pop_view</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">explicit</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pop_view</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">noexcept</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ptr_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">explicit</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pop_view</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">owned_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">make_unique</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">A</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ptr_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">owned_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">pop_view</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pop_view</span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pop_view</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pop_view</span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">pop_view</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pop_view</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">delete</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pop_view</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pop_view</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">delete</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pop_iterator</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">begin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">pop_iterator</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">A</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ptr_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">default_sentinel_t </span><span class="token function" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">noexcept</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">default_sentinel</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">nodiscard</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">bool</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">noexcept</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> ptr_ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> ptr_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">private</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">unique_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> owned_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    A</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> ptr_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p><code>pop_view</code> is explicitly opted into <code>enable_view</code>:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token keyword" style="color:#00009f">typename</span><span class="token plain"> </span><span class="token class-name">A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">inline</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">bool</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">ranges</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">enable_view</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">pop_range</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">pop_view</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>and is <em>not</em> a <code>forward_range</code> — this falls out naturally, since
<code>pop_iterator</code> only satisfies <code>input_iterator</code>, and <code>forward_range</code> requires
<code>forward_iterator</code>, which in turn requires that copies of the iterator be
independently incrementable and still compare equal to each other where
appropriate — a guarantee that's fundamentally incompatible with "advancing
consumes the shared, singular underlying container."</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-viewspop-call-syntax-and-pipe-syntax-c20-portable">4.4 <code>views::pop</code>: call syntax and pipe syntax, C++20-portable<a href="https://cognosnotes.com/blog/cpp-pop-range#44-viewspop-call-syntax-and-pipe-syntax-c20-portable" class="hash-link" aria-label="Direct link to 44-viewspop-call-syntax-and-pipe-syntax-c20-portable" title="Direct link to 44-viewspop-call-syntax-and-pipe-syntax-c20-portable" translate="no">​</a></h3>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token class-name">pop_fn</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">nodiscard</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">pop_view</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">A</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">nodiscard</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">pop_view</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">A</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">inline</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">constexpr</span><span class="token plain"> pop_fn pop</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">nodiscard</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">|</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> adaptor</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> pop_fn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">pop_view</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">A</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">template</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">poppable_adaptor A</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">nodiscard</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">operator</span><span class="token operator" style="color:#393A34">|</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">A</span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> adaptor</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> pop_fn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">pop_view</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">A</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adaptor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>As noted in §2.4, <code>std::ranges::range_adaptor_closure</code> would give <code>|</code> "for
free," but it's a C++23 addition; verified directly by compiling a
minimal check against libstdc++ 13 under both standards:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">$ g++ -std=c++20 -fsyntax-only check.cpp   # error: 'range_adaptor_closure' does not name a type</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">$ g++ -std=c++23 -fsyntax-only check.cpp   # compiles</span><br></div></code></pre></div></div>
<p>Hand-writing <code>operator|</code> sidesteps that dependency entirely, so the library
works unmodified under <code>-std=c++20</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-semantics-and-guarantees">5. Semantics and Guarantees<a href="https://cognosnotes.com/blog/cpp-pop-range#5-semantics-and-guarantees" class="hash-link" aria-label="Direct link to 5. Semantics and Guarantees" title="Direct link to 5. Semantics and Guarantees" translate="no">​</a></h2>
<table><thead><tr><th>Property</th><th>Value</th><th>Why</th></tr></thead><tbody><tr><td>Concept satisfied</td><td><code>input_range</code>, <code>view</code></td><td>Single-pass, destructive traversal</td></tr><tr><td>Concept explicitly <strong>not</strong> satisfied</td><td><code>forward_range</code>, <code>common_range</code></td><td>Re-traversal is impossible; <code>begin()</code>/<code>end()</code> types differ</td></tr><tr><td>Dereference</td><td>by value (<code>value_type</code>)</td><td><code>top()</code>/<code>front()</code> reference dangles after <code>pop()</code></td></tr><tr><td>Side effect of <code>++it</code></td><td>one <code>A::pop()</code></td><td>The defining behavior of the library</td></tr><tr><td>Copyable?</td><td>View: no. Iterator: yes (cheap, just a raw pointer).</td><td>Copying the view would duplicate or alias the container</td></tr><tr><td>Empty container</td><td><code>begin() == end()</code> immediately</td><td>No special-casing needed at call sites</td></tr><tr><td>Composability</td><td>Works with any standard range adaptor (<code>views::take</code>, algorithms, etc.)</td><td>Only requires <code>input_range</code></td></tr></tbody></table>
<p>A worked example of composability: <code>pop(pq) | std::views::take(2)</code> pops
exactly two elements and stops — the other three elements are left
completely untouched in the underlying <code>priority_queue</code>, confirmed by
checking <code>pq.size()</code> afterward. This is only possible because <code>views::take</code>
only asks its underlying range for <code>input_range</code>, which <code>pop_view</code> genuinely
provides — nothing in the pipeline over-reads or over-pops beyond what was
requested.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-testing">6. Testing<a href="https://cognosnotes.com/blog/cpp-pop-range#6-testing" class="hash-link" aria-label="Direct link to 6. Testing" title="Direct link to 6. Testing" translate="no">​</a></h2>
<p>The header (<a href="https://cognosnotes.com/assets/files/pop_range-ee6ea877e211f07918a767e234c4fb5c.hpp" target="_blank" class=""><code>pop_range.hpp</code></a>) and a demo/test program (<a href="https://cognosnotes.com/assets/files/main-2a953d65ac580198a315b3938d469e3f.cpp" target="_blank" class=""><code>main.cpp</code></a>) were
compiled and run with <code>g++ 13.3.0</code>:</p>
<ul>
<li class=""><code>-std=c++20</code> and <code>-std=c++23</code>, both with <code>-Wall -Wextra</code>, zero warnings.</li>
<li class=""><code>-fsanitize=address,undefined</code> for the full test run — no ASan/UBSan
reports.</li>
<li class="">Coverage: <code>stack</code> (LIFO order), <code>queue</code> (FIFO order), <code>priority_queue</code>
(descending heap order), pipe syntax, composition with <code>views::take</code>
(verifying partial-drain leaves the remainder untouched), ownership of an
rvalue-returned adaptor, <code>std::ranges::fold_left</code> over the view, an empty
container producing <code>begin() == end()</code>, and <code>static_assert</code>s pinning down
the exact concept membership (<code>input_iterator</code> yes, <code>input_range</code> yes,
<code>view</code> yes, <code>forward_range</code> no).</li>
<li class="">A second, header-only smoke test confirmed the library compiles and runs
correctly under plain <code>-std=c++20</code> with no <code>-std=c++23</code> dependency.</li>
</ul>
<p>All cases passed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-prior-art">7. Prior Art<a href="https://cognosnotes.com/blog/cpp-pop-range#7-prior-art" class="hash-link" aria-label="Direct link to 7. Prior Art" title="Direct link to 7. Prior Art" translate="no">​</a></h2>
<p>No published library implementing this exact pattern was found. The closest
related material:</p>
<ul>
<li class="">The C++ standard library's own documentation is explicit that
<code>priority_queue</code> "does not allow iteration through its elements" — the
restriction this project works around is by design, not a gap anyone
considers a bug.</li>
<li class=""><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1425r4.pdf" target="_blank" rel="noopener noreferrer" class="">P1425, "Iterators pair constructors for stack and queue"</a>
addresses the opposite direction: <em>constructing</em> a <code>stack</code>/<code>queue</code> from a
range, not extracting a range back out of one.</li>
<li class="">A similar design tension shows up outside C++: a <code>dart-lang/collection</code>
GitHub issue debates adding non-destructive, linear-time iteration to a
priority queue, with a maintainer explicitly weighing (and being wary of)
a destructive-iteration API — the same tradeoff this project resolves by
naming the operation <code>pop</code> rather than pretending it's a peek.</li>
<li class="">The general folk pattern in day-to-day C++ code is simply
<code>while (!pq.empty()) { use(pq.top()); pq.pop(); }</code> written out by hand at
each call site, rather than generalized into a reusable view — plausibly
why nothing like <code>pop_range</code> shows up as an established library.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-limitations-and-future-work">8. Limitations and Future Work<a href="https://cognosnotes.com/blog/cpp-pop-range#8-limitations-and-future-work" class="hash-link" aria-label="Direct link to 8. Limitations and Future Work" title="Direct link to 8. Limitations and Future Work" translate="no">​</a></h2>
<ul>
<li class=""><strong>Not thread-safe.</strong> No synchronization is attempted; concurrent
push/pop from other code while the view is being iterated is undefined
behavior, same as it would be for the raw adaptor.</li>
<li class=""><strong><code>operator*</code> copies.</strong> For expensive-to-copy <code>value_type</code>s, this is an
inherent cost of the design (the alternative — returning a reference — is
unsound, as noted in §4.2). Move-extraction is possible if <code>A</code> exposes a
method to move the top element out before popping, but <code>std::stack</code>,
<code>std::queue</code>, and <code>std::priority_queue</code> don't offer one, and the exercise
intentionally targeted the standard adaptors as-is.</li>
<li class=""><strong>No <code>size()</code>/<code>sized_range</code>.</strong> Deliberately omitted: since consuming
elements changes <code>size()</code> while iterating, exposing it risks implying a
stronger guarantee (like <code>sized_range</code>'s expectation that size doesn't
change under const, unmutated access) than is actually true.</li>
<li class=""><strong>Possible extension:</strong> an explicit non-owning "peek view" (read-only,
repeatable, requires a container that actually supports iteration, e.g.,
by exposing the underlying <code>c</code> member for <code>stack</code>/<code>queue</code> via the
protected-inheritance trick some codebases use) would be a legitimate,
complementary addition — but it is a fundamentally different,
non-destructive contract and was out of scope here.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="appendix-a-full-source">Appendix A: Full Source<a href="https://cognosnotes.com/blog/cpp-pop-range#appendix-a-full-source" class="hash-link" aria-label="Direct link to Appendix A: Full Source" title="Direct link to Appendix A: Full Source" translate="no">​</a></h2>
<p>The complete, tested implementation is in <a href="https://cognosnotes.com/assets/files/pop_range-ee6ea877e211f07918a767e234c4fb5c.hpp" target="_blank" class=""><code>pop_range.hpp</code></a>, with a
demonstration/test suite in <a href="https://cognosnotes.com/assets/files/main-2a953d65ac580198a315b3938d469e3f.cpp" target="_blank" class=""><code>main.cpp</code></a> (both delivered alongside this
document).</p>]]></content>
        <author>
            <name>Austin Kim</name>
            <uri>https://blog.anthonykim.net/</uri>
        </author>
        <category label="C++" term="C++"/>
        <category label="C++20" term="C++20"/>
        <category label="C++23" term="C++23"/>
        <category label="Code" term="Code"/>
        <category label="PipeOperator" term="PipeOperator"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Preloading Multiple ExoPlayer Instances Without Exhausting the VPU's MPS Budget]]></title>
        <id>https://cognosnotes.com/blog/preloading-exo-player</id>
        <link href="https://cognosnotes.com/blog/preloading-exo-player"/>
        <updated>2026-07-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[TL;DR]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tldr">TL;DR<a href="https://cognosnotes.com/blog/preloading-exo-player#tldr" class="hash-link" aria-label="Direct link to TL;DR" title="Direct link to TL;DR" translate="no">​</a></h2>
<ul>
<li class=""><strong>The problem</strong>: preloading several <code>ExoPlayer</code> instances so playback can start instantly hits the VPU's shared decoder admission budget — creating too many <code>MediaCodec</code> hardware decoders concurrently gets rejected (<code>OMX_ErrorInsufficientResources</code> / <code>MediaCodec.CodecException</code>, ExoPlayer error codes 4001/4003) — even though only one video is ever actually playing at a time.</li>
<li class=""><strong>Why it happens</strong>: ExoPlayer sets <code>KEY_OPERATING_RATE</code> to the real content frame rate — effectively "1x," the actual playback rate — on every decoder it creates, whether that decoder is actively playing or just sitting preloaded and idle (Section 5), and it never sets <code>MediaFormat.KEY_PRIORITY</code> at all (confirmed absent from <code>MediaCodecRenderer.java</code>/<code>MediaCodecVideoRenderer.java</code>). The net effect: the VPU's admission accounting has no signal telling it "this decoder isn't playing yet" and treats every created instance as if it needs guaranteed real-time decode throughput the moment it's created, not just once it's actually feeding frames. (Qualcomm's own kernel driver control technically defaults <code>KEY_PRIORITY</code> to non-realtime — Section 3.5 — but the resource exhaustion this document exists to solve is consistent with the vendor's closed-source Codec2/OMX HAL layer not preserving that default in practice; unverified from here, flagged as a caveat throughout.)</li>
<li class=""><strong>The fix</strong>: explicitly set <code>KEY_PRIORITY = 1</code> (non-realtime) and a low <code>KEY_OPERATING_RATE</code> on every decoder in the preload pool while it's idle, then flip both to realtime / full rate only on the one instance that's actually about to play (Section 6). Non-realtime load is charged at roughly <code>resolution × 1</code> instead of <code>resolution × fps</code> in the admission math (Sections 3.1–3.3), and non-realtime instances are excluded entirely from the per-core clock-fit check that gates new decoder admission (Section 3.6). For 30fps video, that's roughly a <strong>30× difference in admission cost</strong> between an idle non-realtime decoder and one actively playing at realtime priority (worked example, Section 3.4) — meaning, in principle, on the order of 30× more preloaded decoder instances can coexist in the same hardware budget than the naive "every decoder is realtime by default" approach allows.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The problem<a href="https://cognosnotes.com/blog/preloading-exo-player#1-the-problem" class="hash-link" aria-label="Direct link to 1. The problem" title="Direct link to 1. The problem" translate="no">​</a></h2>
<p>An app wants to preload several videos so playback can start instantly when the user picks one — but only one video ever plays at a time. The naive approach (create and <code>start()</code> a fully warm <code>MediaCodec</code> hardware decoder for every preloaded <code>ExoPlayer</code> instance) runs into a hard limit: the device's video decode block (VPU) rejects new decoder instances once the platform's admission-control accounting decides the combined load would exceed hardware capacity. This shows up as "insufficient resources" / codec creation failures once enough decoders are open concurrently, even though only one is ever actually decoding frames at once.</p>
<p>The real optimization target is <strong>hardware decoder component init latency</strong> — creating, <code>configure()</code>-ing, and <code>start()</code>-ing a <code>MediaCodec</code> against a HW codec is what's slow (tens of ms and up). Prefetching network/container data alone doesn't remove that latency; only having an already-initialized decoder does.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-the-mps-limit-actually-is">2. What the "MPS limit" actually is<a href="https://cognosnotes.com/blog/preloading-exo-player#2-what-the-mps-limit-actually-is" class="hash-link" aria-label="Direct link to 2. What the &quot;MPS limit&quot; actually is" title="Direct link to 2. What the &quot;MPS limit&quot; actually is" translate="no">​</a></h2>
<p>There's no single standardized "MPS limit" in AOSP. Two different things are easy to conflate:</p>
<ul>
<li class=""><code>MediaCodecInfo.VideoCapabilities</code> (<code>getMacroblocksPerSecond()</code>, <code>isSizeAndRateSupported()</code>) describes what <strong>one</strong> codec instance can sustain. This is public API and standardized.</li>
<li class="">The <strong>shared, cross-instance VPU budget</strong> that rejects a new decoder once too many are open is enforced by the <strong>vendor's resource manager</strong> (Qualcomm, MediaTek, etc.), not by the Android framework. It's undocumented, implementation-specific, and can differ across SoC vendors, chipset generations, and even firmware/Android version updates on the same chipset.</li>
</ul>
<p>Everything below was reverse-engineered from Qualcomm's <code>msm-vidc</code> kernel driver (the Venus/Iris video decode block used on Snapdragon devices). It is a case study, not a platform guarantee — see <a href="https://cognosnotes.com/blog/preloading-exo-player#8-caveats" class="">Section 8</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-core-mechanism-qualcomm-case-study">3. The core mechanism (Qualcomm case study)<a href="https://cognosnotes.com/blog/preloading-exo-player#3-the-core-mechanism-qualcomm-case-study" class="hash-link" aria-label="Direct link to 3. The core mechanism (Qualcomm case study)" title="Direct link to 3. The core mechanism (Qualcomm case study)" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-load-formula">3.1 The load formula<a href="https://cognosnotes.com/blog/preloading-exo-player#31-the-load-formula" class="hash-link" aria-label="Direct link to 3.1 The load formula" title="Direct link to 3.1 The load formula" translate="no">​</a></h3>
<p>From <code>msm_vidc_common.c</code> (<code>drivers/media/platform/msm/vidc/msm_vidc_common.c</code>):</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">msm_comm_get_mbs_per_sec</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token class-name">msm_vidc_inst</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">inst</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> output_port_mbs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> capture_port_mbs</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> fps</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    output_port_mbs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">NUM_MBS_PER_FRAME</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">width</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">OUTPUT_PORT</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                         inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">height</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">OUTPUT_PORT</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    capture_port_mbs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">NUM_MBS_PER_FRAME</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">width</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">CAPTURE_PORT</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                          inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">height</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">CAPTURE_PORT</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">clk_data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">operating_rate </span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        fps </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">clk_data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">operating_rate </span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        fps </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fps</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">output_port_mbs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> capture_port_mbs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> fps</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>NUM_MBS_PER_FRAME(w, h)</code> is <code>(w × h) / (16 × 16)</code> — macroblocks per frame. This per-instance value is summed across every open instance of a given type (<code>msm_comm_get_load()</code>, which iterates <code>core-&gt;instances</code> and adds <code>msm_comm_get_mbs_per_sec()</code> for each) and checked against a platform-defined <code>max_load</code> on codec creation and clock-scaling recalculation. That sum is the actual admission gate.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-what-key_operating_rate-really-does-to-that-formula">3.2 What <code>KEY_OPERATING_RATE</code> really does to that formula<a href="https://cognosnotes.com/blog/preloading-exo-player#32-what-key_operating_rate-really-does-to-that-formula" class="hash-link" aria-label="Direct link to 32-what-key_operating_rate-really-does-to-that-formula" title="Direct link to 32-what-key_operating_rate-really-does-to-that-formula" translate="no">​</a></h3>
<p>The critical detail: <code>fps</code> in the formula is <code>max(configured frame-rate, operating_rate)</code> — <strong>not</strong> the frame-rate alone. <code>KEY_FRAME_RATE</code> sets <code>inst-&gt;prop.fps</code> at <code>configure()</code> time; <code>KEY_OPERATING_RATE</code> sets <code>clk_data.operating_rate</code>.</p>
<p>This means declaring a low <code>KEY_FRAME_RATE</code> for idle/preloaded decoders and later raising <code>KEY_OPERATING_RATE</code> right before playback does <strong>not</strong> quietly bypass accounting — the moment operating rate goes up on an instance, that instance's contribution to the aggregate load recomputes to the real value too. The technique only works because the total is a <strong>sum across instances</strong>: as long as <code>(N−1 idle instances × low load) + (1 active instance × full load) ≤ max_load</code>, admission still succeeds. It requires bringing operating rate back down (or releasing/recreating the codec) once an instance stops being the active one — leaving it elevated defeats the "cheap while idle" premise.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-realtime-vs-non-realtime-sessions--the-more-robust-lever">3.3 Realtime vs. non-realtime sessions — the more robust lever<a href="https://cognosnotes.com/blog/preloading-exo-player#33-realtime-vs-non-realtime-sessions--the-more-robust-lever" class="hash-link" aria-label="Direct link to 3.3 Realtime vs. non-realtime sessions — the more robust lever" title="Direct link to 3.3 Realtime vs. non-realtime sessions — the more robust lever" translate="no">​</a></h3>
<p>The same file defines a documented split for exactly this pattern, in a comment directly above the load-calc logic:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">                 | OPERATING RATE SET   | OPERATING RATE NOT SET |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-----------------|----------------------|------------------------|</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">REALTIME         | load = res * op_rate |  load = res * fps      |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 | clk  = res * op_rate |  clk  = res * fps      |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-----------------|----------------------|------------------------|</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">NON-REALTIME     | load = res * 1 fps   |  load = res * 1 fps    |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 | clk  = res * op_rate |  clk  = res * fps      |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-----------------|----------------------|------------------------|</span><br></div></code></pre></div></div>
<p>For a <strong>non-realtime</strong> session (<code>is_realtime_session(inst)</code> false, gated by the <code>LOAD_CALC_IGNORE_NON_REALTIME_LOAD</code> quirk), the admission <code>load</code> stays pinned near the bare macroblock count (effectively "1 fps") <strong>regardless of operating rate</strong>, while the <code>clk</code> (actual achievable decode throughput) still scales with operating rate. In other words: a non-realtime-priority decoder can be clocked to real-time decode speed via <code>KEY_OPERATING_RATE</code> while still contributing almost nothing to the shared admission budget. This looks like the intended, driver-supported way to do what the frame-rate trick only approximates.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-worked-example-four-concurrent-decoders">3.4 Worked example: four concurrent decoders<a href="https://cognosnotes.com/blog/preloading-exo-player#34-worked-example-four-concurrent-decoders" class="hash-link" aria-label="Direct link to 3.4 Worked example: four concurrent decoders" title="Direct link to 3.4 Worked example: four concurrent decoders" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="KEY_PRIORITY to is_realtime_session to VPU admission load diagram" src="https://cognosnotes.com/assets/images/key-priority-mps-diagram-8417b14ab1d93e50e22a90cb8aa51711.svg" width="1280" height="900" class="img_ev3q"></p>
<p>The diagram above traces the full path — <code>MediaFormat.KEY_PRIORITY</code> → <code>C2_PARAMKEY_PRIORITY</code> (Codec2 vendor HAL) → <code>is_realtime_session(inst)</code> (msm-vidc kernel driver) → which branch of the load formula from Section 3.3 applies — and then applies it to four example <code>MediaCodec</code> instances: three preloaded/idle decoders configured non-realtime (<code>KEY_PRIORITY = 1</code>), and one actively-playing decoder configured realtime (<code>KEY_PRIORITY = 0</code>, the default). All numbers are illustrative, not a real device's certified budget.</p>
<p>Each instance's <code>MPS</code> (macroblocks per second) is <code>NUM_MBS_PER_FRAME(w, h) × fps</code>, where <code>NUM_MBS_PER_FRAME</code> rounds each dimension up to the nearest 16-pixel macroblock boundary before multiplying — this is why 1080p uses a height of 1088 (1080 isn't a multiple of 16; the hardware pads to 68 macroblock rows instead of 67.5) while 720p, whose dimensions are already multiples of 16, needs no rounding:</p>
<ul>
<li class=""><strong>Decoder A</strong> (non-realtime, idle): 1920×1088 → <code>120 × 68 = 8,160</code> mb/frame. Non-realtime forces <code>fps = 1</code> for the load calculation regardless of the real content frame rate, so <code>load = 8,160 × 1 = 8,160 MPS</code>.</li>
<li class=""><strong>Decoder B</strong> (non-realtime, idle): 1280×720 → <code>80 × 45 = 3,600</code> mb/frame. Same non-realtime rule: <code>load = 3,600 × 1 = 3,600 MPS</code>.</li>
<li class=""><strong>Decoder C</strong> (non-realtime, idle): same resolution as A → <code>load = 8,160 MPS</code>.</li>
<li class=""><strong>Decoder D</strong> (realtime, actively playing): 1920×1088 → <code>8,160</code> mb/frame. Realtime uses <code>fps = max(configured_fps, operating_rate) = max(30, 30) = 30</code>, so <code>load = 8,160 × 30 = 244,800 MPS</code> — roughly 30× more expensive than the same resolution sitting idle as non-realtime.</li>
</ul>
<p>Summed: <code>8,160 + 3,600 + 8,160 + 244,800 = 264,720 MPS</code>. Against the example <code>MAX_LOAD ≈ 300,000</code> budget, that fits with roughly 35,000 MPS of headroom. The callout in the diagram shows why this matters: if all four decoders had instead been left at the realtime default while merely sitting idle (i.e. no <code>KEY_PRIORITY</code> override at all, each still counted at its full configured/operating fps), the sum would be <code>8,160×30 + 3,600×30 + 8,160×30 + 8,160×30 = 842,400 MPS</code> — nearly 3× the same budget, and the fourth decoder would fail to admit. That gap is the entire value of the non-realtime-while-idle pattern from Sections 3.2–3.3.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-qualcomms-actual-default-and-what-cts-actually-tests">3.5 Qualcomm's actual default, and what CTS actually tests<a href="https://cognosnotes.com/blog/preloading-exo-player#35-qualcomms-actual-default-and-what-cts-actually-tests" class="hash-link" aria-label="Direct link to 3.5 Qualcomm's actual default, and what CTS actually tests" title="Direct link to 3.5 Qualcomm's actual default, and what CTS actually tests" translate="no">​</a></h3>
<p>Two directly-grounded findings, closing gaps left open above.</p>
<p><strong>Qualcomm's driver-level default is non-realtime.</strong> In <code>msm_vdec.c</code>, the <code>V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY</code> control's <code>default_value</code> is <code>V4L2_MPEG_MSM_VIDC_DISABLE</code>, and the <code>DISABLE</code> case inside <code>msm_vdec_s_ctrl()</code> clears <code>VIDC_REALTIME</code> (<code>inst-&gt;flags &amp;= ~VIDC_REALTIME</code>). Since <code>is_realtime_session()</code> is <code>!!(inst-&gt;flags &amp; VIDC_REALTIME)</code> and <code>inst</code> starts zero-initialized, a session that never touches <code>KEY_PRIORITY</code> is non-realtime by default at the kernel driver level — cheap (<code>mb/frame × 1</code>) rather than expensive (<code>mb/frame × fps</code>). This is grounded independently of, and matches, the generic AOSP contract quoted below. Caveat: this is the kernel control's default specifically; the vendor Codec2/OMX HAL sitting above it could still explicitly override it during its own init sequence, and that layer is closed-source and unverifiable from here.</p>
<p><strong>What CTS actually tests isn't priority-specific MPS accounting — it's the resource-exhaustion contract.</strong> From AOSP's <a href="https://source.android.com/docs/core/media/soc" target="_blank" rel="noopener noreferrer" class="">SoC vendor dependencies for media resource manager</a>:</p>
<blockquote>
<p>An Android Compatibility Test Suite (CTS) test exists to allocate, configure and start each codec repeatedly until catching <code>OMX_ErrorInsufficientResources</code> (pass) or any other error (fail).</p>
</blockquote>
<p>There's no CTS assertion that a <code>KEY_PRIORITY = 1</code> session gets any specific MPS discount — that math is vendor-internal policy, explicitly framed in the same doc as a "hint... for resource planning." What's actually certified is narrower: once a device's shared decoder budget is exhausted (however the vendor accounts for it), the component must fail with a clean, structured error — <code>OMX_ErrorInsufficientResources</code>, or its Codec2 equivalent <code>C2_NO_MEMORY</code> — rather than hang, crash, or ANR. <code>CCodec.cpp</code> carries that status up to the Java layer as <code>MediaCodec.CodecException</code> (the <code>0xfffffff4</code> / <code>-12</code> / <code>ENOMEM</code> error reported against real devices in the wild, and the same failure class behind ExoPlayer's error codes 4001/4003 noted in Section 1). That's the behavior to design a retry/fallback path around when a preload pool actually does exceed budget on a given device — not graceful silent degradation.</p>
<p>The practical CTS-relevant bar for <code>C2_PARAMKEY_PRIORITY</code> itself is just that a compliant component must accept <code>config()</code>/<code>query()</code> on it without error (no <code>C2_BAD_INDEX</code>/unsupported-param failure) — CTS doesn't verify what the component does with the value internally.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="36-the-actual-clkcore-admission-mechanism--and-why-its-realtime-aware-by-construction">3.6 The actual clk/core admission mechanism — and why it's realtime-aware by construction<a href="https://cognosnotes.com/blog/preloading-exo-player#36-the-actual-clkcore-admission-mechanism--and-why-its-realtime-aware-by-construction" class="hash-link" aria-label="Direct link to 3.6 The actual clk/core admission mechanism — and why it's realtime-aware by construction" title="Direct link to 3.6 The actual clk/core admission mechanism — and why it's realtime-aware by construction" translate="no">​</a></h3>
<p>A natural follow-up question: is a resource-exhaustion failure (<code>ENOMEM</code>/<code>OMX_ErrorInsufficientResources</code>) triggered by <code>load</code> (the MPS-sum admission gate from Section 3.1) or by <code>clk</code> (the clock-scaling value from Section 3.3's table)? The honest answer is that they aren't two separate mechanisms — <code>clk</code> is <code>load</code> converted into hardware cycles, and the actual hard-failure path lives in <code>msm_vidc_clocks.c</code>, in <code>msm_vidc_decide_core_and_power_mode()</code>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">current_inst_load </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> min_load </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> max_freq</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">clk_data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">core_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> min_core_id</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">current_inst_lp_load </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> min_load </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> max_freq</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">current_inst_lp_load </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> min_lp_load </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> max_freq</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    rc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">EINVAL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">dprintk</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">VIDC_ERR</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Sorry ... Core Can't support this load\n"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> rc</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>current_inst_load</code> is <code>msm_comm_get_inst_load(inst, ...) × vpp_cycles / work_route</code> — the same REALTIME/NON-REALTIME-gated <code>load</code> value from Section 3.3's table (res×fps or res×1), converted to cycles/sec via a codec-specific cycles-per-macroblock constant. <code>max_freq</code> is <code>allowed_clks_tbl[0].clock_rate</code> — the fastest clock rate the SoC's VPU cores can run at. Failing every branch above returns <code>-EINVAL</code> ("Core Can't support this load") — the mechanism behind the resource-exhaustion failures discussed in 3.5.</p>
<p>The side of the comparison that matters most for this document is how "how full is this core already" gets computed — <code>get_core_load(core, core_id, lp_mode, real_time)</code>, which filters:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">real_time_mode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> inst</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">flags </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> VIDC_REALTIME </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> true </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">real_time_mode </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> real_time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">continue</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p><code>msm_vidc_decide_core_and_power_mode()</code> always calls this with <code>real_time = true</code> — meaning <strong>only already-open realtime instances count toward how full a core is</strong> when deciding whether a new instance fits. Non-realtime instances are invisible to this sum entirely, and — because a non-realtime instance's own <code>current_inst_load</code> is computed at the "×1" rate — it barely registers its own demand either. This is the most direct, mechanistic confirmation in this document of the non-realtime-while-idle strategy from Sections 3.2–3.4: it isn't just cheap in the abstract MPS-sum math, it's specifically excluded from the exact code path that decides whether new decoders get admitted or rejected on a given core.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-the-three-mediaformat-keys-involved">4. The three <code>MediaFormat</code> keys involved<a href="https://cognosnotes.com/blog/preloading-exo-player#4-the-three-mediaformat-keys-involved" class="hash-link" aria-label="Direct link to 4-the-three-mediaformat-keys-involved" title="Direct link to 4-the-three-mediaformat-keys-involved" translate="no">​</a></h2>
<table><thead><tr><th>Key</th><th>What it does</th><th>When settable</th></tr></thead><tbody><tr><td><code>KEY_FRAME_RATE</code></td><td>Baseline fps used in resource/MPS calculations; required for encoders, optional for decoders.</td><td><code>configure()</code> only</td></tr><tr><td><code>KEY_OPERATING_RATE</code></td><td>Hints the desired processing rate; drives clock scaling (and, per above, is folded into the load formula on Qualcomm).</td><td><code>configure()</code>, and confirmed dynamically adjustable later via <code>setParameters()</code></td></tr><tr><td><code>KEY_PRIORITY</code></td><td><code>0</code> = realtime priority (default), any nonzero value (conventionally <code>1</code>) = non-realtime/best-effort. Generic AOSP-level key, not vendor-specific — it's the documented framework signal that (on Qualcomm) maps to <code>is_realtime_session()</code>.</td><td><code>configure()</code>, <strong>and</strong> the framework's SDK→Codec2 mapping layer does not restrict it to configure-only (see note below) — but whether a given vendor's component actually applies a live change is unconfirmed and must be tested on-device</td></tr></tbody></table>
<p>Recommended pattern given the driver behavior above: set <code>KEY_PRIORITY = 1</code> at <code>configure()</code> time for every decoder in your preload pool, and toggle <code>KEY_OPERATING_RATE</code> dynamically per-instance — raise it right before feeding real frames to the instance that's about to play, lower it again once that instance goes back to being merely "preloaded."</p>
<p><strong>Update on <code>KEY_PRIORITY</code> mutability</strong>: <code>frameworks/av/media/codec2/sfplugin/CCodecConfig.cpp</code> (the modern Codec2-based SDK→component mapping layer) maps <code>KEY_PRIORITY</code> to <code>C2_PARAMKEY_PRIORITY</code> with no domain restriction (<code>Domain::ALL</code>, which includes the <code>IS_PARAM</code> bit used for <code>setParameters()</code>), unlike <code>KEY_OPERATING_RATE</code>, which is explicitly restricted to <code>D::PARAM | D::CONFIG</code> and marked <code>// write-only</code>. In other words, the framework does not treat <code>KEY_PRIORITY</code> as configure-only — a live <code>setParameters()</code> call carrying it will be forwarded to the component. Whether the specific vendor Codec2 component actually honors a live priority change once running (vs. silently ignoring it) is component-specific and wasn't verifiable from this generic layer — test it directly: toggle <code>KEY_PRIORITY</code> via <code>setParameters()</code> on a running, idle decoder and watch the vendor's resource-manager/vidc logs to see whether its accounted load actually changes. If it does work live, you may not need a separate configure-only pool at all — you could flip a single decoder's priority down while idle and back up right before promoting it to active playback, alongside the operating-rate toggle.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-what-exoplayer--media3-does-out-of-the-box">5. What ExoPlayer / Media3 does out of the box<a href="https://cognosnotes.com/blog/preloading-exo-player#5-what-exoplayer--media3-does-out-of-the-box" class="hash-link" aria-label="Direct link to 5. What ExoPlayer / Media3 does out of the box" title="Direct link to 5. What ExoPlayer / Media3 does out of the box" translate="no">​</a></h2>
<p>Checked directly against the <code>androidx/media</code> source (<code>release</code> branch):</p>
<ul>
<li class=""><strong><code>MediaFormat.KEY_PRIORITY</code> is never set anywhere</strong> in <code>MediaCodecRenderer.java</code> (the shared base class for all codec-backed renderers) or <code>MediaCodecVideoRenderer.java</code>. Every codec ExoPlayer creates gets whatever the platform/vendor default priority is (typically realtime, <code>0</code>). ExoPlayer's own <code>rendererPriority</code> / <code>MSG_SET_PRIORITY</code> / <code>PriorityQueue</code> are an unrelated, ExoPlayer-internal renderer-scheduling concept — not the codec-level priority.</li>
<li class=""><strong><code>KEY_OPERATING_RATE</code> is actively managed.</strong> <code>MediaCodecVideoRenderer.getCodecOperatingRateV23()</code> computes it as the highest frame rate among the current adaptive stream formats × target playback speed. <code>MediaCodecRenderer</code> applies it at initial <code>configure()</code> and also updates it live later via <code>codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, newCodecOperatingRate)</code> — confirming operating rate really is adjustable post-configure through <code>setParameters()</code>, both per Android's own docs and in ExoPlayer's actual implementation.</li>
<li class=""><strong>The injection point for a custom <code>KEY_PRIORITY</code>/operating-rate policy already exists</strong>: <code>MediaCodecVideoRenderer.getMediaCodecConfiguration()</code> is <code>protected</code> and overridable, and ExoPlayer itself mutates the <code>MediaFormat</code> there before returning it (e.g. <code>maybeSetKeyAllowFrameDrop()</code> sets <code>KEY_ALLOW_FRAME_DROP</code>). A subclass can add <code>mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 1)</code> the same way.</li>
<li class=""><strong>Caveat</strong>: <code>getCodecOperatingRateV23()</code>'s default logic derives operating rate from the real content frame rate regardless of whether the renderer is actually started or just sitting idle post-<code>configure()</code>. Left unmodified, a "preloaded but not yet playing" ExoPlayer instance will still request the real operating rate by default — you need to override this too so idle/preloaded instances ask for a low rate, and only the promoted "about to play" instance asks for the real one.</li>
</ul>
<p>Note: <code>Media3</code>'s <code>PreloadMediaSource</code> / <code>DefaultPreloadManager</code> (added ~1.4, extended through 1.8) is <strong>not</strong> a solution to this problem. It preloads <code>MediaSource</code> extraction/parsing/buffered sample data, and its shared <code>RenderersFactory</code> use is explicitly to check format <em>compatibility</em> "without creating the full renderers." It does not warm an actual <code>MediaCodec</code> decoder instance, so it doesn't touch decoder-init latency at all — the thing this whole exercise is trying to optimize.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-recommended-design">6. Recommended design<a href="https://cognosnotes.com/blog/preloading-exo-player#6-recommended-design" class="hash-link" aria-label="Direct link to 6. Recommended design" title="Direct link to 6. Recommended design" translate="no">​</a></h2>
<ol>
<li class="">Maintain a <strong>bounded pool</strong> of <code>ExoPlayer</code> instances (or raw <code>MediaCodec</code> decoders) sized to fit your target devices' real admission budget — not "as many as the user might preload."</li>
<li class="">Subclass <code>MediaCodecVideoRenderer</code> (via a custom <code>RenderersFactory</code>) to override:<!-- -->
<ul>
<li class=""><code>getMediaCodecConfiguration()</code> — inject <code>KEY_PRIORITY = 1</code> at <code>configure()</code> time for every decoder in the pool. Treat this as the safe default; if on-device testing (Section 7) confirms your target vendor's component honors live <code>setParameters()</code> priority changes, you can instead toggle it dynamically alongside operating rate rather than only setting it once.</li>
<li class=""><code>getCodecOperatingRateV23()</code> — return a low value while an instance is idle/preloaded; return the real value (content frame rate × speed) only once that instance is promoted to "about to actively play."</li>
</ul>
</li>
<li class="">When promoting an instance to active playback, raise its operating rate (this can ride ExoPlayer's existing dynamic-update path); when demoting it back to idle, lower it again. Don't leave operating rate elevated on instances that aren't currently playing.</li>
<li class="">Don't rely on <code>KEY_FRAME_RATE</code> spoofing as the primary lever — treat <code>KEY_PRIORITY</code> (non-realtime) as the intended mechanism, and <code>KEY_OPERATING_RATE</code> as the real-time throughput control.</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-concrete-hook-points-onstarted--onstopped">6.1 Concrete hook points: <code>onStarted()</code> / <code>onStopped()</code><a href="https://cognosnotes.com/blog/preloading-exo-player#61-concrete-hook-points-onstarted--onstopped" class="hash-link" aria-label="Direct link to 61-concrete-hook-points-onstarted--onstopped" title="Direct link to 61-concrete-hook-points-onstarted--onstopped" translate="no">​</a></h3>
<p><code>BaseRenderer</code> (the root of ExoPlayer's renderer hierarchy) exposes <code>onStarted()</code> and <code>onStopped()</code> lifecycle callbacks, fired when a renderer transitions to/from actually being started (i.e. <code>playWhenReady</code> and the player is in <code>STATE_STARTED</code>). <code>MediaCodecRenderer.java</code> already overrides both (currently no-ops, just present "to remove throws clause"), confirming these are legitimate, safe override points in a subclass — this is the natural place to promote/demote priority, separate from <code>getMediaCodecConfiguration()</code> which only runs once at codec creation.</p>
<p><img decoding="async" loading="lazy" alt="MediaCodecVideoRenderer lifecycle: KEY_PRIORITY flips between idle and active playback" src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTIwMCA2NTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcm9sZT0iaW1nIj4KPHRpdGxlPk1lZGlhQ29kZWNWaWRlb1JlbmRlcmVyIGxpZmVjeWNsZTogS0VZX1BSSU9SSVRZIGZsaXBzIGJldHdlZW4gaWRsZSBhbmQgYWN0aXZlIHBsYXliYWNrPC90aXRsZT4KPGRlc2M+RGlhZ3JhbSBzaG93aW5nIGEgY3VzdG9tIE1lZGlhQ29kZWNWaWRlb1JlbmRlcmVyIHN1YmNsYXNzIHRoYXQgc2V0cyBLRVlfUFJJT1JJVFk9MSAobm9uLXJlYWx0aW1lKSBhdCBjb25maWd1cmUgdGltZSB2aWEgZ2V0TWVkaWFDb2RlY0NvbmZpZ3VyYXRpb24sIHRoZW4gcHJvbW90ZXMgdG8gS0VZX1BSSU9SSVRZPTAgKHJlYWx0aW1lKSB2aWEgb25TdGFydGVkIHdoZW4gcGxheWJhY2sgYmVnaW5zLCBhbmQgZGVtb3RlcyBiYWNrIHRvIEtFWV9QUklPUklUWT0xIHZpYSBvblN0b3BwZWQgd2hlbiBwbGF5YmFjayBlbmRzLCByaWRpbmcgRXhvUGxheWVyJ3MgZXhpc3RpbmcgTVNHX1NFVF9DT0RFQ19QQVJBTUVURVJTIHRvIGNvZGVjLnNldFBhcmFtZXRlcnMgcGlwZWxpbmUuPC9kZXNjPgo8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTIwMCIgaGVpZ2h0PSI2NTAiIGZpbGw9IiNmZmZmZmYiLz4KCjx0ZXh0IHg9IjYwMCIgeT0iMjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjIyIiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWYyNDMwIj5SZWNvbW1lbmRlZCBEZXNpZ246IFByaW9yaXR5IEZsaXBzIFdpdGggUGxheWJhY2sgU3RhdGU8L3RleHQ+Cjx0ZXh0IHg9IjYwMCIgeT0iNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjNWI2NDcyIj5DdXN0b20gTWVkaWFDb2RlY1ZpZGVvUmVuZGVyZXIgc3ViY2xhc3Mg4oCUIGhvb2tzIGNvbmZpcm1lZCBpbiBhbmRyb2lkeC9tZWRpYSBzb3VyY2UgKFNlY3Rpb24gNSk8L3RleHQ+Cgo8IS0tIENvbmZpZ3VyZSBib3ggLS0+CjxyZWN0IHg9IjYwIiB5PSI2NiIgd2lkdGg9IjMyMCIgaGVpZ2h0PSI3NiIgcng9IjEwIiBmaWxsPSIjZWVmMWY1IiBzdHJva2U9IiNjM2M5ZDMiLz4KPHRleHQgeD0iMjIwIiB5PSI4NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9Ii1hcHBsZS1zeXN0ZW0sU2Vnb2UgVUksUm9ib3RvLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTAuNSIgZmlsbD0iIzViNjQ3MiI+TWVkaWFDb2RlY1ZpZGVvUmVuZGVyZXIgc3ViY2xhc3M8L3RleHQ+Cjx0ZXh0IHg9IjIyMCIgeT0iMTA4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLFNGTW9uby1SZWd1bGFyLE1lbmxvLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMS41IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWYyNDMwIj5nZXRNZWRpYUNvZGVjQ29uZmlndXJhdGlvbigpPC90ZXh0Pgo8dGV4dCB4PSIyMjAiIHk9IjEyNyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSxTRk1vbm8tUmVndWxhcixNZW5sbyxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTEiIGZpbGw9IiMxZjI0MzAiPuKGkiBzZXRJbnRlZ2VyKEtFWV9QUklPUklUWSwgMSk8L3RleHQ+Cgo8cGF0aCBkPSJNMjIwIDE0MiBWMTY4IiBzdHJva2U9IiM4YTk0YTMiIHN0cm9rZS13aWR0aD0iMS42IiBtYXJrZXItZW5kPSJ1cmwoI2Fycm93RykiLz4KCjwhLS0gU3RhdGUgMTogaWRsZSAoZXZlbiBuYXJyb3dlcikgLS0+CjxyZWN0IHg9IjYwIiB5PSIxNzAiIHdpZHRoPSIzMjAiIGhlaWdodD0iMjUwIiByeD0iMTIiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzRmN2ZjMCIgc3Ryb2tlLXdpZHRoPSIxLjYiLz4KPHJlY3QgeD0iNzYiIHk9IjE4NCIgd2lkdGg9IjE1MCIgaGVpZ2h0PSIyNCIgcng9IjEyIiBmaWxsPSIjZTNlZGZiIi8+Cjx0ZXh0IHg9IjE1MSIgeT0iMjAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSxTZWdvZSBVSSxSb2JvdG8sc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzJmNWY5ZSI+SURMRSDCtyBQUkVMT0FERUQ8L3RleHQ+Cjx0ZXh0IHg9Ijc4IiB5PSIyNDAiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWYyNDMwIj5SZW5kZXJlciBub3Qgc3RhcnRlZDwvdGV4dD4KPHRleHQgeD0iNzgiIHk9IjI2NiIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSxTRk1vbm8tUmVndWxhcixNZW5sbyxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTMuNSIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFmMjQzMCI+UFJJT1JJVFkgPSAxIChub24tcmVhbHRpbWUpPC90ZXh0Pgo8dGV4dCB4PSI3OCIgeT0iMjg4IiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLFNGTW9uby1SZWd1bGFyLE1lbmxvLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi41IiBmaWxsPSIjM2E0MTUwIj5PUEVSQVRJTkdfUkFURSA9IGxvdyAoMeKAkzUpPC90ZXh0Pgo8bGluZSB4MT0iNzgiIHkxPSIzMDIiIHgyPSIzNjIiIHkyPSIzMDIiIHN0cm9rZT0iI2U0ZTdlYyIvPgo8dGV4dCB4PSI3OCIgeT0iMzIyIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLFNGTW9uby1SZWd1bGFyLE1lbmxvLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMS41IiBmb250LXN0eWxlPSJpdGFsaWMiIGZpbGw9IiM0YTY2OTAiPmlzX3JlYWx0aW1lX3Nlc3Npb24gPSBmYWxzZTwvdGV4dD4KPHRleHQgeD0iNzgiIHk9IjM0NCIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSxTRk1vbm8tUmVndWxhcixNZW5sbyxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMxZjI0MzAiPmxvYWQg4omIIG1iL2ZyYW1lIMOXIDE8L3RleHQ+Cjx0ZXh0IHg9Ijc4IiB5PSIzNjYiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBmb250LXN0eWxlPSJpdGFsaWMiIGZpbGw9IiM1YjY0NzIiPmNoZWFwIOKAlCBzZWUgU2VjdGlvbiAzLjQ8L3RleHQ+Cgo8IS0tIFN0YXRlIDI6IGFjdGl2ZSAoZXZlbiBuYXJyb3dlcikgLS0+CjxyZWN0IHg9IjgyMCIgeT0iMTcwIiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjI1MCIgcng9IjEyIiBmaWxsPSIjZmZmYWYzIiBzdHJva2U9IiNkOThhMmIiIHN0cm9rZS13aWR0aD0iMiIvPgo8cmVjdCB4PSI4MzYiIHk9IjE4NCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIyNCIgcng9IjEyIiBmaWxsPSIjZmRlY2Q4Ii8+Cjx0ZXh0IHg9IjkxNiIgeT0iMjAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSxTZWdvZSBVSSxSb2JvdG8sc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iI2I1NmExMiI+4pa2IEFDVElWRUxZIFBMQVlJTkc8L3RleHQ+Cjx0ZXh0IHg9IjgzOCIgeT0iMjQwIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSxTZWdvZSBVSSxSb2JvdG8sc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFmMjQzMCI+UmVuZGVyZXIgc3RhcnRlZCAocGxheWluZyk8L3RleHQ+Cjx0ZXh0IHg9IjgzOCIgeT0iMjY2IiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLFNGTW9uby1SZWd1bGFyLE1lbmxvLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMy41IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWYyNDMwIj5QUklPUklUWSA9IDAgKHJlYWx0aW1lKTwvdGV4dD4KPHRleHQgeD0iODM4IiB5PSIyODgiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsU0ZNb25vLVJlZ3VsYXIsTWVubG8sbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjUiIGZpbGw9IiMzYTQxNTAiPk9QRVJBVElOR19SQVRFID0gcmVhbCBmcHM8L3RleHQ+CjxsaW5lIHgxPSI4MzgiIHkxPSIzMDIiIHgyPSIxMTIyIiB5Mj0iMzAyIiBzdHJva2U9IiNlZGRhYzAiLz4KPHRleHQgeD0iODM4IiB5PSIzMjIiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsU0ZNb25vLVJlZ3VsYXIsTWVubG8sbW9ub3NwYWNlIiBmb250LXNpemU9IjExLjUiIGZvbnQtc3R5bGU9Iml0YWxpYyIgZmlsbD0iIzhhNjEzNSI+aXNfcmVhbHRpbWVfc2Vzc2lvbiA9IHRydWU8L3RleHQ+Cjx0ZXh0IHg9IjgzOCIgeT0iMzQ0IiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLFNGTW9uby1SZWd1bGFyLE1lbmxvLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzFmMjQzMCI+bG9hZCA9IG1iL2ZyYW1lIMOXIGZwczwvdGV4dD4KPHRleHQgeD0iODM4IiB5PSIzNjYiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBmb250LXN0eWxlPSJpdGFsaWMiIGZpbGw9IiM4YTYxMzUiPmZ1bGwgY29zdCDigJQgc2VlIFNlY3Rpb24gMy40PC90ZXh0PgoKPCEtLSBQcm9tb3RlIGFycm93ICsgbGFiZWwgKG11Y2ggd2lkZXIgZ2FwIG5vdzogMzgwIHRvIDgyMCkgLS0+Cjx0ZXh0IHg9IjYwMCIgeT0iMTkyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSxTZWdvZSBVSSxSb2JvdG8sc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iI2I1NmExMiI+UFJPTU9URTwvdGV4dD4KPHRleHQgeD0iNjAwIiB5PSIyMTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjExLjUiIGZpbGw9IiM4YTYxMzUiPnNldFBsYXlXaGVuUmVhZHkodHJ1ZSkgLyBzZWVrIGhlcmU8L3RleHQ+Cjx0ZXh0IHg9IjYwMCIgeT0iMjMwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLFNGTW9uby1SZWd1bGFyLE1lbmxvLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMS41IiBmaWxsPSIjOGE2MTM1Ij5SZW5kZXJlci5zdGFydCgpIOKGkiBvblN0YXJ0ZWQoKTwvdGV4dD4KPHRleHQgeD0iNjAwIiB5PSIyNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsU0ZNb25vLVJlZ3VsYXIsTWVubG8sbW9ub3NwYWNlIiBmb250LXNpemU9IjExLjUiIGZpbGw9IiM4YTYxMzUiPnNldFBhcmFtZXRlcnMoUFJJT1JJVFk9MCwgT1BfUkFURT1mcHMpPC90ZXh0Pgo8cGF0aCBkPSJNMzg0IDI2NiBIODEyIiBzdHJva2U9IiNkOThhMmIiIHN0cm9rZS13aWR0aD0iMiIgbWFya2VyLWVuZD0idXJsKCNhcnJvd0FtYmVyMikiLz4KCjwhLS0gRGVtb3RlIGFycm93ICsgbGFiZWwgLS0+CjxwYXRoIGQ9Ik04MTYgMzE0IEgzODgiIHN0cm9rZT0iIzRmN2ZjMCIgc3Ryb2tlLXdpZHRoPSIyIiBtYXJrZXItZW5kPSJ1cmwoI2Fycm93Qmx1ZTIpIi8+Cjx0ZXh0IHg9IjYwMCIgeT0iMzM2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSxTZWdvZSBVSSxSb2JvdG8sc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzJmNWY5ZSI+REVNT1RFPC90ZXh0Pgo8dGV4dCB4PSI2MDAiIHk9IjM1NiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9Ii1hcHBsZS1zeXN0ZW0sU2Vnb2UgVUksUm9ib3RvLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEuNSIgZmlsbD0iIzRhNjY5MCI+cGxheWJhY2sgZW5kcyAvIHN3aXBlZCBhd2F5PC90ZXh0Pgo8dGV4dCB4PSI2MDAiIHk9IjM3NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSxTRk1vbm8tUmVndWxhcixNZW5sbyxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTEuNSIgZmlsbD0iIzRhNjY5MCI+UmVuZGVyZXIuc3RvcCgpIOKGkiBvblN0b3BwZWQoKTwvdGV4dD4KPHRleHQgeD0iNjAwIiB5PSIzOTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsU0ZNb25vLVJlZ3VsYXIsTWVubG8sbW9ub3NwYWNlIiBmb250LXNpemU9IjExLjUiIGZpbGw9IiM0YTY2OTAiPnNldFBhcmFtZXRlcnMoUFJJT1JJVFk9MSwgT1BfUkFURT1sb3cpPC90ZXh0PgoKPCEtLSBFeGlzdGluZyBwaXBlbGluZSBub3RlIC0tPgo8cmVjdCB4PSI5MCIgeT0iNDUwIiB3aWR0aD0iMTAyMCIgaGVpZ2h0PSIxMTgiIHJ4PSIxMCIgZmlsbD0iI2Y2ZjdmOSIgc3Ryb2tlPSIjYzNjOWQzIiBzdHJva2UtZGFzaGFycmF5PSI0LDMiLz4KPHRleHQgeD0iNjAwIiB5PSI0NzQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWYyNDMwIj5UaGlzIGFscmVhZHkgcmlkZXMgYW4gZXhpc3RpbmcgRXhvUGxheWVyIHBpcGVsaW5lPC90ZXh0Pgo8dGV4dCB4PSI2MDAiIHk9IjQ5NiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSxTRk1vbm8tUmVndWxhcixNZW5sbyxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiMxZjI0MzAiPk1lZGlhQ29kZWNSZW5kZXJlci5oYW5kbGVNZXNzYWdlKE1TR19TRVRfQ09ERUNfUEFSQU1FVEVSUyk8L3RleHQ+Cjx0ZXh0IHg9IjYwMCIgeT0iNTE1IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLFNGTW9uby1SZWd1bGFyLE1lbmxvLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzFmMjQzMCI+4oaSIGNvZGVjLnNldFBhcmFtZXRlcnMoYWN0aXZlQ29kZWNQYXJhbWV0ZXJzLnRvQnVuZGxlKCkpPC90ZXh0Pgo8dGV4dCB4PSI2MDAiIHk9IjUzNSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9Ii1hcHBsZS1zeXN0ZW0sU2Vnb2UgVUksUm9ib3RvLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGZvbnQtc3R5bGU9Iml0YWxpYyIgZmlsbD0iIzViNjQ3MiI+Q29uZmlybWVkIGluIE1lZGlhQ29kZWNSZW5kZXJlci5qYXZhIOKAlCBhbHJlYWR5IHVzZWQgdG8gcHVzaCBLRVlfT1BFUkFUSU5HX1JBVEUgbGl2ZTs8L3RleHQ+Cjx0ZXh0IHg9IjYwMCIgeT0iNTUxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSxTZWdvZSBVSSxSb2JvdG8sc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgZm9udC1zdHlsZT0iaXRhbGljIiBmaWxsPSIjNWI2NDcyIj5leHRlbmQgdGhlIHNhbWUgQnVuZGxlIHRvIGFsc28gY2FycnkgS0VZX1BSSU9SSVRZIGZvciB0aGUgcHJvbW90ZS9kZW1vdGUgY2FsbHMgYWJvdmUuPC90ZXh0PgoKPHRleHQgeD0iNjAwIiB5PSI1OTQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLFNlZ29lIFVJLFJvYm90byxzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBmaWxsPSIjOWMyYjFmIj5WZXJpZnkgcGVyIHRhcmdldCB2ZW5kb3Igd2hldGhlciBhIGxpdmUgS0VZX1BSSU9SSVRZIGNoYW5nZSB2aWEgc2V0UGFyYW1ldGVycygpIGlzIGhvbm9yZWQgd2hpbGUgcnVubmluZyAoU2VjdGlvbiA3KS48L3RleHQ+Cjx0ZXh0IHg9IjYwMCIgeT0iNjEyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSxTZWdvZSBVSSxSb2JvdG8sc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzljMmIxZiI+SWYgbm90IGhvbm9yZWQsIGZhbGwgYmFjayB0byBhIHRlYXJkb3duL3JlY29uZmlndXJlIHN0ZXAgd2hlbiBwcm9tb3RpbmcvZGVtb3RpbmcgaW5zdGVhZCBvZiBhIGxpdmUgZmxpcC48L3RleHQ+Cgo8ZGVmcz4KICA8bWFya2VyIGlkPSJhcnJvd0ciIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iOSIgcmVmWT0iNSIgbWFya2VyV2lkdGg9IjciIG1hcmtlckhlaWdodD0iNyIgb3JpZW50PSJhdXRvLXN0YXJ0LXJldmVyc2UiPgogICAgPHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCB6IiBmaWxsPSIjOGE5NGEzIi8+CiAgPC9tYXJrZXI+CiAgPG1hcmtlciBpZD0iYXJyb3dBbWJlcjIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iOSIgcmVmWT0iNSIgbWFya2VyV2lkdGg9IjciIG1hcmtlckhlaWdodD0iNyIgb3JpZW50PSJhdXRvLXN0YXJ0LXJldmVyc2UiPgogICAgPHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCB6IiBmaWxsPSIjZDk4YTJiIi8+CiAgPC9tYXJrZXI+CiAgPG1hcmtlciBpZD0iYXJyb3dCbHVlMiIgdmlld0JveD0iMCAwIDEwIDEwIiByZWZYPSI5IiByZWZZPSI1IiBtYXJrZXJXaWR0aD0iNyIgbWFya2VySGVpZ2h0PSI3IiBvcmllbnQ9ImF1dG8tc3RhcnQtcmV2ZXJzZSI+CiAgICA8cGF0aCBkPSJNMCAwIEwxMCA1IEwwIDEwIHoiIGZpbGw9IiM0ZjdmYzAiLz4KICA8L21hcmtlcj4KPC9kZWZzPgo8L3N2Zz4K" width="1200" height="650" class="img_ev3q"></p>
<p>As the diagram shows: <code>getMediaCodecConfiguration()</code> sets <code>KEY_PRIORITY = 1</code> once, at <code>configure()</code> time, before the decoder ever plays anything. Overriding <code>onStarted()</code> is where you promote — call <code>codec.setParameters()</code> with <code>KEY_PRIORITY = 0</code> and the real operating rate right as the renderer actually starts pushing frames. Overriding <code>onStopped()</code> is the mirror — drop back to <code>KEY_PRIORITY = 1</code> and a low operating rate the moment the renderer stops being active (paused, ended, or the user swipes to a different preloaded item).</p>
<p>This isn't a new mechanism to build from scratch: <code>MediaCodecRenderer.java</code> already has a live parameter-update pipeline wired up — <code>handleMessage(MSG_SET_CODEC_PARAMETERS)</code> calls <code>codec.setParameters(activeCodecParameters.toBundle())</code> — which is exactly how ExoPlayer pushes <code>KEY_OPERATING_RATE</code> changes to a running codec today. Extending the same <code>Bundle</code> to also carry <code>KEY_PRIORITY</code> from your <code>onStarted()</code>/<code>onStopped()</code> overrides reuses infrastructure that's already proven to work for live parameter changes on a running codec — the only open question (Section 7) is whether the vendor's Codec2 component actually acts on <code>KEY_PRIORITY</code> specifically, versus <code>KEY_OPERATING_RATE</code>, once it arrives.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-validation-checklist-do-this-before-shipping">7. Validation checklist (do this before shipping)<a href="https://cognosnotes.com/blog/preloading-exo-player#7-validation-checklist-do-this-before-shipping" class="hash-link" aria-label="Direct link to 7. Validation checklist (do this before shipping)" title="Direct link to 7. Validation checklist (do this before shipping)" translate="no">​</a></h2>
<ul>
<li class="">Confirm on each <strong>target chipset family</strong> (Qualcomm, MediaTek, Exynos, Unisoc, etc.) — the load formula above is Qualcomm-specific; other vendors have different resource managers with different, likely undocumented, logic.</li>
<li class="">Watch <code>adb logcat</code> / kernel debug logs for the vendor's resource manager while creating decoders (<code>ResourceManager</code>, <code>Codec2</code>, <code>OMX</code>, <code>vidc</code>, "insufficient resource" strings) to see the actual summed load per instance and confirm it tracks <code>KEY_PRIORITY</code>/<code>KEY_OPERATING_RATE</code> as expected.</li>
<li class="">Confirm whether the vendor's Codec2 <strong>component</strong> actually applies a live <code>KEY_PRIORITY</code> change sent via <code>setParameters()</code> on an already-running codec. The framework-level mapping (<code>CCodecConfig.cpp</code>) does forward it, but component-side acceptance is vendor/implementation-specific — this determines whether an idle-pool decoder can be "promoted" via a live priority flip or needs a reconfigure/recreate step instead.</li>
<li class="">Verify actual decode is glitch-free (no dropped/late frames) the moment an idle, non-realtime instance is promoted and fed real frames at full operating rate — confirm the clock ramp-up doesn't introduce a startup stutter.</li>
<li class="">Re-run all of the above after any OEM firmware or Android version bump on your test devices; this is implementation-defined behavior, not a platform contract, and can silently change.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-caveats">8. Caveats<a href="https://cognosnotes.com/blog/preloading-exo-player#8-caveats" class="hash-link" aria-label="Direct link to 8. Caveats" title="Direct link to 8. Caveats" translate="no">​</a></h2>
<ul>
<li class="">
<p>Everything under Sections 3–4 beyond the generic <code>MediaFormat</code> key documentation is grounded in one Qualcomm kernel driver source tree (<code>msm-vidc</code>, Nougat-era <code>omx_vdec</code> / <code>msm_vidc_common.c</code>). Modern devices largely run Codec2 rather than OMX, and the exact parameter path from <code>MediaFormat.KEY_PRIORITY</code> down to <code>is_realtime_session()</code> on a current Codec2-based Qualcomm stack was not directly traced in this session — it needs on-device confirmation.</p>
</li>
<li class="">
<p><strong>MediaTek:</strong> the mainline/upstream open-source MediaTek decoder driver (<code>drivers/media/platform/mediatek/vcodec/</code>, used mainly on ChromeOS devices — MT8173/8183/8186/8188/8192/8195) has no equivalent to Qualcomm's <code>is_realtime_session()</code>/MPS-load-formula anywhere in its structures (<code>mtk_vcodec_dec_drv.h</code>). Its resource model looks architecturally different: a fixed hardware-core count (<code>MTK_VDEC_HW_MAX</code>) with per-core mutexes (<code>dec_mutex[]</code>), not a summed numeric budget across instances. MediaTek's actual production Android phone Codec2 HAL (Dimensity/Helio) is closed-source and not published in AOSP, so its internal accounting can't be directly inspected the way Qualcomm's was.</p>
<p>That said, AOSP's own <a href="https://source.android.com/docs/core/media/soc" target="_blank" rel="noopener noreferrer" class="">SoC vendor dependencies for media resource manager</a> documentation specifies <code>OMX_IndexConfigPriority</code> / <code>C2_PARAMKEY_PRIORITY</code> as a hook <strong>every</strong> SoC vendor — MediaTek included — is expected to implement for Android's media resource manager, backed by a CTS test that repeatedly allocates/configures/starts codecs until it catches <code>OMX_ErrorInsufficientResources</code>. So priority-based resource planning is a platform requirement, not a Qualcomm-only quirk. What's unconfirmed is whether MediaTek's internal accounting resembles Qualcomm's MPS-sum formula or something closer to the mainline driver's fixed-core model (which would behave differently under the "declare many non-realtime, promote one" pattern — worth testing directly on Dimensity/Helio hardware before relying on it).</p>
<p>One more thing surfaced by that same AOSP doc, independent of the preload-pool question: it states priority defaults to non-realtime (<code>1</code>) unless explicitly configured to <code>0</code>, and warns implementers not to assume realtime priority unless it's set. Section 5 confirmed ExoPlayer never sets <code>KEY_PRIORITY</code> at all — and Section 3.5 confirms, directly from <code>msm_vdec.c</code>, that Qualcomm's own kernel driver default agrees: the <code>V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY</code> control defaults to <code>DISABLE</code>, which clears <code>VIDC_REALTIME</code>. So on Qualcomm at least, an ExoPlayer decoder that never sets <code>KEY_PRIORITY</code> should land non-realtime by default at the driver level — though whether the vendor's Codec2/OMX HAL layer above the kernel driver preserves that default, rather than overriding it during its own init, remains unverified (that layer is closed-source).</p>
</li>
<li class="">
<p>Exynos, Unisoc, and other vendor resource managers were not investigated at all. Assume nothing about their behavior without separately confirming it.</p>
</li>
<li class="">
<p>This entire approach is an unofficial technique riding on vendor implementation details, not a documented Android platform capability. It should be validated per target device and treated as something that can regress on firmware updates.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-sources">9. Sources<a href="https://cognosnotes.com/blog/preloading-exo-player#9-sources" class="hash-link" aria-label="Direct link to 9. Sources" title="Direct link to 9. Sources" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://github.com/realme-kernel-opensource/realme5-kernel-source/blob/master/drivers/media/platform/msm/vidc/msm_vidc_common.c" target="_blank" rel="noopener noreferrer" class="">msm_vidc_common.c — realme5-kernel-source (Qualcomm msm-vidc driver)</a></li>
<li class=""><a href="https://android.googlesource.com/platform/frameworks/av/+/master/media/codec2/sfplugin/CCodecConfig.cpp" target="_blank" rel="noopener noreferrer" class="">CCodecConfig.cpp — platform/frameworks/av (AOSP, SDK↔Codec2 key mapping)</a></li>
<li class=""><a href="https://android.googlesource.com/platform/frameworks/av/+/master/media/codec2/sfplugin/CCodecConfig.h" target="_blank" rel="noopener noreferrer" class="">CCodecConfig.h — platform/frameworks/av (AOSP, Domain bitmask definitions)</a></li>
<li class=""><a href="https://developer.android.com/reference/android/media/MediaFormat" target="_blank" rel="noopener noreferrer" class="">MediaFormat — Android Developers reference</a></li>
<li class=""><a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/media/java/android/media/MediaFormat.java" target="_blank" rel="noopener noreferrer" class="">MediaFormat.java — platform/frameworks/base (AOSP)</a></li>
<li class=""><a href="https://github.com/androidx/media/blob/release/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java" target="_blank" rel="noopener noreferrer" class="">MediaCodecVideoRenderer.java — androidx/media (release branch)</a></li>
<li class=""><a href="https://github.com/androidx/media/blob/release/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java" target="_blank" rel="noopener noreferrer" class="">MediaCodecRenderer.java — androidx/media (release branch)</a></li>
<li class=""><a href="https://github.com/androidx/media/blob/release/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java" target="_blank" rel="noopener noreferrer" class="">MediaCodecInfo.java — androidx/media (release branch)</a></li>
<li class=""><a href="https://android-developers.googleblog.com/2025/09/introducing-preloading-with-media3.html" target="_blank" rel="noopener noreferrer" class="">Elevating media playback: Introducing preloading with Media3 — Part 1 (Android Developers Blog)</a></li>
<li class=""><a href="https://android-developers.googleblog.com/2025/09/a-deep-dive-into-media3-preloadmanager.html" target="_blank" rel="noopener noreferrer" class="">Elevating media playback: A deep dive into Media3's PreloadManager — Part 2 (Android Developers Blog)</a></li>
<li class=""><a href="https://github.com/androidx/media/blob/release/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java" target="_blank" rel="noopener noreferrer" class="">PreloadMediaSource.java — androidx/media (release branch)</a></li>
<li class=""><a href="https://github.com/torvalds/linux/blob/master/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h" target="_blank" rel="noopener noreferrer" class="">mtk_vcodec_dec_drv.h — torvalds/linux mainline (MediaTek decoder driver)</a></li>
<li class=""><a href="https://source.android.com/docs/core/media/soc" target="_blank" rel="noopener noreferrer" class="">SoC vendor dependencies for media resource manager — Android Open Source Project</a></li>
<li class=""><a href="https://github.com/realme-kernel-opensource/realme5-kernel-source/blob/master/drivers/media/platform/msm/vidc/msm_vdec.c" target="_blank" rel="noopener noreferrer" class="">msm_vdec.c — realme5-kernel-source (Qualcomm msm-vidc driver, V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY control default)</a></li>
<li class=""><a href="https://android.googlesource.com/platform/frameworks/av/+/master/media/codec2/sfplugin/CCodec.cpp" target="_blank" rel="noopener noreferrer" class="">CCodec.cpp — platform/frameworks/av (AOSP, Codec2↔MediaCodec status/error mapping)</a></li>
<li class=""><a href="https://github.com/realme-kernel-opensource/realme5-kernel-source/blob/master/drivers/media/platform/msm/vidc/msm_vidc_clocks.c" target="_blank" rel="noopener noreferrer" class="">msm_vidc_clocks.c — realme5-kernel-source (Qualcomm msm-vidc driver, per-core clock/load admission — <code>msm_vidc_decide_core_and_power_mode()</code>, <code>get_core_load()</code>)</a></li>
</ul>]]></content>
        <author>
            <name>Austin Kim</name>
            <uri>https://blog.anthonykim.net/</uri>
        </author>
        <category label="MediaCodec" term="MediaCodec"/>
        <category label="AOSP" term="AOSP"/>
        <category label="ExoPlayer" term="ExoPlayer"/>
        <category label="video" term="video"/>
        <category label="VPU" term="VPU"/>
        <category label="MPS" term="MPS"/>
    </entry>
</feed>