QT作为一个流行的C++开发框架,保留了C++代码执行效率高的同时,也提供了类似动态语言的反射机制,能够实现很多标准c++做不到的事情,这些要归功于QT moc出来的元数据。那么,QT元数据都有哪些信息呢?下面通过一个例子详细说明(基于QT6.5)。
TodoModelView这个类的声明以及moc出来的源文件如下:
cpp
#ifndef TODOMODELVIEWMODEL_H
#define TODOMODELVIEWMODEL_H
#include <QObject>
#include <QString>
class TodoModel;
class TodoModelViewModel : public QObject
{
Q_OBJECT
Q_PROPERTY(TodoModel* TodM READ todoModel CONSTANT)
Q_PROPERTY(QString Itext READ inputText WRITE setInputText NOTIFY inputTextChanged)
Q_PROPERTY(bool bCad READ canAdd NOTIFY inputTextChanged)
Q_PROPERTY(int cCount READ completedCount NOTIFY todoModelChanged)
Q_PROPERTY(int tCount READ totalCount NOTIFY todoModelChanged)
public:
explicit TodoModelViewModel(QObject *parent = nullptr);
TodoModel* todoModel() const { return m_todoModel; }
QString inputText() const { return m_inputText; }
void setInputText(const QString &text);
bool canAdd() const { return !m_inputText.trimmed().isEmpty(); }
int completedCount() const;
int totalCount() const { return m_todoModel->rowCount(); }
Q_INVOKABLE void addTodo();
Q_INVOKABLE void removeTodo(int index);
Q_INVOKABLE void toggleTodo(int index);
Q_INVOKABLE void clearInput();
signals:
void inputTextChanged();
void todoModelChanged();
private:
TodoModel *m_todoModel;
QString m_inputText;
};
#endif // TODOMODELVIEWMODEL_H
cpp
/****************************************************************************
** Meta object code from reading C++ file 'todomodelviewmodel.h'
**
** Created by: The Qt Meta Object Compiler version 68 (Qt 6.5.3)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "../../../todomodelviewmodel.h"
#include <QtCore/qmetatype.h>
#if __has_include(<QtCore/qtmochelpers.h>)
#include <QtCore/qtmochelpers.h>
#else
QT_BEGIN_MOC_NAMESPACE
#endif
#include <memory>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'todomodelviewmodel.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 68
#error "This file was generated using the moc from 6.5.3. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
#ifndef Q_CONSTINIT
#define Q_CONSTINIT
#endif
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QT_WARNING_DISABLE_GCC("-Wuseless-cast")
namespace {
#ifdef QT_MOC_HAS_STRINGDATA
struct qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t {};
static constexpr auto qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS = QtMocHelpers::stringData(
"TodoModelViewModel",
"inputTextChanged",
"",
"todoModelChanged",
"addTodo",
"removeTodo",
"index",
"toggleTodo",
"clearInput",
"TodM",
"TodoModel*",
"Itext",
"bCad",
"cCount",
"tCount"
);
#else // !QT_MOC_HAS_STRING_DATA
struct qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t {
uint offsetsAndSizes[30];
char stringdata0[19];
char stringdata1[17];
char stringdata2[1];
char stringdata3[17];
char stringdata4[8];
char stringdata5[11];
char stringdata6[6];
char stringdata7[11];
char stringdata8[11];
char stringdata9[5];
char stringdata10[11];
char stringdata11[6];
char stringdata12[5];
char stringdata13[7];
char stringdata14[7];
};
#define QT_MOC_LITERAL(ofs, len) \
uint(sizeof(qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t::offsetsAndSizes) + ofs), len
Q_CONSTINIT static const qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS = {
{
QT_MOC_LITERAL(0, 18), // "TodoModelViewModel"
QT_MOC_LITERAL(19, 16), // "inputTextChanged"
QT_MOC_LITERAL(36, 0), // ""
QT_MOC_LITERAL(37, 16), // "todoModelChanged"
QT_MOC_LITERAL(54, 7), // "addTodo"
QT_MOC_LITERAL(62, 10), // "removeTodo"
QT_MOC_LITERAL(73, 5), // "index"
QT_MOC_LITERAL(79, 10), // "toggleTodo"
QT_MOC_LITERAL(90, 10), // "clearInput"
QT_MOC_LITERAL(101, 4), // "TodM"
QT_MOC_LITERAL(106, 10), // "TodoModel*"
QT_MOC_LITERAL(117, 5), // "Itext"
QT_MOC_LITERAL(123, 4), // "bCad"
QT_MOC_LITERAL(128, 6), // "cCount"
QT_MOC_LITERAL(135, 6) // "tCount"
},
"TodoModelViewModel",
"inputTextChanged",
"",
"todoModelChanged",
"addTodo",
"removeTodo",
"index",
"toggleTodo",
"clearInput",
"TodM",
"TodoModel*",
"Itext",
"bCad",
"cCount",
"tCount"
};
#undef QT_MOC_LITERAL
#endif // !QT_MOC_HAS_STRING_DATA
} // unnamed namespace
Q_CONSTINIT static const uint qt_meta_data_CLASSTodoModelViewModelENDCLASS[] = {
// content:
11, // revision
0, // classname
0, 0, // classinfo
6, 14, // methods
5, 60, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
2, // signalCount
// signals: name, argc, parameters, tag, flags, initial metatype offsets
1, 0, 50, 2, 0x06, 6 /* Public */,
3, 0, 51, 2, 0x06, 7 /* Public */,
// methods: name, argc, parameters, tag, flags, initial metatype offsets
4, 0, 52, 2, 0x02, 8 /* Public */,
5, 1, 53, 2, 0x02, 9 /* Public */,
7, 1, 56, 2, 0x02, 11 /* Public */,
8, 0, 59, 2, 0x02, 13 /* Public */,
// signals: parameters
QMetaType::Void,
QMetaType::Void,
// methods: parameters
QMetaType::Void,
QMetaType::Void, QMetaType::Int, 6,
QMetaType::Void, QMetaType::Int, 6,
QMetaType::Void,
// properties: name, type, flags, notify, revision
9, 0x80000000 | 10, 0x00015409, uint(-1), 0,
11, QMetaType::QString, 0x00015003, uint(0), 0,
12, QMetaType::Bool, 0x00015001, uint(0), 0,
13, QMetaType::Int, 0x00015001, uint(1), 0,
14, QMetaType::Int, 0x00015001, uint(1), 0,
0 // eod
};
Q_CONSTINIT const QMetaObject TodoModelViewModel::staticMetaObject = { {
QMetaObject::SuperData::link<QObject::staticMetaObject>(),
qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS.offsetsAndSizes,
qt_meta_data_CLASSTodoModelViewModelENDCLASS,
qt_static_metacall,
nullptr,
qt_incomplete_metaTypeArray<qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t,
// property 'TodM'
QtPrivate::TypeAndForceComplete<TodoModel*, std::true_type>,
// property 'Itext'
QtPrivate::TypeAndForceComplete<QString, std::true_type>,
// property 'bCad'
QtPrivate::TypeAndForceComplete<bool, std::true_type>,
// property 'cCount'
QtPrivate::TypeAndForceComplete<int, std::true_type>,
// property 'tCount'
QtPrivate::TypeAndForceComplete<int, std::true_type>,
// Q_OBJECT / Q_GADGET
QtPrivate::TypeAndForceComplete<TodoModelViewModel, std::true_type>,
// method 'inputTextChanged'
QtPrivate::TypeAndForceComplete<void, std::false_type>,
// method 'todoModelChanged'
QtPrivate::TypeAndForceComplete<void, std::false_type>,
// method 'addTodo'
QtPrivate::TypeAndForceComplete<void, std::false_type>,
// method 'removeTodo'
QtPrivate::TypeAndForceComplete<void, std::false_type>,
QtPrivate::TypeAndForceComplete<int, std::false_type>,
// method 'toggleTodo'
QtPrivate::TypeAndForceComplete<void, std::false_type>,
QtPrivate::TypeAndForceComplete<int, std::false_type>,
// method 'clearInput'
QtPrivate::TypeAndForceComplete<void, std::false_type>
>,
nullptr
} };
void TodoModelViewModel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<TodoModelViewModel *>(_o);
(void)_t;
switch (_id) {
case 0: _t->inputTextChanged(); break;
case 1: _t->todoModelChanged(); break;
case 2: _t->addTodo(); break;
case 3: _t->removeTodo((*reinterpret_cast< std::add_pointer_t<int>>(_a[1]))); break;
case 4: _t->toggleTodo((*reinterpret_cast< std::add_pointer_t<int>>(_a[1]))); break;
case 5: _t->clearInput(); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (TodoModelViewModel::*)();
if (_t _q_method = &TodoModelViewModel::inputTextChanged; *reinterpret_cast<_t *>(_a[1]) == _q_method) {
*result = 0;
return;
}
}
{
using _t = void (TodoModelViewModel::*)();
if (_t _q_method = &TodoModelViewModel::todoModelChanged; *reinterpret_cast<_t *>(_a[1]) == _q_method) {
*result = 1;
return;
}
}
} else if (_c == QMetaObject::RegisterPropertyMetaType) {
switch (_id) {
default: *reinterpret_cast<int*>(_a[0]) = -1; break;
case 0:
*reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< TodoModel* >(); break;
}
} else if (_c == QMetaObject::ReadProperty) {
auto *_t = static_cast<TodoModelViewModel *>(_o);
(void)_t;
void *_v = _a[0];
switch (_id) {
case 0: *reinterpret_cast< TodoModel**>(_v) = _t->todoModel(); break;
case 1: *reinterpret_cast< QString*>(_v) = _t->inputText(); break;
case 2: *reinterpret_cast< bool*>(_v) = _t->canAdd(); break;
case 3: *reinterpret_cast< int*>(_v) = _t->completedCount(); break;
case 4: *reinterpret_cast< int*>(_v) = _t->totalCount(); break;
default: break;
}
} else if (_c == QMetaObject::WriteProperty) {
auto *_t = static_cast<TodoModelViewModel *>(_o);
(void)_t;
void *_v = _a[0];
switch (_id) {
case 1: _t->setInputText(*reinterpret_cast< QString*>(_v)); break;
default: break;
}
} else if (_c == QMetaObject::ResetProperty) {
} else if (_c == QMetaObject::BindableProperty) {
}
}
const QMetaObject *TodoModelViewModel::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *TodoModelViewModel::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS.stringdata0))
return static_cast<void*>(this);
return QObject::qt_metacast(_clname);
}
int TodoModelViewModel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QObject::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 6)
qt_static_metacall(this, _c, _id, _a);
_id -= 6;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 6)
*reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();
_id -= 6;
}else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
|| _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty
|| _c == QMetaObject::RegisterPropertyMetaType) {
qt_static_metacall(this, _c, _id, _a);
_id -= 5;
}
return _id;
}
// SIGNAL 0
void TodoModelViewModel::inputTextChanged()
{
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
// SIGNAL 1
void TodoModelViewModel::todoModelChanged()
{
QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
}
QT_WARNING_POP
在moc_todomodelviewmodel.cpp中qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS(太长了,记作①),qt_meta_data_CLASSTodoModelViewModelENDCLASS(记作②)以及staticMetaObject(③)是存放元数据的具体结构。其中①是moc元数据时注册的字符串资源,是一个字符串数组。除了第一个元素offsetsAndSizes是uint类型,用于存放字符串资源索引头(每个字符串的偏移+长度),其余元素都是char类型,存放与该类相关的字符串信息。②是一个uint数组,信息密度很高:


staticMetaObject中有一个字段存放了该类所有property属性和invokable方法,涉及到的类型的基本信息表qt_incomplete_metaTypeArray,每种类型都提供了这些信息:
cpp
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,//与QT版本相关的版本号
/*.alignment=*/ alignof(T),
/*.size=*/ sizeof(T),//类型的size
/*.flags=*/ QMetaTypeForType<T>::Flags,
/*.typeId=*/ BuiltinMetaType<T>::value,
/*.metaObjectFn=*/ MetaObjectForType<T>::metaObjectFunction,//该函数返回类型的staticMetaObject地址
/*.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()
};
};
开发者通常不需要也不应该直接手动调用 qt_metacall。它是 Qt 内部机制的底层接口,由以下高级 API 间接触发:
| 触发场景 | 调用的高层 API | 底层行为映射 |
|---|---|---|
| 信号发射 | emit mySignal(); |
调用QMetaObject::activate -> 触发 qt_metacall(InvokeMetaMethod, signalIndex, ...)。 |
| 反射调用方法 | QMetaObject::invokeMethod(obj, "addTodo") |
查找方法 ID -> 调用 obj->qt_metacall(InvokeMetaMethod, methodIndex, args)。 |
| 动态属性读取 | obj->property("Itext").toString() |
查找属性 ID -> 调用 obj->qt_metacall(ReadProperty, propertyIndex, &value)。 |
| QML 交互 | QML 引擎访问 C++ 对象 |