<!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Inspecting exception&lowbar;ptr</title>
        <style>
/* From extension vscode.github */
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

.vscode-dark img[src$=\#gh-light-mode-only],
.vscode-light img[src$=\#gh-dark-mode-only] {
	display: none;
}

</style>
        
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/markdown.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/highlight.css">
<style>
            body {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif;
                font-size: 14px;
                line-height: 1.6;
            }
        </style>
        <style>
.task-list-item {
    list-style-type: none;
}

.task-list-item-checkbox {
    margin-left: -20px;
    vertical-align: middle;
    pointer-events: none;
}
</style>
        
    </head>
    <body class="vscode-body vscode-light">
        <table>
<thead>
<tr>
<th>Document Number:</th>
<th>p2927r0</th>
</tr>
</thead>
<tbody>
<tr>
<td>Date:</td>
<td>2023-06-16</td>
</tr>
<tr>
<td>Target:</td>
<td>EWG, LEWG, LEWGI</td>
</tr>
<tr>
<td>Reply to:</td>
<td><a href="mailto:gorn@microsoft.com">gorn@microsoft.com</a></td>
</tr>
</tbody>
</table>
<h1 id="inspecting-exception_ptr">Inspecting exception_ptr</h1>
<h2 id="abstract">Abstract</h2>
<p>Provide facility to observe exceptions stored in <code>std::exception_ptr</code> without throwing or catching exceptions.</p>
<h2 id="introduction">Introduction</h2>
<p>This is a followup to two previous papers in this area:</p>
<table>
<thead>
<tr>
<th>Date</th>
<th>Link</th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr>
<td>Feb 7, 2018</td>
<td><a href="https://wg21.link/p0933">https://wg21.link/p0933</a></td>
<td>Runtime introspection of exception_ptr</td>
</tr>
<tr>
<td>Oct 6, 2018</td>
<td><a href="https://wg21.link/p1066">https://wg21.link/p1066</a></td>
<td>How to catch an exception_ptr without even try-ing</td>
</tr>
</tbody>
</table>
<p>These papers received positive feedback. In 2018 Rapperswil meeting, EWG expressed strong desire in having such facility:</p>
<blockquote>
<p>Does EWG want a non throwing mechanism to get to the exception held by exception_ptr even if the performance was the same</p>
</blockquote>
<table>
<thead>
<tr>
<th>SF</th>
<th>F</th>
<th>N</th>
<th>A</th>
<th>SA</th>
</tr>
</thead>
<tbody>
<tr>
<td>16</td>
<td>8</td>
<td>1</td>
<td>0</td>
<td>0</td>
</tr>
</tbody>
</table>
<p>LEWG looked at this at SanDiego 2018 and encouraged to come back after
addressing the following points:</p>
<ul>
<li>Remove the handle interface.</li>
<li>Combine with P0933R0.</li>
<li>Demonstrate field and implementation experience.</li>
<li>Add wording</li>
<li>Reach out to implementors and confirm this is implementable.</li>
</ul>
<p>This paper brings back exception_ptr inspection facility in a simplified form addressing
the earlier feedback.</p>
<h2 id="proposal-at-a-glance">Proposal at a glance</h2>
<p>We introduce a single function <code>try_cast</code> that takes <code>std::exception_ptr</code> as an argument <code>e</code> and returns a pointer to an object referred to by <code>e</code>.</p>
<pre><code class="language-c++"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-function"><span class="hljs-type">const</span> std::<span class="hljs-type">remove_cvref_t</span>&lt;T&gt;* 
<span class="hljs-title">try_cast</span><span class="hljs-params">(<span class="hljs-type">const</span> std::exception_ptr&amp; e)</span> <span class="hljs-keyword">noexcept</span></span>;
</code></pre>
<p>If <code>exception_ptr</code> is not empty and <code>std::remove_cvref_t&lt;T&gt;</code> is the type of the stored exception <code>E</code> or its unambiguous base, a const pointer to the stored exception is returned; otherwise <code>nullptr</code> is returned.</p>
<p><strong>Example:</strong></p>
<p>Given the following error classes:</p>
<pre><code class="language-c++"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Foo</span> {
    <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">Foo</span>() {}
    <span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;
};
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Bar</span> : Foo, std::logic_error {
    <span class="hljs-built_in">Bar</span>() : std::<span class="hljs-built_in">logic_error</span>(<span class="hljs-string">&quot;This is Bar exception&quot;</span>) {}
    <span class="hljs-type">int</span> j = <span class="hljs-number">2</span>;
};
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Baz</span> : Bar {};
</code></pre>
<p>The execution of the following program</p>
<pre><code class="language-c++"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> exp = std::<span class="hljs-built_in">make_exception_ptr</span>(<span class="hljs-built_in">Baz</span>());
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">auto</span>* x = <span class="hljs-built_in">try_cast</span>&lt;Baz&gt;(exp))
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;got &#x27;%s&#x27; i: %d j: %d\n&quot;</span>, <span class="hljs-built_in">typeid</span>(*x).<span class="hljs-built_in">name</span>(), x-&gt;i, x-&gt;j); 
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">auto</span>* x = <span class="hljs-built_in">try_cast</span>&lt;Bar&gt;(exp))
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;got &#x27;%s&#x27; i: %d j: %d\n&quot;</span>, <span class="hljs-built_in">typeid</span>(*x).<span class="hljs-built_in">name</span>(), x-&gt;i, x-&gt;j);
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">auto</span>* x = <span class="hljs-built_in">try_cast</span>&lt;Foo&gt;(exp))
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;got &#x27;%s&#x27; i: %d\n&quot;</span>, <span class="hljs-built_in">typeid</span>(*x).<span class="hljs-built_in">name</span>(), x-&gt;i);
}
</code></pre>
<p>results in this output:</p>
<pre><code>got '3Baz' what:'This is Bar exception' i: 1 j: 2
got '3Baz' what:'This is Bar exception' i: 1 j: 2
got '3Baz' i: 1
</code></pre>
<p>See implementation for GCC and MSVC using available (but undocumented) APIs <a href="https://godbolt.org/z/ErePMf66h">https://godbolt.org/z/ErePMf66h</a>.</p>
<h2 id="discussion">Discussion</h2>
<p>Let's look at existing facilities in the library that serve somewhat similar purpose:</p>
<p><strong>any_cast:</strong></p>
<pre><code class="language-c++"><span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">class</span> T&gt; T <span class="hljs-title">any_cast</span><span class="hljs-params">(<span class="hljs-type">const</span> any&amp; operand)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">class</span> T&gt; T <span class="hljs-title">any_cast</span><span class="hljs-params">(any&amp; operand)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">class</span> T&gt; T <span class="hljs-title">any_cast</span><span class="hljs-params">(any&amp;&amp; operand)</span></span>;

<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">class</span> T&gt; <span class="hljs-type">const</span> T* <span class="hljs-title">any_cast</span><span class="hljs-params">(<span class="hljs-type">const</span> any* operand)</span> <span class="hljs-keyword">noexcept</span></span>;
<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">class</span> T&gt; T* <span class="hljs-title">any_cast</span><span class="hljs-params">(any* operand)</span> <span class="hljs-keyword">noexcept</span></span>;
</code></pre>
<p>Last two overloads perform inspection of a stored value and give a pointer to it
if the typeid matches, otherwise return <code>nullptr</code>.</p>
<p>The first three, throw <code>bad_any_cast</code> exception if the stored value does not match the requested type.</p>
<p><strong>get/get_if:</strong></p>
<pre><code class="language-c++"><span class="hljs-comment">// get forms that takes a reference and throw if the type is not active alternative</span>
<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">class</span> T, <span class="hljs-keyword">class</span>... Types&gt; T* <span class="hljs-title">get_if</span><span class="hljs-params">(variant&lt;Types...&gt;* pv)</span> <span class="hljs-keyword">noexcept</span></span>;
<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">class</span> T, <span class="hljs-keyword">class</span>... Types&gt; <span class="hljs-type">const</span> T* <span class="hljs-title">get_if</span><span class="hljs-params">(<span class="hljs-type">const</span> variant&lt;Types...&gt;* pv)</span> <span class="hljs-keyword">noexcept</span></span>;
</code></pre>
<p>Similarly to <code>any_cast</code>, get and get_if offer throwing versions that take a reference
to a variant and a non-throwing that take a variant by pointer and return a pointer
to the value of the requested type or <code>nullptr</code> if it is not an active alternative.</p>
<p><strong>exception_ptr:</strong></p>
<p><strong>Q</strong>: Should we follow the the examples above and have two flavors of exception_ptr inspection,
one that throws if the requested type <code>E</code> is not stored and one that does not.</p>
<p><strong>A</strong>: No. The goal of this facility is to provide a way to inspect a stored exception value
without relying on throwing exception.</p>
<p><strong>Q</strong>: Non-throwing versions of <code>dynamic_cast</code>, <code>any_cast</code> and <code>get_if</code> all take a pointer to a value to be inspected,
should this API take the pointer as well?</p>
<p><strong>A</strong>: <code>dynamic_cast</code> and <code>any_cast</code> relies on a distinction on how the value is passed
(by pointer or by reference) to select what semantic to provide. In our case,
we always perform a conditional access, thus, requiring a pointer would be a needless syntactic noise.</p>
<p><strong>Q</strong>: The <code>variant</code> and <code>any</code> inspection APIs offers a version that allows both const and
non const access to the stored value. Should we do the same for <code>exception_ptr</code>?</p>
<p><strong>A</strong>: No. N4928/[propagation]/7 states that:</p>
<blockquote>
<p>For purposes of determining the presence of a data race, operations on exception_ptr objects shall access and modify only the exception_ptr objects themselves and not the exceptions they refer to.</p>
</blockquote>
<p>Offering the API flavor that allows mutation of the stored exception
has potential of injecting a data race. Cpp core guidelines recommend
catching exception by const reference.</p>
<p>In this paper we only offer a <code>try_cast</code> API that offer an ability to inspect,
but not modify stored exception. If in the future, we will discover an important use
case that requires changing the stored exception a different API with lengthier
and less convenient name, say <code>try_cast_mutable</code> or something along those lines can be added.</p>
<p>The desire is to offer the simplest name for the most common use case that is a good
default for the majority of the users.</p>
<p>Similarly, we chose to specify the desired type in the simplest possible form, i.e:</p>
<pre><code class="language-sql">auto<span class="hljs-operator">*</span> x <span class="hljs-operator">=</span> try_cast<span class="hljs-operator">&lt;</span>const <span class="hljs-type">int</span><span class="hljs-operator">*</span><span class="hljs-operator">&gt;</span>(eptr); <span class="hljs-operator">/</span><span class="hljs-operator">/</span> <span class="hljs-keyword">no</span>
auto<span class="hljs-operator">*</span> x <span class="hljs-operator">=</span> try_cast<span class="hljs-operator">&lt;</span><span class="hljs-type">int</span><span class="hljs-operator">*</span><span class="hljs-operator">&gt;</span>(eptr);       <span class="hljs-operator">/</span><span class="hljs-operator">/</span> <span class="hljs-keyword">no</span>
auto<span class="hljs-operator">*</span> x <span class="hljs-operator">=</span> try_cast<span class="hljs-operator">&lt;</span><span class="hljs-type">int</span><span class="hljs-operator">&gt;</span>(eptr);        <span class="hljs-operator">/</span><span class="hljs-operator">/</span> yes
</code></pre>
<p>Thus, the thought process above led to the following API shape:</p>
<pre><code class="language-c++"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-function"><span class="hljs-type">const</span> std::<span class="hljs-type">remove_cvref_t</span>&lt;T&gt;* 
<span class="hljs-title">try_cast</span><span class="hljs-params">(<span class="hljs-type">const</span> std::exception_ptr&amp; e)</span> <span class="hljs-keyword">noexcept</span></span>;
</code></pre>
<p><strong>Q</strong>: Could we reuse <code>any_cast</code> name?</p>
<p><strong>A</strong>: No. It encodes <code>any</code> in its name strongly hinting it is meant
to be used with std::any.</p>
<p><strong>Q</strong>: Maybe <code>get_if</code> would work?</p>
<p><strong>A</strong>: Possibly, but, it has slightly different semantics.
Unlike <code>any_cast</code>, <code>dynamic_cast</code> and offered here <code>try_cast</code> have
<code>cast</code> in its name, implying that it does not have to be exact match,
whereas <code>get_if</code> expects the type to be exact match from the list of variant
alternatives.</p>
<p>It is possible that in the future, we will have a unifying
facility (pattern matching, for example) that would allow uniform access across
variant, any, exception_ptr and other types. Finding such facility is out of
scope of this paper.</p>
<p><strong>Q</strong>: Why <code>const std::remove_cvref_t&lt;T&gt;*</code> in the return value? Could it be just T?</p>
<p><strong>A</strong>: The intent here is to make sure that people who habitually write <code>catch (const E&amp; e) { ... }</code> can continue doing it with <code>try_cast&lt;const E&amp;&gt;</code>, as opposed to getting a compilation error. This is an error from the category:
The compiler/library knows what you mean, but, will force you to write exactly as it wants.</p>
<h2 id="other-names-considered-but-rejected">Other names considered but rejected</h2>
<ul>
<li><code>cast_exception_ptr&lt;E&gt;</code></li>
<li><code>cast_exception&lt;E&gt;</code></li>
<li><code>try_cast_exception_ptr&lt;E&gt;</code></li>
<li><code>try_cast_exception&lt;E&gt;</code></li>
<li><code>catch_as&lt;E&gt;</code></li>
</ul>
<h2 id="pattern-matching">Pattern matching</h2>
<p>We expect that <code>try_cast</code> will be integrated in the <a href="https://wg21.link/p1371">pattern matching facility</a> and
will allow inspection of <code>exception_ptr</code> as follows:</p>
<pre><code class="language-c++"><span class="hljs-built_in">inspect</span> (eptr) {
   &lt;logic_error&gt; e =&gt; { ... }
   &lt;exception&gt; e =&gt; { ... }
   <span class="hljs-literal">nullptr</span> =&gt; { <span class="hljs-built_in">puts</span>(<span class="hljs-string">&quot;no exception&quot;</span>); }
   __ =&gt; { <span class="hljs-built_in">puts</span>(some other exception<span class="hljs-string">&quot;); }
} 
</span></code></pre>
<h2 id="implementation">Implementation</h2>
<p>GCC, MSVC implementation is possible using available (but undocumented) APIs <a href="https://godbolt.org/z/ErePMf66h">https://godbolt.org/z/ErePMf66h</a>. Implementability was also confirmed by MSVC and libstdc++ implementors.</p>
<p>A similar facility is available in Folly and supports Windows, libstdc++, and libc++ on linux/apple/freebsd.</p>
<p><a href="https://github.com/facebook/folly/blob/v2023.06.26.00/folly/lang/Exception.h">https://github.com/facebook/folly/blob/v2023.06.26.00/folly/lang/Exception.h</a>
<a href="https://github.com/facebook/folly/blob/v2023.06.26.00/folly/lang/Exception.cpp">https://github.com/facebook/folly/blob/v2023.06.26.00/folly/lang/Exception.cpp</a></p>
<p>Implementation there under the names:
folly::exception_ptr_get_object
folly::exception_ptr_get_type</p>
<p>Extra constraint imposed by MSVC ABI: it doesn't have information stored to do a full dynamic_cast. It can only recover types for which a catch block could potentially match.
This does not conflict with the <code>try_cast</code> facility offered in this paper.</p>
<h2 id="usage-experience">Usage experience</h2>
<p>A version of exception_ptr inspection facilities is deployed widely in Meta as
a part of Folly's future continuation matching.</p>
<h2 id="proposed-wording">Proposed wording</h2>
<p>TBD</p>
<h2 id="acknowledgments">Acknowledgments</h2>
<p>Many thanks to those who provided valuable feedback, among them:
Aaryaman Sagar,
Arthur O'Dwyer, Jan Wilmans, Joshua Berne,
Lee Howes, Michael Park, Peter Dimov, Ville Voutilainen, Yedidya Feldblum.</p>
<h2 id="references">References</h2>
<p><a href="https://godbolt.org/z/ErePMf66h">https://godbolt.org/z/ErePMf66h</a> (gcc and msvc implementation)</p>
<p><a href="https://wg21.link/p0933">https://wg21.link/p0933</a> Runtime introspection of exception_ptr</p>
<p><a href="https://wg21.link/p1066">https://wg21.link/p1066</a> How to catch an exception_ptr without even try-ing</p>
<p><a href="https://wg21.link/p1371">https://wg21.link/p1371</a> Pattern Matching</p>

        
        
    </body>
    </html>