1+1=10

扬长避短 vs 取长补短

"Qt Macro: Q_DECLARE_METATYPE"

QMetaType

  • It associates a type name to a type ID, enabling construction and destruction to occur dynamically at runtime.
  • QMetaType is used as a helper in QVariant and queued signals and slots connections.

example1:

#include <QtCore>

class MyClass
{
public:
    MyClass() {qDebug("Created");}
    ~MyClass() {qDebug("Destroyed");}
};

int main(int argc, char *argv[])
{
    int id = qRegisterMetaType<MyClass>("MyClass");

    void *myClassPtr = QMetaType::create(id);
    QMetaType::destroy(id, myClassPtr);
    myClassPtr = 0;
}

and example2:

#include <QtCore>

class MyClass
{
public:
    MyClass() {qDebug("Created");}
    MyClass(const MyClass& ){qDebug("Copy");}
    ~MyClass() {qDebug("Destroyed");}
};
Q_DECLARE_METATYPE(MyClass)

int main(int argc, char *argv[])
{
    MyClass cls;
    QVariant var1 = QVariant::fromValue(cls);
    MyClass cls1 = var1.value<MyClass>();
}

The Q_DECLARE_METATYPE() makes the type known to all template based functions, including QVariant. But if we want to use the type in queued signal and slot connections or in QObject's property system, you have to call qRegisterMetaType() since the names are resolved at runtime.

Q_DECLARE_METATYPE

This macro is used to specialise the template class QMetaTypeId with typename TYPE, in which, a static member function qt_metatype_id() is defined.

qRegisterMetaType() is called to register the TYPE and generate a TYPE ID. Then the TYPE ID is saved in local static vairable metatype_id.

#define Q_DECLARE_METATYPE(TYPE)                                        \
    QT_BEGIN_NAMESPACE                                                  \
    template <>                                                         \
    struct QMetaTypeId< TYPE >                                          \
    {                                                                   \
        enum { Defined = 1 };                                           \
        static int qt_metatype_id()                                     \
            {                                                           \
                static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
                if (const int id = metatype_id.loadAcquire())           \
                    return id;                                          \
                const int newId = qRegisterMetaType< TYPE >(#TYPE,      \
                              reinterpret_cast< TYPE *>(quintptr(-1))); \
                metatype_id.storeRelease(newId);                        \
                return newId;                                           \
            }                                                           \
    };                                                                  \
    QT_END_NAMESPACE

Note that, for Qt's builtin types, Q_DECLARE_BUILTIN_METATYPE instead of Q_DECLARE_METATYPE is used. The ids of these types are constant.

#define Q_DECLARE_BUILTIN_METATYPE(TYPE, METATYPEID, NAME) \
    QT_BEGIN_NAMESPACE \
    template<> struct QMetaTypeId2<NAME> \
    { \
        enum { Defined = 1, IsBuiltIn = true, MetaType = METATYPEID };   \
        static inline Q_DECL_CONSTEXPR int qt_metatype_id() { return METATYPEID; } \
    }; \
    QT_END_NAMESPACE

Olivier Goffart said that,

I beleive it has been added so adding builting type do not conflicts with
Q_DECLARE_METATYPE of the same type.

qRegisterMetaType

Information of Qt's builtin types is saved in a static global const struct array types[].

static const struct { const char * typeName; int typeNameLength; int type; } types[] = {
    //  ...
    {0, 0, QMetaType::UnknownType}
};

While the information of the types register through qRegisterMetaType is stored in static QVector with type QCustomTypeInfo

Q_GLOBAL_STATIC(QVector<QCustomTypeInfo>, customTypes)

The definition of QCustomTypeInfo:

class QMetaTypeInterface
{
public:
    QMetaType::Creator creator;
    QMetaType::Deleter deleter;
    QMetaType::SaveOperator saveOp;
    QMetaType::LoadOperator loadOp;
    QMetaType::Constructor constructor;
    QMetaType::Destructor destructor;
    int size;
    quint32 flags; // same as QMetaType::TypeFlags
    const QMetaObject *metaObject;
};

public:
class QCustomTypeInfo : public QMetaTypeInterface
{
public:
    QCustomTypeInfo()
        : alias(-1)
    {
        QMetaTypeInterface empty = QT_METATYPE_INTERFACE_INIT(void);
        *static_cast<QMetaTypeInterface*>(this) = empty;
    }
    QByteArray typeName;
    int alias;
};

qRegisterMetaType() vs qRegisterMetaType(const char *)

When Call qRegisterMetaType() to register the type T. T must be declared with Q_DECLARE_METATYPE() As the member function qt_metatype_id() which is expaned from Q_DECLARE_METATYPE will be called in qMetaTypeId<T>().

template <typename T>
inline Q_DECL_CONSTEXPR int qRegisterMetaType()
{
    return qMetaTypeId<T>();
}

And we can see that, qRegisterMetaType(const char *) is called in qt_metatype_id() too.

template <typename T>
int qRegisterMetaType(const char *typeName
#ifndef Q_QDOC
    , T * dummy = 0
    , typename QtPrivate::MetaTypeDefinedHelper<T, QMetaTypeId2<T>::Defined && !QMetaTypeId2<T>::IsBuiltIn>::DefinedType defined = QtPrivate::MetaTypeDefinedHelper<T, QMetaTypeId2<T>::Defined && !QMetaTypeId2<T>::IsBuiltIn>::Defined
#endif
)
{
    QT_PREPEND_NAMESPACE(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName);
    return qRegisterNormalizedMetaType<T>(normalizedTypeName, dummy, defined);
}

Finally, QCustomTypeInfo will be constructed and added to the static QVector.

    QVector<QCustomTypeInfo> *ct = customTypes();
//...

            QCustomTypeInfo inf;
            inf.typeName = normalizedTypeName;
            inf.creator = creator;
            inf.deleter = deleter;
            //...
            inf.metaObject = metaObject;
            idx = ct->size() + User;
            ct->append(inf);
            return idx;

pointerToTypeDerivedFromQObject ?

Q_DECLARE_METATYPE for QObjectDerived class can be omitted. For example,

#include <QtCore>

class QObjectDerived : public QObject
{
  Q_OBJECT
};
//Q_DECLARE_METATYPE(QObjectDerived*)

class MyClass
{

};
Q_DECLARE_METATYPE(MyClass*)

#include "main.moc"

int main()
{
    qDebug() << qMetaTypeId<MyClass*>();
    qDebug() << qMetaTypeId<QObjectDerived*>();

    return 0;
}

Here, another internal QMetaTypeId* is introduced.

template <typename T>
struct QMetaTypeIdQObject<T*, /* isPointerToTypeDerivedFromQObject */ true>
{
    enum {
        Defined = 1
    };

    static int qt_metatype_id()
    {
        static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
        if (const int id = metatype_id.loadAcquire())
            return id;
        const char * const cName = T::staticMetaObject.className();
        QByteArray typeName;
        typeName.reserve(int(strlen(cName)) + 1);
        typeName.append(cName).append('*');
        const int newId = qRegisterNormalizedMetaType<T*>(
                        typeName,
                        reinterpret_cast<T**>(quintptr(-1)));
        metatype_id.storeRelease(newId);
        return newId;
    }
};

Reference

  • QMetaTypeId and QMetaTypeId2
  • http://steveire.wordpress.com/2011/03/16/implementing-qvariantqmetatype-features-with-template-tricks/
  • http://qt-project.org/wiki/QVariant

Comments