- Qt中的Meta Type 包含两种类型,每个类型都以一个整型ID来表示, 内建类型和用户自定义类型(自定义类型对应的整型ID 从65536 即 QMetaType::User 开始):
1)内建类型:
corelib\kernel\qmetatype.h// F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType)
#define QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(F)\F(Bool, 1, bool) \F(Int, 2, int) \F(UInt, 3, uint) \F(LongLong, 4, qlonglong) \F(ULongLong, 5, qulonglong) \F(Double, 6, double) \F(Long, 32, long) \F(Short, 33, short) \F(Char, 34, char) \F(Char16, 56, char16_t) \F(Char32, 57, char32_t) \F(ULong, 35, ulong) \F(UShort, 36, ushort) \F(UChar, 37, uchar) \F(Float, 38, float) \F(SChar, 40, signed char) \F(Nullptr, 51, std::nullptr_t) \F(QCborSimpleType, 52, QCborSimpleType) \#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F) \QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(F) \F(Void, 43, void) \#define QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(F) \F(VoidStar, 31, void*) \#if QT_CONFIG(easingcurve)
#define QT_FOR_EACH_STATIC_EASINGCURVE(F)\F(QEasingCurve, 29, QEasingCurve)
#else
#define QT_FOR_EACH_STATIC_EASINGCURVE(F)
#endif#if QT_CONFIG(itemmodel)
#define QT_FOR_EACH_STATIC_ITEMMODEL_CLASS(F)\F(QModelIndex, 42, QModelIndex) \F(QPersistentModelIndex, 50, QPersistentModelIndex)
#else
#define QT_FOR_EACH_STATIC_ITEMMODEL_CLASS(F)
#endif#if QT_CONFIG(regularexpression)
# define QT_FOR_EACH_STATIC_REGULAR_EXPRESSION(F) \F(QRegularExpression, 44, QRegularExpression)
#else
# define QT_FOR_EACH_STATIC_REGULAR_EXPRESSION(F)
#endif#define QT_FOR_EACH_STATIC_CORE_CLASS(F)\F(QChar, 7, QChar) \F(QString, 10, QString) \F(QByteArray, 12, QByteArray) \F(QBitArray, 13, QBitArray) \F(QDate, 14, QDate) \F(QTime, 15, QTime) \F(QDateTime, 16, QDateTime) \F(QUrl, 17, QUrl) \F(QLocale, 18, QLocale) \F(QRect, 19, QRect) \F(QRectF, 20, QRectF) \F(QSize, 21, QSize) \F(QSizeF, 22, QSizeF) \F(QLine, 23, QLine) \F(QLineF, 24, QLineF) \F(QPoint, 25, QPoint) \F(QPointF, 26, QPointF) \QT_FOR_EACH_STATIC_EASINGCURVE(F) \F(QUuid, 30, QUuid) \F(QVariant, 41, QVariant) \QT_FOR_EACH_STATIC_REGULAR_EXPRESSION(F) \F(QJsonValue, 45, QJsonValue) \F(QJsonObject, 46, QJsonObject) \F(QJsonArray, 47, QJsonArray) \F(QJsonDocument, 48, QJsonDocument) \F(QCborValue, 53, QCborValue) \F(QCborArray, 54, QCborArray) \F(QCborMap, 55, QCborMap) \F(Float16, 63, qfloat16) \QT_FOR_EACH_STATIC_ITEMMODEL_CLASS(F)#define QT_FOR_EACH_STATIC_CORE_POINTER(F)\F(QObjectStar, 39, QObject*)#define QT_FOR_EACH_STATIC_CORE_TEMPLATE(F)\F(QVariantMap, 8, QVariantMap) \F(QVariantList, 9, QVariantList) \F(QVariantHash, 28, QVariantHash) \F(QVariantPair, 58, QVariantPair) \F(QByteArrayList, 49, QByteArrayList) \F(QStringList, 11, QStringList) \#if QT_CONFIG(shortcut)
#define QT_FOR_EACH_STATIC_KEYSEQUENCE_CLASS(F)\F(QKeySequence, 0x100b, QKeySequence)
#else
#define QT_FOR_EACH_STATIC_KEYSEQUENCE_CLASS(F)
#endif#define QT_FOR_EACH_STATIC_GUI_CLASS(F)\F(QFont, 0x1000, QFont) \F(QPixmap, 0x1001, QPixmap) \F(QBrush, 0x1002, QBrush) \F(QColor, 0x1003, QColor) \F(QPalette, 0x1004, QPalette) \F(QIcon, 0x1005, QIcon) \F(QImage, 0x1006, QImage) \F(QPolygon, 0x1007, QPolygon) \F(QRegion, 0x1008, QRegion) \F(QBitmap, 0x1009, QBitmap) \F(QCursor, 0x100a, QCursor) \QT_FOR_EACH_STATIC_KEYSEQUENCE_CLASS(F) \F(QPen, 0x100c, QPen) \F(QTextLength, 0x100d, QTextLength) \F(QTextFormat, 0x100e, QTextFormat) \F(QTransform, 0x1010, QTransform) \F(QMatrix4x4, 0x1011, QMatrix4x4) \F(QVector2D, 0x1012, QVector2D) \F(QVector3D, 0x1013, QVector3D) \F(QVector4D, 0x1014, QVector4D) \F(QQuaternion, 0x1015, QQuaternion) \F(QPolygonF, 0x1016, QPolygonF) \F(QColorSpace, 0x1017, QColorSpace) \#define QT_FOR_EACH_STATIC_WIDGETS_CLASS(F)\F(QSizePolicy, 0x2000, QSizePolicy) \// F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, AliasingType, "RealType")
#define QT_FOR_EACH_STATIC_ALIAS_TYPE(F)\F(ULong, -1, ulong, "unsigned long") \F(UInt, -1, uint, "unsigned int") \F(UShort, -1, ushort, "unsigned short") \F(UChar, -1, uchar, "unsigned char") \F(LongLong, -1, qlonglong, "long long") \F(ULongLong, -1, qulonglong, "unsigned long long") \F(SChar, -1, signed char, "qint8") \F(UChar, -1, uchar, "quint8") \F(Short, -1, short, "qint16") \F(UShort, -1, ushort, "quint16") \F(Int, -1, int, "qint32") \F(UInt, -1, uint, "quint32") \F(LongLong, -1, qlonglong, "qint64") \F(ULongLong, -1, qulonglong, "quint64") \F(QVariantList, -1, QVariantList, "QList<QVariant>") \F(QVariantMap, -1, QVariantMap, "QMap<QString,QVariant>") \F(QVariantHash, -1, QVariantHash, "QHash<QString,QVariant>") \F(QVariantPair, -1, QVariantPair, "QPair<QVariant,QVariant>") \F(QByteArrayList, -1, QByteArrayList, "QList<QByteArray>") \F(QStringList, -1, QStringList, "QList<QString>") \#define QT_FOR_EACH_STATIC_TYPE(F)\QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(F)\QT_FOR_EACH_STATIC_CORE_CLASS(F)\QT_FOR_EACH_STATIC_CORE_POINTER(F)\QT_FOR_EACH_STATIC_CORE_TEMPLATE(F)\QT_FOR_EACH_STATIC_GUI_CLASS(F)\QT_FOR_EACH_STATIC_WIDGETS_CLASS(F)\#define QT_DEFINE_METATYPE_ID(TypeName, Id, Name) \TypeName = Id,#define QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(F) \F(QList) \F(QQueue) \F(QStack) \F(QSet) \/*end*/#define QT_FOR_EACH_AUTOMATIC_TEMPLATE_2ARG(F) \F(QHash, class) \F(QMap, class)#define QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(F) \F(QSharedPointer) \F(QWeakPointer) \F(QPointer)class Q_CORE_EXPORT QMetaType {
public:
#ifndef Q_QDOC// The code that actually gets compiled.enum Type {// these are merged with QVariantQT_FOR_EACH_STATIC_TYPE(QT_DEFINE_METATYPE_ID)FirstCoreType = Bool,LastCoreType = Float16,FirstGuiType = QFont,LastGuiType = QColorSpace,FirstWidgetsType = QSizePolicy,LastWidgetsType = QSizePolicy,HighestInternalId = LastWidgetsType,QReal = sizeof(qreal) == sizeof(double) ? Double : Float,UnknownType = 0,User = 65536};
#else// If we are using QDoc it fakes the Type enum looks like this.enum Type {UnknownType = 0, Bool = 1, Int = 2, UInt = 3, LongLong = 4, ULongLong = 5,Double = 6, Long = 32, Short = 33, Char = 34, ULong = 35, UShort = 36,UChar = 37, Float = 38,VoidStar = 31,QChar = 7, QString = 10, QStringList = 11, QByteArray = 12,QBitArray = 13, QDate = 14, QTime = 15, QDateTime = 16, QUrl = 17,QLocale = 18, QRect = 19, QRectF = 20, QSize = 21, QSizeF = 22,QLine = 23, QLineF = 24, QPoint = 25, QPointF = 26,QEasingCurve = 29, QUuid = 30, QVariant = 41, QModelIndex = 42,QPersistentModelIndex = 50, QRegularExpression = 44,QJsonValue = 45, QJsonObject = 46, QJsonArray = 47, QJsonDocument = 48,QByteArrayList = 49, QObjectStar = 39, SChar = 40,Void = 43,Nullptr = 51,QVariantMap = 8, QVariantList = 9, QVariantHash = 28, QVariantPair = 58,QCborSimpleType = 52, QCborValue = 53, QCborArray = 54, QCborMap = 55,Char16 = 56, Char32 = 57,Int128 = 59, UInt128 = 60, Float128 = 61, BFloat16 = 62, Float16 = 63,// Gui typesQFont = 0x1000, QPixmap = 0x1001, QBrush = 0x1002, QColor = 0x1003, QPalette = 0x1004,QIcon = 0x1005, QImage = 0x1006, QPolygon = 0x1007, QRegion = 0x1008, QBitmap = 0x1009,QCursor = 0x100a, QKeySequence = 0x100b, QPen = 0x100c, QTextLength = 0x100d, QTextFormat = 0x100e,QTransform = 0x1010, QMatrix4x4 = 0x1011, QVector2D = 0x1012,QVector3D = 0x1013, QVector4D = 0x1014, QQuaternion = 0x1015, QPolygonF = 0x1016, QColorSpace = 0x1017,// Widget typesQSizePolicy = 0x2000,// Start-point for client-code types:User = 65536};
2)自定义类型,需要向customRegistry中注册用户自定义类型信息:
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \QT_WARNING_PUSH \QT_WARNING_DISABLE_CLANG("-Wunevaluated-expression") \namespace { struct Q_QGS_ ## NAME { \typedef TYPE QGS_Type; \static void innerFunction(void *pointer) \noexcept(noexcept(std::remove_cv_t<QGS_Type> ARGS)) \{ \new (pointer) QGS_Type ARGS; \} \}; } \Q_CONSTINIT static QGlobalStatic<QtGlobalStatic::Holder<Q_QGS_ ## NAME>> NAME; \QT_WARNING_POP/**/#define Q_GLOBAL_STATIC(TYPE, NAME, ...) \Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, (__VA_ARGS__))Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)宏定义替换后会得到一个静态全局变量 customTypeRegistry的声明,类型为QGlobalStatic<QtGlobalStatic::Holder<Q_QGS_customTypeRegistry >>, 替换后的代码如下:namespace
{ struct Q_QGS_customTypeRegistry{ typedef TYPE QMetaTypeCustomRegistry; static void innerFunction(void *pointer) noexcept(noexcept(QMetaTypeCustomRegistry ())) { new (pointer) QMetaTypeCustomRegistry() ; } };
}Q_CONSTINIT static QGlobalStatic<QtGlobalStatic::Holder<Q_QGS_customTypeRegistry >> customTypeRegistry; 在QMetaTypeCustomRegistry类中提供了注册自定义类型的接口:
struct QMetaTypeCustomRegistry
{#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)QMetaTypeCustomRegistry(){/* qfloat16 was neither a builtin, nor unconditionally registeredin QtCore in Qt <= 6.2.Inserting it as an alias ensures that a QMetaType::id callwill get the correct built-in type-id (the interface pointersmight still not match, but we already deal with that case.*/aliases.insert("qfloat16", QtPrivate::qMetaTypeInterfaceForType<qfloat16>());}
#endifQReadWriteLock lock;QList<const QtPrivate::QMetaTypeInterface *> registry;QHash<QByteArray, const QtPrivate::QMetaTypeInterface *> aliases;// index of first empty (unregistered) type in registry, if any.int firstEmpty = 0;int registerCustomType(const QtPrivate::QMetaTypeInterface *cti){// we got here because cti->typeId is 0, so this is a custom meta type// (not read-only)auto ti = const_cast<QtPrivate::QMetaTypeInterface *>(cti);{QWriteLocker l(&lock);if (int id = ti->typeId.loadRelaxed())return id;QByteArray name =
#ifndef QT_NO_QOBJECTQMetaObject::normalizedType
#endif(ti->name);if (auto ti2 = aliases.value(name)) {const auto id = ti2->typeId.loadRelaxed();ti->typeId.storeRelaxed(id);return id;}aliases[name] = ti;int size = registry.size();while (firstEmpty < size && registry[firstEmpty])++firstEmpty;if (firstEmpty < size) {registry[firstEmpty] = ti;++firstEmpty;} else {registry.append(ti);firstEmpty = registry.size();}ti->typeId.storeRelaxed(firstEmpty + QMetaType::User);}if (ti->legacyRegisterOp)ti->legacyRegisterOp();return ti->typeId.loadRelaxed();};.........
.........
.........
};
- 在信号槽机制中,如果使用QueuedConnection连接类型,当信号和槽中使用了自定义数据类型传递参数时需要首先注册自定义数据类型,如:
qRegisterMetaType("Add");
至于为何需要注册自定义类型,个人理解理由如下:
在QueuedConnection 连接方式下 信号触发时 并非 对 槽函数的同步调用,而是封装一个QMetaCallEvent 以post方式放到连接队列中 等待后续EventLoop循环中对取对应的Event处理。信号函数调用结束后 栈帧已经销毁,无法在后续的Eventloop 循环中从栈中拿到对应的参数数据。
因此,比较自然的方法就是将要传递的函数参数保存在 堆上,这样不会随信号函数调用的结束而丢失要传递的参数。保存在堆上的参数内存地址 存放在上述的MetaCallEvent对象中,这样在后续循环中执行槽函数时可以获取到需要的函数参数数据。
又由于Qt 针对信号槽机制中传递参数为了更通用,因此创建链接的内部框架代码中 参数都是以 void* 类型来声明的。因此在传递给Qt框架源码时都将 用户的数据强制转换为了 void*。例如如下代码所示:
由于传递进去的是void* 类型的参数,因此要将 该参数 拷贝到 堆上保存,这时就需要知道要拷贝多少个字节。为了需要这个字节大小信息 ,因此需要有个地方记录,而记录字节大小的信息就以QMetaType的形式存放在customTypeRegistry中。Qt框架中记录了信号和槽函数中参数个数,参数类型信息。在使用自定义数据类型前先注册,注册时会在注册表中给这个新的用户自定义类型信息分配一个 整型ID,同时会根据该数据类型是否是 non-trival class 为其声明和定义copyCtr,moveCtr,等拷贝构造,移动构造信息。如下代码:
template<typename T>
struct QMetaTypeInterfaceWrapper
{// if the type ID for T is known at compile-time, then we can declare// the QMetaTypeInterface object const; otherwise, we declare it as// non-const and the .typeId is updated by QMetaType::idHelper().static constexpr bool IsConstMetaTypeInterface = !!BuiltinMetaType<T>::value;using InterfaceType = std::conditional_t<IsConstMetaTypeInterface, const QMetaTypeInterface, NonConstMetaTypeInterface>;static inline InterfaceType metaType = {/*.revision=*/ QMetaTypeInterface::CurrentRevision,/*.alignment=*/ alignof(T),/*.size=*/ sizeof(T),/*.flags=*/ QMetaTypeForType<T>::Flags,/*.typeId=*/ BuiltinMetaType<T>::value,/*.metaObjectFn=*/ MetaObjectForType<T>::metaObjectFunction,/*.name=*/ QMetaTypeForType<T>::getName(),/*.defaultCtr=*/ QMetaTypeForType<T>::getDefaultCtr(),/*.copyCtr=*/ QMetaTypeForType<T>::getCopyCtr(),/*.moveCtr=*/ QMetaTypeForType<T>::getMoveCtr(),/*.dtor=*/ QMetaTypeForType<T>::getDtor(),/*.equals=*/ QEqualityOperatorForType<T>::equals,/*.lessThan=*/ QLessThanOperatorForType<T>::lessThan,/*.debugStream=*/ QDebugStreamOperatorForType<T>::debugStream,/*.dataStreamOut=*/ QDataStreamOperatorForType<T>::dataStreamOut,/*.dataStreamIn=*/ QDataStreamOperatorForType<T>::dataStreamIn,/*.legacyRegisterOp=*/ QMetaTypeForType<T>::getLegacyRegister()};
};template<typename S>
class QMetaTypeForType
{
public:static constexpr decltype(typenameHelper<S>()) name = typenameHelper<S>();static constexpr unsigned Flags = QMetaTypeTypeFlags<S>::Flags;static constexpr QMetaTypeInterface::DefaultCtrFn getDefaultCtr(){if constexpr (std::is_default_constructible_v<S> && !QTypeInfo<S>::isValueInitializationBitwiseZero) {return [](const QMetaTypeInterface *, void *addr) { new (addr) S(); };} else {return nullptr;}}static constexpr QMetaTypeInterface::CopyCtrFn getCopyCtr(){if constexpr (std::is_copy_constructible_v<S> && !std::is_trivially_copy_constructible_v<S>) {return [](const QMetaTypeInterface *, void *addr, const void *other) {new (addr) S(*reinterpret_cast<const S *>(other));};} else {return nullptr;}}static constexpr QMetaTypeInterface::MoveCtrFn getMoveCtr(){if constexpr (std::is_move_constructible_v<S> && !std::is_trivially_move_constructible_v<S>) {return [](const QMetaTypeInterface *, void *addr, void *other) {new (addr) S(std::move(*reinterpret_cast<S *>(other)));};} else {return nullptr;}}static constexpr QMetaTypeInterface::DtorFn getDtor(){if constexpr (std::is_destructible_v<S> && !std::is_trivially_destructible_v<S>)return [](const QMetaTypeInterface *, void *addr) {reinterpret_cast<S *>(addr)->~S();};elsereturn nullptr;}
}
===========================================================
C++ trivial和non-trivial构造函数及POD类型今天看书看到侯捷的《STL源码剖析》里提到trivial和non-trivial及POD类型,查了些资料理解了一下。trivial意思是无意义,这个trivial和non-trivial是对类的四种函数来说的:构造函数(ctor)
复制构造函数(copy)
赋值函数(assignment)
析构函数(dtor)
如果至少满足下面3条里的一条:显式(explict)定义了这四种函数。
类里有非静态非POD的数据成员。
有基类。
那么上面的四种函数是non-trivial函数,比如叫non-trivial ctor、non-trivial copy…,也就是说有意义的函数,里面有一下必要的操作,比如类成员的初始化,释放内存等。那个POD意思是Plain Old Data,也就是C++的内建类型或传统的C结构体类型。POD类型必然有trivial ctor/dtor/copy/assignment四种函数。//整个T是POD类型
class T
{//没有显式定义ctor/dtor/copy/assignemt所以都是trivialint a; //POD类型
};//整个T1是非POD类型
class T1
{T1() //显式定义了构造函数,所以是non-trivial ctor{}//没有显式定义ctor/dtor/copy/assignemt所以都是trivialint a;//POD类型std::string b; //非POD类型
};
那这有什么用处呢?如果这个类都是trivial ctor/dtor/copy/assignment函数,我们对这个类进行构造、析构、拷贝和赋值时可以采用最有效率的方法,不调用无所事事正真的那些ctor/dtor等,而直接采用内存操作如malloc()、memcpy()等提高性能,这也是SGI STL内部干的事情。比如STL的copy算法最基本的想法是这样的:// 非POD重载指针数值
template <class T> void copy(T* source, T* destination, int n, __false_type)
{// 省略异常处理for (; n > 0; n--,source++,destination++){// 调用source的复制构造函数constructor(source, *destination);}
}// POD重载指针数值
template <class T> void copy(T* source, T* destination, int n, __false_type)
{// 省略异常处理memmove(source, destination, n);
}
当然实际的copy比这个复杂多了,有非常多的特化等,这个只是其中一方面而已。