How does qobject_cast work?

29,005

This is a little complicated...

Remember that qobject_cast<T>(obj) is a way to dynamically cast a QObject to the target type T which also derives from QObject. Now, for this to work, the macro Q_OBJECT should be included in the definition of class T.

Apparently, the qt_check_for_QOBJECT_macro call is for checking that the class really contains the Q_OBJECT macro. When the macro is expanded, it contains the following definitions:

template <typename T> inline void qt_check_for_QOBJECT_macro(const T &_q_argument) const 
   { int i = qYouForgotTheQ_OBJECT_Macro(this, &_q_argument); i = i; }

template <typename T1, typename T2>
inline int qYouForgotTheQ_OBJECT_Macro(T, T) { return 0; }

So if you have an object x of type T and an object y of type U, the call x->qt_check_for_QOBJECT_macro(y) calls the function qYouForgotTheQ_OBJECT_Macro with parameters of types T* and U*. Because the function is templated with a single type parameter, the types T and U must be the same.

Now, if you call x->qt_check_for_QOBJECT_macro(x) then you should expect the types to be the same and for the compilation to trivially succeed. However, remember that this has the same type as the class the method was defined in. So if x is of a class that was derived from T but doesn't contain its own definition of qt_check_for_QOBJECT_macro, the call will fail.

So we have a way to check if the target type T contains the correct mechanism for the dynamic cast, but we don't have a object of type T to call this method on yet. That's what the reinterpret_cast<T>(0) is for. We don't need an actual object as this, since the compiler only needs the object types for the check to succeed. Instead, we call a method on a null pointer of type T.

I don't think this is allowed by the C++ standard, but it works since this isn't actually used inside the method.

Share:
29,005
ronag
Author by

ronag

Master of Science (M.Sc.), Software Engineering and Technology at Chalmers University of Technology. I wrote most of CasparCG 2.0 Server, an open-source video- and graphics playout server used by the Swedish Broadcasting Corporation 24/4 for all regional and national broadcasts in Sweden. Big fan of the ffmpeg project.

Updated on January 06, 2020

Comments

  • ronag
    ronag over 4 years

    I just found the following code in Qt and I'm a bit confused what's happening here.

    Especially as to what reinterpret_cast<T>(0) does?

    template <class T>
    inline T qobject_cast(const QObject *object)
    {
        // this will cause a compilation error if T is not const
        register T ptr = static_cast<T>(object);
        Q_UNUSED(ptr);
    
    #if !defined(QT_NO_MEMBER_TEMPLATES) && !defined(QT_NO_QOBJECT_CHECK)
        reinterpret_cast<T>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<T>(const_cast<QObject *>(object)));
    #endif
        return static_cast<T>(const_cast<QObject *>(reinterpret_cast<T>(0)->staticMetaObject.cast(const_cast<QObject *>(object))));
    }
    

    Anyone care to explain?

  • Samuel Harmer
    Samuel Harmer over 12 years
    Could you clarify which part you don't think is allowed by the C++ standard? I'm curious.
  • Matthew Read
    Matthew Read over 11 years
    The method is static, it's not called "on" the pointer. this would reference the caller and not a non-existent object.
  • Amit Tomar
    Amit Tomar over 11 years
    Now, for this to work, the macro Q_OBJECT should be included in the definition of class T. This might be wrong. Qt document here says While it is possible to use QObject as a base class without the Q_OBJECT macro and without meta-object code, neither signals and slots nor the other features described here will be available if the Q_OBJECT macro is not used.