[Development] QVariant and types with throwing dtors
Marc Mutz
marc.mutz at qt.io
Mon Aug 26 07:24:49 CEST 2024
(sorry for the late entrance, was OOO the last two weeks)
My vote goes to (3). (2) is a good intermediate step (e.g. for Qt 6,
with Qt 7 converting to static_assert()). Remember that ctors are
implicitly noexcept in C++, so you need to do actual work to make a type
that's not nothrow_destructible. In Qt, incl. 3rd-party monster
chromium, we have _two_ such types, QChildProcess and
QScopedPropertyUpdateGroup. QtC has another two, in sqlite:
~AbstractThrowingSessionTransaction and ~AbstractThrowingTransaction.
So, by an educated guesstimation, this won't be an issue, and possibly
catch mistakes, like when using a 3rd-party library type in QVariant.
The other option (4) is to make it supported: have a
QVariant::destroy<T>() function that's conditionally noexcept, so users
can destroy the payload in a controlled environment instead of running
into std::terminate() in ~QVariant() (throwing from a noexcept function
will _not_ unroll the stack, so you might not even get logging that you
expected).
IMHO, (1) is not an acceptable option. Us C++ professionals having identified
this problem after years of it lying dormant, it behooves us, at the
very least, to educate our users about this, e.g. by adding docs, and
maybe a qWarning() in ~QVariant(), if we don't do (2).
Thanks,
Marc
On 09.08.24 10:46, Fabian Kosmale via Development wrote:
> Hi,
>
> Qt's container classes (at least those listed in
> https://doc.qt.io/qt-6/containers.html + QCache) reject types with
> throwing destructorss via static_assert[1].
>
> There is however one "container" which doesn't reject such types:
> QVariant. Even though it's own destructor is noexcept, it doesn't reject
> types whose destructor is potentially throwing. My question is: Do we
> want to change this, and if so, how? There are at least three alternatives:
>
> 1. Don't do anything; that's the behavior we have since at least Qt 5.
> If the dtor doesn't actually throw, everything is fine; if it does
> throw, we're calling std::terminate. Might lead to unexpected results,
> but doing nothing obviously doesn't break any existing code. It is
> however inconsistent with other types.
> 2. Warn, but still accept such types [2]. I've implemented that in
> https://codereview.qt-project.org/c/qt/qtbase/+/580560. Anyone one
> stores a type with a throwing dtor would get a warning, but everything
> still compiles. The warning can however be considered spurious if the
> author knows that given their usage, the type would never throw.
> 3. Reject such types at compile time. That would be consistent with
> other types, but might break existing code, even code that works
> perfectly fine because it can never actually throw.
>
> I'm leaning towards either 1 (nobody ever reported a bug about QVariant
> calling std::terminate so far), or 2 ( more consistent with our policy
> for containers, highlights that such usage is potentially dangerous).
>
> I'd welcome any comments on concerns.
>
> Kind regards,
> Fabian
>
> [1] There are however still some methods which are marked as
> conditionally noexept, like QCache::take, even though that can never be
> the case given the static_assert.
> [2] At least in cases where we can detect it at compile time; the API
> using QMetaType would require storing the necessary information in
> QMetaType, and a runtime check.
>
--
Marc Mutz <marc.mutz at qt.io> (he/his)
Principal Software Engineer
The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io
Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B
More information about the Development
mailing list