<!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],
.vscode-high-contrast:not(.vscode-high-contrast-light) img[src$=\#gh-light-mode-only],
.vscode-high-contrast-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>
<style>
:root {
  --color-note: #0969da;
  --color-tip: #1a7f37;
  --color-warning: #9a6700;
  --color-severe: #bc4c00;
  --color-caution: #d1242f;
  --color-important: #8250df;
}

</style>
<style>
@media (prefers-color-scheme: dark) {
  :root {
    --color-note: #2f81f7;
    --color-tip: #3fb950;
    --color-warning: #d29922;
    --color-severe: #db6d28;
    --color-caution: #f85149;
    --color-important: #a371f7;
  }
}

</style>
<style>
.markdown-alert {
  padding: 0.5rem 1rem;
  margin-bottom: 16px;
  color: inherit;
  border-left: .25em solid #888;
}

.markdown-alert>:first-child {
  margin-top: 0
}

.markdown-alert>:last-child {
  margin-bottom: 0
}

.markdown-alert .markdown-alert-title {
  display: flex;
  font-weight: 500;
  align-items: center;
  line-height: 1
}

.markdown-alert .markdown-alert-title .octicon {
  margin-right: 0.5rem;
  display: inline-block;
  overflow: visible !important;
  vertical-align: text-bottom;
  fill: currentColor;
}

.markdown-alert.markdown-alert-note {
  border-left-color: var(--color-note);
}

.markdown-alert.markdown-alert-note .markdown-alert-title {
  color: var(--color-note);
}

.markdown-alert.markdown-alert-important {
  border-left-color: var(--color-important);
}

.markdown-alert.markdown-alert-important .markdown-alert-title {
  color: var(--color-important);
}

.markdown-alert.markdown-alert-warning {
  border-left-color: var(--color-warning);
}

.markdown-alert.markdown-alert-warning .markdown-alert-title {
  color: var(--color-warning);
}

.markdown-alert.markdown-alert-tip {
  border-left-color: var(--color-tip);
}

.markdown-alert.markdown-alert-tip .markdown-alert-title {
  color: var(--color-tip);
}

.markdown-alert.markdown-alert-caution {
  border-left-color: var(--color-caution);
}

.markdown-alert.markdown-alert-caution .markdown-alert-title {
  color: var(--color-caution);
}

</style>
        
        </head>
        <body class="vscode-body vscode-light">
            <style type="text/css">
p {text-align:justify}
li {text-align:justify}
blockquote.note
{
background-color:#E0E0E0;
padding-left: 15px;
padding-right: 15px;
padding-top: 1px;
padding-bottom: 1px;
}
code
{
color:#000000;
}
ins {background-color:#A0FFA0}
del {background-color:#FFA0A0}
table {border-collapse: collapse;}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
<table>
<thead>
<tr>
<th>Document Number:</th>
<th>p2927r2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Date:</td>
<td>2024-04-15</td>
</tr>
<tr>
<td>Target:</td>
<td>LWG</td>
</tr>
<tr>
<td>Revises:</td>
<td>p2927r1</td>
</tr>
<tr>
<td>Reply to:</td>
<td>Arthur O'Dwyer (<a href="mailto:arthur.j.odwyer@gmail.com">arthur.j.odwyer@gmail.com</a>), Gor Nishanov (<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. This was reaffirmed
in 2023 Kona meeting.</p>
<p>This paper brings back exception_ptr inspection facility in a simplified form addressing
the earlier feedback.</p>
<h2 id="revision-history">Revision history</h2>
<p><strong>r0</strong> - restart the proposal in a simplified form</p>
<p><strong>r1</strong> - implement &quot;strict&quot; behavior (<code>exception_ptr_cast&lt;logic_error&gt;</code>, as opposed to also allowing cv-ref qualified types, as in <code>exception_ptr_cast&lt;const logic_error&amp;&gt;</code>, for example)</p>
<p><strong>r2</strong> - rename to <code>exception_ptr_cast</code>, add motivation section, add feature test macro.</p>
<h2 id="proposal-at-a-glance">Proposal at a glance</h2>
<p>We introduce a single function <code>exception_ptr_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> T* 
<span class="hljs-title">exception_ptr_cast</span><span class="hljs-params">(<span class="hljs-type">const</span> exception_ptr&amp; e)</span> <span class="hljs-keyword">noexcept</span></span>;
</code></pre>
<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 = std::<span class="hljs-built_in">exception_ptr_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 = std::<span class="hljs-built_in">exception_ptr_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 = std::<span class="hljs-built_in">exception_ptr_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/E8n69xKjs">https://godbolt.org/z/E8n69xKjs</a>.</p>
<h2 id="motivation">Motivation</h2>
<p>In asynchronous programming, errors frequently
travel packaged in an <code>exception_ptr</code>. For example, see: <code>std::future</code>, <code>boost::future</code>, nvidia's <code>exec::task</code>, <code>Folly::Future</code>, <code>libunifex::task</code>, <code>ppl::task</code>, etc.</p>
<p>However, exception_ptr itself is an opaque type and
to get to the exact error stored requires
rethrow and catch.</p>
<p>The facility offered here allows getting access to
the stored error that is significantly (100x) faster.
While theoretically, some exception patterns could
be optimized by the compilers (see <a href="https://wg21.link/p1676">https://wg21.link/p1676</a>),
no major compiler has implemented any of these
optimizations since C++ existed.</p>
<p><em>[Edit: The following example was added after Tokyo WG21 meeting for the post Tokyo mailing]</em></p>
<h3 id="before">Before</h3>
<pre><code class="language-c++"><span class="hljs-comment">// Examine the exception stored in exception_ptr and decide if retry is needed.</span>
<span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">should_retry</span><span class="hljs-params">(<span class="hljs-type">const</span> std::exception_ptr&amp; eptr)</span>
</span>{
    <span class="hljs-keyword">try</span>
    {
        std::<span class="hljs-built_in">rethrow_exception</span>(eptr);
    }
    <span class="hljs-built_in">catch</span>(std::system_error&amp; e)
    {
        <span class="hljs-keyword">return</span> e.<span class="hljs-built_in">code</span>() == std::errc::device_or_resource_busy;
    }
    <span class="hljs-built_in">catch</span>(...)
    {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
}
</code></pre>
<h3 id="after">After</h3>
<pre><code class="language-c++"><span class="hljs-comment">// Examine the exception stored in exception_ptr and decide if retry is needed.</span>
<span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">should_retry</span><span class="hljs-params">(<span class="hljs-type">const</span> std::exception_ptr&amp; eptr)</span>
</span>{
    <span class="hljs-keyword">auto</span>* e = std::<span class="hljs-built_in">exception_ptr_cast</span>&lt;std::system_error&gt;(eptr);
    <span class="hljs-keyword">return</span> e &amp;&amp; e-&gt;<span class="hljs-built_in">code</span>() == std::errc::device_or_resource_busy;
}
</code></pre>
<h2 id="simplification-post-kona-2023">Simplification post Kona 2023</h2>
<p>Previous revision tentatively proposed a complicated signature imitating the syntax of a catch-parameter, as in <code>old::exception_ptr_cast&lt;const std::exception&amp;&gt;(p)</code>.
In Kona, we were convinced to simplify the signature to assume catch-by-const-reference no matter what: <code>std::exception_ptr_cast&lt;std::exception&gt;(p)</code>.</p>
<ul>
<li>With the old design, there were two ways to simulate catching by const reference: <code>old::exception_ptr_cast&lt;const std::exception&amp;&gt;(p)</code>
and <code>old::exception_ptr_cast&lt;std::exception&gt;(p)</code>. The new syntax removes that needless variability of style.</li>
<li>Users of the old syntax might think they had to write <code>old::exception_ptr_cast&lt;const std::exception&amp;&gt;(p)</code> in order to catch by reference;
that's unnecessarily verbose and hard to read. The new syntax is short and readable.</li>
<li>The old syntax permitted <code>old::exception_ptr_cast&lt;std::exception&amp;&gt;(p)</code> to catch by non-const reference; this is sometimes legitimate but usually it's just an unnecessary violation of const-correctness. Users might omit the <code>const</code> out of inattention or laziness. The new syntax always returns <code>const*</code>, privileging the common and const-correct case.</li>
<li>Users who want to modify the in-flight exception object can still explicitly write <code>const_cast&lt;std::exception*&gt;(std::exception_ptr_cast&lt;std::exception&gt;(p))</code>. The <code>const_cast</code> is visible and greppable; this is a good thing.</li>
<li>The old syntax permitted nonsensical scenarios such as <code>old::exception_ptr_cast&lt;int(&amp;)()&gt;(p)</code>. Such a catch handler is physically impossible to hit, because you can't throw a function; functions are not objects. The new syntax admits only <code>old::exception_ptr_cast&lt;int()&gt;(p)</code>, which is ill-formed by our new Mandates element because <code>int()</code> is not an object type. Similarly, we disallow <code>old::exception_ptr_cast&lt;int[5]&gt;(p)</code> via the Mandates element.</li>
</ul>
<p>P2927R0 proposed that <code>exception_ptr_cast</code> should be able to catch pointer types, just like an ordinary catch clause. That is, not only were you allowed to inspect a thrown <code>Derived</code> object with <code>old::exception_ptr_cast&lt;const Base&amp;&gt;</code> (which would return a possibly null <code>const Base*</code>), you were also allowed to inspect a thrown <code>Derived*</code> object with <code>old::exception_ptr_cast&lt;const Base*&gt;</code> (which would return a possibly null <code>const Base**</code>). This turned out to be unimplementable. When a catch-handler catches <code>Derived*</code> as <code>Base*</code>, it may need to adjust the pointer for multiple and/or virtual base classes. The pointer caught by the core language, then, is a temporary. We can't return a <code>const Base**</code> pointing to that temporary adjusted pointer, because there's nowhere for the temporary adjusted pointer to live after the call to <code>exception_ptr_cast</code> has returned.</p>
<p>In other words, the new design has a strict invariant: the pointer returned from <code>exception_ptr_cast</code> always points to the in-flight exception object itself. It never points to any other object, such as a temporary or global. Thus, we must disallow:</p>
<pre><code class="language-c++">    <span class="hljs-keyword">using</span> IntPtr = <span class="hljs-type">int</span>*;
    std::<span class="hljs-type">nullptr_t</span> np;
    <span class="hljs-keyword">auto</span> p = std::<span class="hljs-built_in">make_exception_ptr</span>(np);
      <span class="hljs-comment">// The in-flight exception object is of type std::nullptr_t</span>
    <span class="hljs-type">const</span> IntPtr *ex = old::<span class="hljs-built_in">exception_ptr_cast</span>&lt;IntPtr&gt;(p);
      <span class="hljs-comment">// ex cannot possibly point to the in-flight exception object, because the in-flight object is not an IntPtr!</span>
    <span class="hljs-keyword">try</span> {
        std::<span class="hljs-built_in">rethrow_exception</span>(p);
    } <span class="hljs-built_in">catch</span> (<span class="hljs-type">const</span> IntPtr&amp; ex) {
        <span class="hljs-comment">// OK, ex refers to a temporary that lives only as long as this catch block</span>
    }
</code></pre>
<p>Our solution is simply to extend our Mandates element to also forbid <code>std::exception_ptr_cast</code> with a template argument of pointer or pointer-to-member type; these are the only two kinds of types where a core-language catch-handler parameter would sometimes bind to a temporary, so these are the only kinds of types we need to forbid. Later, we found that [<a href="https://github.com/scylladb/scylladb/blob/946d281/utils/exceptions.hh#L128-L151">ScyllaDB</a>] had independently implemented the same solution (i.e. explicitly forbid pointer types) in 2022.</p>
<p>Throwing pointers is rare — probably unheard of in real code. This does prevent users from using <code>std::exception_ptr_cast&lt;const char*&gt;(p)</code> to inspect the results of <code>throw &quot;foo&quot;</code>, which comes up sometimes in example code; but it shouldn't happen in real code.</p>
<!--
## Discussion

Let's look at existing facilities in the library that serve somewhat similar purpose:

**any_cast:**

```c++
template<class T> T any_cast(const any& operand);
template<class T> T any_cast(any& operand);
template<class T> T any_cast(any&& operand);

template<class T> const T* any_cast(const any* operand) noexcept;
template<class T> T* any_cast(any* operand) noexcept;
```

Last two overloads perform inspection of a stored value and give a pointer to it
if the typeid matches, otherwise return `nullptr`.

The first three, throw `bad_any_cast` exception if the stored value does not match the requested type.

**get/get_if:**

```c++
// get forms that takes a reference and throw if the type is not active alternative
template<class T, class... Types> T* get_if(variant<Types...>* pv) noexcept;
template<class T, class... Types> const T* get_if(const variant<Types...>* pv) noexcept;
```

Similarly to `any_cast`, 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 `nullptr` if it is not an active alternative.

**exception_ptr:**

**Q**: Should we follow the the examples above and have two flavors of exception_ptr inspection,
one that throws if the requested type `E` is not stored and one that does not.

**A**: No. The goal of this facility is to provide a way to inspect a stored exception value
without relying on throwing exception.

**Q**: Non-throwing versions of `dynamic_cast`, `any_cast` and `get_if` all take a pointer to a value to be inspected,
should this API take the pointer as well?

**A**: `dynamic_cast` and `any_cast` 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.

**Q**: The `variant` and `any` inspection APIs offers a version that allows both const and
non const access to the stored value. Should we do the same for `exception_ptr`?

**A**: No. N4928/[propagation]/7 states that:

> 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.

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.

In this paper we only offer a `exception_ptr_cast` 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 `try_cast_mutable` or something along those lines can be added.

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.

Similarly, we chose to specify the desired type in the simplest possible form, i.e:

```sql
auto* x = exception_ptr_cast<const int*>(eptr); // no
auto* x = exception_ptr_cast<int*>(eptr);       // no
auto* x = exception_ptr_cast<int>(eptr);        // yes
```

Thus, the thought process above led to the following API shape:

```c++
template <typename T>
const decay_t<T>* 
exception_ptr_cast(const exception_ptr& e) noexcept;
```

**Q**: Could we reuse `any_cast` name?

**A**: No. It encodes `any` in its name strongly hinting it is meant
to be used with std::any.

**Q**: Maybe `get_if` would work?

**A**: Possibly, but, it has slightly different semantics.
Unlike `any_cast`, `dynamic_cast` and offered here `exception_ptr_cast` have
`cast` in its name, implying that it does not have to be exact match,
whereas `get_if` expects the type to be exact match from the list of variant
alternatives.

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.

**Q**: Why `const std::decay_t<T>*` in the return value? Could it be just T*?

**A**: The intent here is to make sure that people who habitually write `catch (const E& e) { ... }` can continue doing it with `exception_ptr_cast<const E&>`, 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.
-->
<h2 id="pattern-matching">Pattern matching</h2>
<p>We expect that <code>exception_ptr_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>(<span class="hljs-string">&quot;some other exception&quot;</span>); }
}
</code></pre>
<h2 id="other-names-considered">Other names considered</h2>
<ul>
<li><code>try_cast&lt;E&gt;</code></li>
<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>
<li><code>exception_cast&lt;E&gt;</code></li>
</ul>
<p>Based on the recent discussion on LEWG mattermost on March 22, 2024, the two top names favored were:</p>
<ul>
<li><code>exception_ptr_cast</code></li>
<li><code>exception_cast</code></li>
</ul>
<p>This revision optimistically chosen the first alternative,
as it seemed to get more likes, but, the authors will gladly rename the facility to
any LEWG approved name.</p>
<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>exception_ptr_cast</code> facility offered in this paper.</p>
<p>Arthur has implemented P2927R1 <code>std::exception_ptr_cast</code> in his fork of libc++; see [<a href="https://github.com/Quuxplusone/llvm-project/commit/6e20a0b9d5a2280bfab8ab42bee841cfbcc4a8bd">libc++</a>] and [<a href="https://godbolt.org/z/3Y8Gzfr7r">Godbolt</a>].</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>
<p>ScyllaDB implements almost exactly the wording of this proposal, under the name <code>try_catch&lt;E&gt;(p)</code>; see [<a href="https://github.com/scylladb/scylladb/blob/946d281/utils/exceptions.hh#L128-L151">ScyllaDB</a>]. The only difference is that they return <code>E*</code> instead of <code>const E*</code>. We hear from them that they don't actually use the mutability for anything; and even if they did, they could add <code>const_cast</code> as mentioned above.</p>
<h2 id="proposed-wording-relative-to-n4950">Proposed wording (relative to n4950)</h2>
<p>In section [exception.syn] add definition for <code>exception_ptr_cast</code>
as follows:</p>
<div style="margin-left: 30px;">
<code>
exception_ptr current_exception() noexcept;<br>
[[noreturn]] void rethrow_exception(exception_ptr p);<br>
<ins>
template &lt;class E&gt;<br>
&nbsp;&nbsp;const E* exception_ptr_cast(const exception_ptr& p) noexcept;
</ins><br>
template &lt;class T&gt; [[noreturn]] void throw_with_nested(T&& t);
</code>
</div>
<p></p>
Modify paragraph 7 of section Exception propagation [propagation] as follows:
<p></p>
<div style="margin-left: 30px;">
For purposes of determining the presence of a data race, operations on <code>exception_ptr</code> objects shall access and modify only the <code>exception_ptr</code> objects themselves and not the exceptions they refer to. Use of <code>rethrow_exception</code> <ins>or <code>exception_ptr_cast</code></ins>
on <code>exception_ptr</code> objects that refer to the same exception object shall not introduce a data race.
</div>
<br>
Add the following paragraph immediately after paragraph 8 of section Exception propagation [propagation]:
<p></p>
<div style="margin-left: 30px;">
<ins>
template &lt;class E&gt;<br>
&nbsp;&nbsp;const E* exception_ptr_cast(const exception_ptr& p) noexcept;
<br>
<br><i>Mandates:</i> <code>E</code> is a <i>cv</i>-unqualified complete object type. <code>E</code> is not an array type. <code>E</code> is not a pointer or pointer-to-member type. <i>[Note: When <code>E</code> is a pointer or pointer-to-member type, a handler of type <code>const E&amp;</code> can match without binding to the exception object itself. —end note]</i>
<br>
<br><i>Returns: </i>
A pointer to the exception object referred to by <code>p</code>,
if <code>p</code> is not null and a handler of type <code>const E&amp;</code> would be a
match [except.handle] for that exception object.
Otherwise, <code>nullptr</code>.
</ins><br>
<div style="margin-left: 20px;">
</div>
</div>
<br>
<b>[version.syn]</b>
<br><br>
Add:
<br>
<div style="margin-left: 20px;">
<ins>#define __cpp_lib_exception_ptr_cast   202403L // also in &lt;exception&gt;</ins>
</div>
<!--
## Kona 2023 EWG Feedback

Suggestions mentioned but not polled:

* Add a note that to modify the stored exception, one can use `const_cast`. For example: `const_cast<int*>(exception_ptr_cast<int>(eptr))`. (This was mentioned as an alternative to having dedicated `mutable_try_cast`).
* Make `exception_ptr_cast<int[5]>` or `exception_ptr_cast<void(int)>` ill-formed
* Make `exception_ptr_cast<int&>` ill-formed
* Alternatively, give mutable and non-mutable versions depending on the type:
    * `exception_ptr_cast<int>(eptr) -> const int*`
    * `exception_ptr_cast<int&>(eptr) -> int*`
-->
<h2 id="acknowledgments">Acknowledgments</h2>
<p>Many thanks to those who provided valuable feedback, among them:
Aaryaman Sagar,
Barry Revzin,
Gabriel Dos Reis, Jan Wilmans, Joshua Berne,
Lee Howes, Lewis Baker, Michael Park, Peter Dimov, Ville Voutilainen, Yedidya Feldblum.</p>
<h2 id="references">References</h2>
<p><a href="https://godbolt.org/z/E8n69xKjs">https://godbolt.org/z/E8n69xKjs</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>
<p><a href="https://github.com/scylladb/scylladb/blob/946d281/utils/exceptions.hh#L128-L151">https://github.com/scylladb/scylladb/blob/946d281/utils/exceptions.hh#L128-L151</a> ScyllaDb</p>
<p>An implementation of try cast and libc++ and matching godbolt:</p>
<p><a href="https://github.com/Quuxplusone/llvm-project/commit/6e20a0b9d5a2280bfab8ab42bee841cfbcc4a8bd">https://github.com/Quuxplusone/llvm-project/commit/6e20a0b9d5a2280bfab8ab42bee841cfbcc4a8bd</a></p>
<p><a href="https://godbolt.org/z/3Y8Gzfr7r">https://godbolt.org/z/3Y8Gzfr7r</a></p>
<p><a href="https://wg21.link/p1676">https://wg21.link/p1676</a> Optimizing exceptions</p>
<!--
get_if

template<size_t I, class... Types>
constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
  get_if(const variant<Types...>* v) noexcept;
Mandates: I < sizeof...(Types).
Returns: A pointer to the value stored in the variant, if v != nullptr and v->index() == I.
Otherwise, returns nullptr.

any_cast
Returns: If operand != nullptr && operand->type() == typeid(T), a pointer to the object con- tained by operand; otherwise, nullptr.

14.4 Handling an exception [except.handle]
1 The exception-declaration in a handler describes the type(s) of exceptions that can cause that handler to be entered. The exception-declaration shall not denote an incomplete type, an abstract class type, or an rvalue reference type. The exception-declaration shall not denote a pointer or reference to an incomplete type, other than “pointer to cv void”.
2 A handler of type “array of T” or function type T is adjusted to be of type “pointer to T”.
§ 14.4 455
(3.1)
(3.2) (3.3)
(3.3.1)
(3.3.2) (3.3.3) (3.4)
— The handler is of type cv T or cv T& and E and T are the same type (ignoring the top-level cv-qualifiers), or
— the handler is of type cv T or cv T& and T is an unambiguous public base class of E, or
— the handler is of type cv T or const T& where T is a pointer or pointer-to-member type and E is a
pointer or pointer-to-member type that can be converted to T by one or more of
— a standard pointer conversion (7.3.12) not involving conversions to pointers to private or protected
or ambiguous classes
— a function pointer conversion (7.3.14)
— a qualification conversion (7.3.6), or
— the handler is of type cv T or const T& where T is a pointer or pointer-to-member type and E is std::nullptr_t.






f(T, eptr) -> T*

```c++
f<T>(eptr) -> T*

f<const T&>(eptr)

f<int>(eptr) -> const int*


f<const int&>(eptr) -> const int& *


catch (const int&)
f<const int>
f<int>
```


mutable_try_cast - unnecessary

const_cast<int*>(exception_ptr_cast<int>(eptr))


-->

            
            
        </body>
        </html>