qobject_cast: optimize when the target is marked final

If it is, then we can perform a direct meta object pointer comparison.
Comparing meta objects is, after all, what QMetaObject::inherits() and
QMetaObject::cast()) are doing:

    const QMetaObject *m = this;
    do {
        if (metaObject == m)
            return true;
    } while ((m = m->d.superdata));

But if we know the class is final, we know an object can either be
exactly of the type we want or not be so at all.

Change-Id: If6333547d215c1d84d1cfffdfd82c04e75251608
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Thiago Macieira 2025-01-30 12:57:38 -08:00 committed by Marc Mutz
parent 01ad117b32
commit 8a029843a3
3 changed files with 48 additions and 13 deletions

View File

@ -420,28 +420,17 @@ bool QObject::setProperty(const char *name, QVariant &&value)
template <class T> template <class T>
inline T qobject_cast(QObject *object) inline T qobject_cast(QObject *object)
{ {
static_assert(std::is_pointer_v<T>, return QtPrivate::qobject_cast_helper<T>(object);
"qobject_cast requires to cast towards a pointer type");
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
"qobject_cast requires the type to have a Q_OBJECT macro");
return static_cast<T>(ObjType::staticMetaObject.cast(object));
} }
template <class T> template <class T>
inline T qobject_cast(const QObject *object) inline T qobject_cast(const QObject *object)
{ {
static_assert(std::is_pointer_v<T>,
"qobject_cast requires to cast towards a pointer type");
static_assert(std::is_const_v<std::remove_pointer_t<T>>, static_assert(std::is_const_v<std::remove_pointer_t<T>>,
"qobject_cast cannot cast away constness (use const_cast)"); "qobject_cast cannot cast away constness (use const_cast)");
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType; return QtPrivate::qobject_cast_helper<T>(object);
static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
"qobject_cast requires the type to have a Q_OBJECT macro");
return static_cast<T>(ObjType::staticMetaObject.cast(object));
} }
template <class T> constexpr const char * qobject_interface_iid() = delete; template <class T> constexpr const char * qobject_interface_iid() = delete;
template <class T> inline T * template <class T> inline T *
qobject_iid_cast(QObject *object, const char *IId = qobject_interface_iid<T *>()) qobject_iid_cast(QObject *object, const char *IId = qobject_interface_iid<T *>())

View File

@ -737,6 +737,24 @@ namespace QtPrivate {
static int test(int (Object::*)(QMetaObject::Call, int, void **)); static int test(int (Object::*)(QMetaObject::Call, int, void **));
enum { Value = sizeof(test(&Object::qt_metacall)) == sizeof(int) }; enum { Value = sizeof(test(&Object::qt_metacall)) == sizeof(int) };
}; };
template <class TgtType, class SrcType>
inline TgtType qobject_cast_helper(SrcType *object)
{
using ObjType = std::remove_cv_t<std::remove_pointer_t<TgtType>> ;
static_assert(std::is_pointer_v<TgtType>,
"qobject_cast requires to cast towards a pointer type");
static_assert(HasQ_OBJECT_Macro<ObjType>::Value,
"qobject_cast requires the type to have a Q_OBJECT macro");
if constexpr (std::is_final_v<ObjType>) {
if (object && object->metaObject() == &ObjType::staticMetaObject)
return static_cast<TgtType>(object);
return nullptr;
} else {
return static_cast<TgtType>(ObjType::staticMetaObject.cast(object));
}
}
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -92,6 +92,7 @@ private slots:
void dumpObjectTree(); void dumpObjectTree();
void connectToSender(); void connectToSender();
void qobjectConstCast(); void qobjectConstCast();
void qobjectCastFinal();
void uniqConnection(); void uniqConnection();
void uniqConnectionPtr(); void uniqConnectionPtr();
void interfaceIid(); void interfaceIid();
@ -2429,6 +2430,13 @@ public:
int rtti() const override { return 43; } int rtti() const override { return 43; }
}; };
class FinalObject final: public FooObject
{
Q_OBJECT
public:
int rtti() const override { return 44; }
};
void tst_QObject::declareInterface() void tst_QObject::declareInterface()
{ {
FooObject obj; FooObject obj;
@ -3691,9 +3699,29 @@ void tst_QObject::qobjectConstCast()
const QObject *cptr = &obj; const QObject *cptr = &obj;
QVERIFY(qobject_cast<FooObject *>(ptr)); QVERIFY(qobject_cast<FooObject *>(ptr));
QVERIFY(qobject_cast<const FooObject *>(ptr));
QVERIFY(qobject_cast<const FooObject *>(cptr)); QVERIFY(qobject_cast<const FooObject *>(cptr));
} }
void tst_QObject::qobjectCastFinal()
{
FooObject foo;
QObject *ptr = &foo;
const QObject *cptr = &foo;
QCOMPARE(qobject_cast<FinalObject *>(ptr), nullptr);
QCOMPARE(qobject_cast<const FinalObject *>(ptr), nullptr);
QCOMPARE(qobject_cast<const FinalObject *>(cptr), nullptr);
FinalObject final;
ptr = &final;
cptr = &final;
QCOMPARE(qobject_cast<FinalObject *>(ptr), &final);
QCOMPARE(qobject_cast<const FinalObject *>(ptr), &final);
QCOMPARE(qobject_cast<const FinalObject *>(cptr), &final);
}
void tst_QObject::uniqConnection() void tst_QObject::uniqConnection()
{ {
SenderObject s; SenderObject s;