From d4435a37cae43abfbdb247b7d4a3a950aced2751 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Tue, 30 Apr 2019 12:39:22 +0200 Subject: [PATCH] Add qobject_cast operators for std::shared_ptr Mimicking what we currently have for QSharedPointer, but also adding * snake_case version (matching the ones in std) * rvalue-overloaded versions (matching the C++2a overloads). [ChangeLog][QtCore][QSharedPointer] Overloads of qSharedPointerObjectCast have been added to work on std::shared_ptr. Change-Id: I26ddffd82b000bf876e7c141fdce86a7b8c1d75a Reviewed-by: Thiago Macieira --- src/corelib/tools/qsharedpointer.cpp | 51 +++++++++++++++++ src/corelib/tools/qsharedpointer_impl.h | 42 ++++++++++++++ .../qsharedpointer/tst_qsharedpointer.cpp | 55 +++++++++++++++++++ 3 files changed, 148 insertions(+) diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 62b76c5bb7..0aedf4c6d6 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -1299,6 +1299,57 @@ \sa QSharedPointer::objectCast(), qSharedPointerCast(), qSharedPointerConstCast() */ +/*! + \fn template std::shared_ptr qSharedPointerObjectCast(const std::shared_ptr &src) + \relates QSharedPointer + \since 5.14 + + Returns a shared pointer to the pointer held by \a src, using a + \l qobject_cast() to type \tt X to obtain an internal pointer of the + appropriate type. If the \tt qobject_cast fails, the object + returned will be null. + + Note that \tt X must have the same cv-qualifiers (\tt const and + \tt volatile) that \tt T has, or the code will fail to + compile. Use const_pointer_cast to cast away the constness. +*/ + +/*! + \fn template std::shared_ptr qobject_pointer_cast(const std::shared_ptr &src) + \relates QSharedPointer + \since 5.14 + + Same as qSharedPointerObjectCast(). This function is provided for STL + compatibility. +*/ + +/*! + \fn template std::shared_ptr qSharedPointerObjectCast(std::shared_ptr &&src) + \relates QSharedPointer + \since 5.14 + + Returns a shared pointer to the pointer held by \a src, using a + \l qobject_cast() to type \tt X to obtain an internal pointer of the + appropriate type. + + If the \tt qobject_cast succeeds, the function will return a valid shared + pointer, and \a src is reset to null. If the \tt qobject_cast fails, the + object returned will be null, and \a src will not be modified. + + Note that \tt X must have the same cv-qualifiers (\tt const and + \tt volatile) that \tt T has, or the code will fail to + compile. Use const_pointer_cast to cast away the constness. +*/ + +/*! + \fn template std::shared_ptr qobject_pointer_cast(std::shared_ptr &&src) + \relates QSharedPointer + \since 5.14 + + Same as qSharedPointerObjectCast(). This function is provided for STL + compatibility. +*/ + /*! \fn template template QSharedPointer qSharedPointerObjectCast(const QWeakPointer &src) \relates QSharedPointer diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 81d8dcd839..0851121ff2 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -67,6 +67,8 @@ QT_END_NAMESPACE #endif #include +#include + QT_BEGIN_NAMESPACE @@ -996,6 +998,46 @@ qSharedPointerFromVariant(const QVariant &variant) return qSharedPointerObjectCast(QtSharedPointer::sharedPointerFromVariant_internal(variant)); } +// std::shared_ptr helpers + +template +std::shared_ptr qobject_pointer_cast(const std::shared_ptr &src) +{ + using element_type = typename std::shared_ptr::element_type; + return std::shared_ptr(src, qobject_cast(src.get())); +} + +template +std::shared_ptr qobject_pointer_cast(std::shared_ptr &&src) +{ + using element_type = typename std::shared_ptr::element_type; + auto castResult = qobject_cast(src.get()); + if (castResult) { + auto result = std::shared_ptr(std::move(src), castResult); +#if __cplusplus <= 201703L + // C++2a's move aliasing constructor will leave src empty. + // Before C++2a we don't really know if the compiler has support for it. + // The move aliasing constructor is the resolution for LWG2996, + // which does not impose a feature-testing macro. So: clear src. + src.reset(); +#endif + return result; + } + return std::shared_ptr(); +} + +template +std::shared_ptr qSharedPointerObjectCast(const std::shared_ptr &src) +{ + return qobject_pointer_cast(src); +} + +template +std::shared_ptr qSharedPointerObjectCast(std::shared_ptr &&src) +{ + return qobject_pointer_cast(std::move(src)); +} + #endif template Q_DECLARE_TYPEINFO_BODY(QWeakPointer, Q_MOVABLE_TYPE); diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index 19b2aa02f3..3e87a506bf 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -78,6 +78,7 @@ private slots: void sharedPointerFromQObjectWithWeak(); void weakQObjectFromSharedPointer(); void objectCast(); + void objectCastStdSharedPtr(); void differentPointers(); void virtualBaseDifferentPointers(); #ifndef QTEST_NO_RTTI @@ -1113,6 +1114,60 @@ void tst_QSharedPointer::objectCast() safetyCheck(); } + +void tst_QSharedPointer::objectCastStdSharedPtr() +{ + { + OtherObject *data = new OtherObject; + std::shared_ptr baseptr = std::shared_ptr(data); + QVERIFY(baseptr.get() == data); + + // perform successful object cast + std::shared_ptr ptr = qobject_pointer_cast(baseptr); + QVERIFY(ptr.get()); + QVERIFY(ptr.get() == data); + + QVERIFY(baseptr.get() == data); + } + + { + OtherObject *data = new OtherObject; + std::shared_ptr baseptr = std::shared_ptr(data); + QVERIFY(baseptr.get() == data); + + // perform successful object cast + std::shared_ptr ptr = qobject_pointer_cast(std::move(baseptr)); + QVERIFY(ptr.get()); + QVERIFY(ptr.get() == data); + + QVERIFY(!baseptr.get()); + } + + { + QObject *data = new QObject; + std::shared_ptr baseptr = std::shared_ptr(data); + QVERIFY(baseptr.get() == data); + + // perform unsuccessful object cast + std::shared_ptr ptr = qobject_pointer_cast(baseptr); + QVERIFY(!ptr.get()); + + QVERIFY(baseptr.get() == data); + } + + { + QObject *data = new QObject; + std::shared_ptr baseptr = std::shared_ptr(data); + QVERIFY(baseptr.get() == data); + + // perform unsuccessful object cast + std::shared_ptr ptr = qobject_pointer_cast(std::move(baseptr)); + QVERIFY(!ptr.get()); + + QVERIFY(baseptr.get() == data); + } +} + void tst_QSharedPointer::differentPointers() { {