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>
inline T qobject_cast(QObject *object)
{
static_assert(std::is_pointer_v<T>,
"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));
return QtPrivate::qobject_cast_helper<T>(object);
}
template <class T>
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>>,
"qobject_cast cannot cast away constness (use const_cast)");
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));
return QtPrivate::qobject_cast_helper<T>(object);
}
template <class T> constexpr const char * qobject_interface_iid() = delete;
template <class T> inline 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 **));
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

View File

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