Inspecting exception_ptr should be constexpr
Motivation
Since P3068r6 C++ exceptions and most of exception_ptr
support functions to get current_exception
, make_exception_ptr
, and rethrow_exception
are constexpr
enabled. But newly added function exception_ptr_cast
from paper P2927r3 adopted in Sofia is not. I consider this an unfortunate overlook but easily fixable as its implementation is trivial.
Change
This will allow functionality to query if exception is of certain type also during constant evaluation. In terms of wording it's just adding two constexpr
.
Implementation
I don't have implementation in my library as I learned about it few hours ago, so I have it only here in this paper, but I'm pretty comfortable it will work. There are two possible implementation depending if a platforms is having exception storage reference counted. Or is doing copies for each exception rethrow.
Reference counting
template <class E> constexpr const E* exception_ptr_cast(const exception_ptr& p) noexcept {
// Mandates
static_assert(!std::is_const_v<E>);
static_assert(!std::is_reference_v<E>);
static_assert(std::is_object_v<E>);
static_assert(!std::is_array_v<E>);
static_assert(!std::is_pointer_v<E>);
static_assert(!std::is_member_pointer_v<E>);
try {
if (p != nullptr) {
std::rethrow_exception(p);
}
} catch (const E & exc) {
return &exc; // on platforms with ref-counted exceptions
} catch (...) {
// fallthrough so we just ignore other exceptions
// but they are also not copied, just thrown,
// and still alive in the exception's refcounted storage
}
return nullptr;
}
Exception copying
In my implementation exception_ptr
is just a wrapper to void *
pointer to the exception object. So I can use following implementation:
template <class E> constexpr const E* exception_ptr_cast(const exception_ptr& p) noexcept {
// skipping all the mandates here
return dynamic_cast<const E *>(p.__ptr); // cute isn't?
}
Alternative is to have a builtin to do that information and use metadata known to compiler during constant evaluation in if consteval
and do whatever the original paper proposed in runtime. Note constant-time thrown exceptions can't leave constant evaluation according [expr.const]
.
Wording
In section [exception.syn]
update definition for exception_ptr_cast
:
constexpr const E* exception_ptr_cast(const exception_ptr& p) noexcept;
In section Exception propagation [propagation]
:
constexpr const E* exception_ptr_cast(const exception_ptr& p) noexcept;
Mandates: E
is a cv-unqualified complete object type. E
is not an array type. E
is not a pointer or pointer-to-member type. [Note: When E
is a pointer or pointer-to-member type, a handler of type const E& can match without binding to the exception object itself. —end note]
Returns: A pointer to the exception object referred to by p
, if p
is not null and a handler of type const E&
would be a match [except.handle]
for that exception object. Otherwise, nullptr
.
Feature test macro
17.3.2 Header <version> synopsis [version.syn]
#define __cpp_lib_exception_ptr_cast 2026XX202403L // also in <exception>