qt creator 4.8.0 /qt version 5.12.0
有人将qt的meta系统称为“元对象系统”、“元系统”,“MetaObject系统”,我这里统称为meta系统。meta系统除了包含MetaObject类及其还有很多独特的数据结构和方法,并且强烈依赖moc生成meta系统所必须的一些代码和数据。所以我称之为meta系统。
希望让继承自QObject类的meta系统中拥有constructor相关信息,能让QMetaObject通过NewInstance生成类对象,需要在构造函数前加Q_INVOKABLE宏。这样类的构造函数才能进入MetaObect系统。
//base.h
class Base : public QObject
{
Q_OBJECT
Q_CLASSINFO("name","fund")
public:
enum XType{AA,BB,CC};
Q_ENUM(XType);
//Q_INVOKABLE宏让构造函数进入meta系统中
Q_INVOKABLE explicit Base(QObject *parent = nullptr);
Q_INVOKABLE Base(int a,int b,QObject *parent = nullptr);
Q_INVOKABLE Base(int a,QObject *parent = nullptr);
Q_INVOKABLE void fun();
void funt();
signals:
void sig(float c,char d);
void sig1();
void sig2();
public slots:
void fun1(float a,char b);
void fun2();
private:
QTimer t;
int a;
};
};
--------------------------------------------------
//moc_base.cpp
static const qt_meta_stringdata_Base_t qt_meta_stringdata_Base = {
{
QT_MOC_LITERAL(0, 0, 4), // "Base"
QT_MOC_LITERAL(1, 5, 4), // "name"
QT_MOC_LITERAL(2, 10, 4), // "fund"
QT_MOC_LITERAL(3, 15, 3), // "sig"
QT_MOC_LITERAL(4, 19, 0), // ""
QT_MOC_LITERAL(5, 20, 1), // "c"
QT_MOC_LITERAL(6, 22, 1), // "d"
QT_MOC_LITERAL(7, 24, 4), // "sig1"
QT_MOC_LITERAL(8, 29, 4), // "sig2"
QT_MOC_LITERAL(9, 34, 4), // "fun1"
QT_MOC_LITERAL(10, 39, 1), // "a"
QT_MOC_LITERAL(11, 41, 1), // "b"
QT_MOC_LITERAL(12, 43, 4), // "fun2"
QT_MOC_LITERAL(13, 48, 3), // "fun" //Q_INVOKABLE将普通函数纳入到了meta系统中
QT_MOC_LITERAL(14, 52, 6), // "parent"
QT_MOC_LITERAL(15, 59, 5), // "XType"
QT_MOC_LITERAL(16, 65, 2), // "AA"
QT_MOC_LITERAL(17, 68, 2), // "BB"
QT_MOC_LITERAL(18, 71, 2) // "CC"
},
"Base\0name\0fund\0sig\0\0c\0d\0sig1\0sig2\0"
"fun1\0a\0b\0fun2\0fun\0parent\0XType\0AA\0BB\0"
"CC"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_Base[] = {
// content:
8, // revision
0, // classname
1, 14, // classinfo
6, 16, // methods //methodcount methoddata(qt_meta_data_Base[16])
0, 0, // properties
1, 84, // enums/sets //enumcount enumdata
//注意这里的constructors数量是6。带默认参数的构造函数被拆分成带该参数和不带该参数两个函数去了。
6, 95, // constructors
0, // flags
3, // signalCount
//下面数组中key,value项表示在qt_meta_stringdata_Base中该函数名字的下标
// classinfo: key, value
1, 2,
//下面数组中name项表示在qt_meta_stringdata_Base中该函数名字的下标,argc表示参数个数,
//parameters表示该函数参数的起始描述在qt_meta_data_Base中的下标,
//tag描述tag在qt_meta_stringdata_Base中该tag的名字下标,flags为qmetaobject_p.h中
//enum MethodFlags类型数据的组合
// signals: name, argc, parameters, tag, flags
3, 2, 46, 4, 0x06 /* Public */,
7, 0, 51, 4, 0x06 /* Public */,
8, 0, 52, 4, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
9, 2, 53, 4, 0x0a /* Public */,
12, 0, 58, 4, 0x0a /* Public */,
//类中被Q_INVOCABLE的普通函数在qt_meta_data_Base中的存放记录
// methods: name, argc, parameters, tag, flags
13, 0, 59, 4, 0x02 /* Public */,
//5,6 分别表示参数名字在qt_meta_stringdata_Base中的下标
// signals: parameters
QMetaType::Void, QMetaType::Float, QMetaType::Char, 5, 6,
QMetaType::Void,
QMetaType::Void,
// slots: parameters
QMetaType::Void, QMetaType::Float, QMetaType::Char, 10, 11,
QMetaType::Void,
// methods: parameters
QMetaType::Void,
//构造函数被Q_INVOKABLE修饰后在qt_meta_data_Base中的存放记录
// constructors: parameters
0x80000000 | 4, QMetaType::QObjectStar, 14, //带 有默认参数值的构造函数
0x80000000 | 4, //不带 有默认参数值的构造函数
0x80000000 | 4, QMetaType::Int, QMetaType::Int, QMetaType::QObjectStar, 10, 11, 14,
0x80000000 | 4, QMetaType::Int, QMetaType::Int, 10, 11,
0x80000000 | 4, QMetaType::Int, QMetaType::QObjectStar, 10, 14,
0x80000000 | 4, QMetaType::Int, 10,
// enums: name, alias, flags, count, data
15, 15, 0x0, 3, 89,
// enum data: key, value
16, uint(Base::AA),
17, uint(Base::BB),
18, uint(Base::CC),
// constructors: name, argc, parameters, tag, flags
0, 1, 60, 4, 0x0e /* Public */,
0, 0, 63, 4, 0x2e /* Public | MethodCloned */,
0, 3, 64, 4, 0x0e /* Public */,
0, 2, 71, 4, 0x2e /* Public | MethodCloned */,
0, 2, 76, 4, 0x0e /* Public */,
0, 1, 81, 4, 0x2e /* Public | MethodCloned */,
0 // eod
};
--------------------------------------------------
int main()
{
Base ba;
QMetaObject qme= ba.staticMetaObject;
int count = qme.methodCount();
for(int i=0;i<count;i++)
{
QMetaMethod me = qme.method(i);
qDebug()<<me.name()<<endl;
}
}
/*输出:
"destroyed"
"destroyed"
"objectNameChanged"
"deleteLater"
"_q_reregisterTimers"
//前面5个是QObject中的method,method包括qt_meta_data_Base中的signals,slots,methods
"sig"
"sig1"
"sig2"
"fun1"
"fun2"
"fun"
*/
Q_INVOKABLE的作用还可以将普通函数也纳入meta系统中。这样可以通过QMetaObject 的invokemethod方法来调用对应函数。这种方式可以让代码系统变得轻类型的,主要应用于RPC的时候。
QMetaObjectPrivate定义在Src\qtbase\src\corelib\kernel\qmetaobject_p.h 中,QMetaObject实现过程中对QMetaObjectPrivate的依赖非常隐秘。QMetaObject没有显示的声明QMetaObjectPrivate相关的数据成员,而是在使用时隐秘的将QMetaObject的数据成员d->data转成QMetaObjectPrivate对象后直接使用。举例如下。
//moc_base.cpp中meta_data的定义
static const uint qt_meta_data_Base[] = {
// content:
8, // revision
0, // classname
1, 14, // classinfo
6, 16, // methods
1, 84, // properties
1, 87, // enums/sets
6, 98, // constructors
0, // flags
3, // signalCount
// classinfo: key, value
1, 2,
// signals: name, argc, parameters, tag, flags
3, 2, 46, 4, 0x06 /* Public */,
7, 0, 51, 4, 0x06 /* Public */,
8, 0, 52, 4, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
9, 2, 53, 4, 0x0a /* Public */,
12, 0, 58, 4, 0x0a /* Public */,
// methods: name, argc, parameters, tag, flags
13, 0, 59, 4, 0x02 /* Public */,
.....
}
//对staticMetaObject 对象的数据成员d赋值。
QT_INIT_METAOBJECT const QMetaObject Base::staticMetaObject = { {
&B::staticMetaObject,
qt_meta_stringdata_Base.data,
qt_meta_data_Base, //meta_data赋予给staticmetaObject对象成员d->data
qt_static_metacall,
nullptr,
nullptr
} };
--------------------------------------------------
//qmetaobject_p.h中定义QMetaObjectPrivate类
struct QMetaObjectPrivate
{
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
int flags;
int signalCount;
//sizeof(QMetaObjectPrivate)等于从revision到signalCount一共14个数据成员大小
........
}
--------------------------------------------------
//qobjectdefs.h中定义QMetaObject类
struct Q_CORE_EXPORT QMetaObject
{
......
struct { // private data
const QMetaObject *superdata;
const QByteArrayData *stringdata;
const uint *data;
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const QMetaObject * const *relatedMetaObjects;
void *extradata; //reserved for future use
} d; //QMetaObject的数据成员d,也是唯一的数据成员。sizeof(QMetaObject) == sizeof(d)
}
--------------------------------------------------
//qmetaobject.cpp
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
//qmetaObject与QmetaObjectPrivate没有直接的显示的关联,其关联用法如下。
int QMetaObject::methodOffset() const
{
int offset = 0;
const QMetaObject *m = d.superdata;
while (m) {
//通过priv函数将uint*data强制转换成QmetaObjectPrivate对象,并获取到对象的数据成员。
offset += priv(m->d.data)->methodCount;
m = m->d.superdata;
}
return offset;
}
/*
......
// signals: name, argc, parameters, tag, flags
3, 2, 46, 4, 0x06 /* Public */,
7, 0, 51, 4, 0x06 /* Public */,
8, 0, 52, 4, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
9, 2, 53, 4, 0x0a /* Public */,
12, 0, 58, 4, 0x0a /* Public */,
// methods: name, argc, parameters, tag, flags
13, 0, 59, 4, 0x02 /* Public */,
......
*/
//从qt_meta_data_Base中获取函数参数个数。本质就是获取signals/slots/methods的argc项
int QMetaMethodPrivate::parameterCount() const
{
Q_ASSERT(priv(mobj->d.data)->revision >= 7);
return mobj->d.data[handle + 1];
}
可以确认,QPrivateMetaObject对象强制转换实例化只使用了qt_meta_data_Base的content部分,qt_meta_data_Base后面的数据(content之后的数据)都是根据QPrivateMetaObject、QMetaMethod、QMetaMethodPrivate、QMetaObject中的方法进行解析。
QmetaMethodPrivate定义在qmetaobject.cpp中。
qt meta系统中为每一个标记为meta系统的函数都预留了一个tag项,这里的tag(标记)是一个极为宽泛的概念,他包含了C++关键字、C++属性修饰词、自定义类名等等,tag项默认值为“”(空字符串),正常也应该为“”(空字符串),但是当qt遇到C++中合法而moc的语法器无法识别(可能qt开发者忘记写了)的symbol(具有特殊意义的字符串)时,就将该symbol当做一个tag(标记)。比如为slot或signal或Q_INVOKABLE标记的函数 添加__stdcall 或__cdecl (函数调用规范) 属性 修饰时,就会出现将__stdcall 或__cdecl标记 当做未识别的tag的情况,虽然最后程序会报错。
//A.h
class A{
Q_OBJECT
public:
B(QObject *parent = nullptr);
signals :
void sig();
void __stdcall sig1();//(int a); //构建生成moc_A.cpp后会报错
};
-----------------------------
//moc_A.cpp
static const qt_meta_stringdata_A_t qt_meta_stringdata_A = {
{
QT_MOC_LITERAL(0, 0, 1), // "A"
QT_MOC_LITERAL(1, 2, 3), // "sig"
QT_MOC_LITERAL(2, 6, 0), // ""
QT_MOC_LITERAL(3, 7, 4), // "sig1"
QT_MOC_LITERAL(4, 12, 9), // "__stdcall" //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
QT_MOC_LITERAL(5, 22, 4) // "void"
},
"A\0sig\0\0sig1\0__stdcall\0void"
};
static const uint qt_meta_data_A[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
1, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signalCount
// signals: name, argc, parameters, tag, flags
1, 0, 24, 2, 0x06 /* Public */,
3, 0, 25, 5, 0x06 /* Public */,
// signals: parameters
0x80000000 | 2,
0 // eod
};
void A::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
A *_t = static_cast<A *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->sig(); break;
case 1: { __stdcall _r = _t->sig1(); //error
if (_a[0]) *reinterpret_cast< __stdcall*>(_a[0]) = std::move(_r); } break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (A::*)();
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&A::sig)) {
*result = 0;
return;
}
}
{
using _t = __stdcall (A::*)(); //error
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&A::sig1)) {
*result = 1;
return;
}
}
}
}
利用利用 Q_INVOKABLE 和 QMetaObject::newInstance来生成对象的新实例_hanhanluma的博客-CSDN博客meta programing思想:元编程 (meta-programming) - 知乎
CC++中tag和type - 百度文库